OSDN Git Service

Reduce the amount of buffering used and eliminate hb_snooze in the encoding pipeline
authorjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 5 Dec 2009 17:15:57 +0000 (17:15 +0000)
committerjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 5 Dec 2009 17:15:57 +0000 (17:15 +0000)
For HD sources on an 8 core system with hyperthreading, we were using 1.5GB
of ram.  Add to that the 600MB x264 uses for rc-lookahead, pushes it north of 2GB.

To reduce our memory usage, the fifo depths have been reduced are are no longer
a multiple of cpu count.  Use of hb_snooze has been eliminated in the encoding
pipeline so that performance doesn't fall as a result of the reduced fifo depths.

In sync, each audio and video were given separate threads so that each can wait on
it's respective input fifo without blocking the others.  In muxcommon, each stream
being muxed was given a separate thread so that each can wait on it's respective fifo.
This allows the removal of hb_snooze in the sync and muxer work loops. In both sync
and muxer, there is common data that is shared by all threads, so special init
routines allocate this shared data and initialize the threads.

git-svn-id: svn://localhost/HandBrake/trunk@3007 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.h
libhb/fifo.c
libhb/hb.c
libhb/internal.h
libhb/muxcommon.c
libhb/ports.c
libhb/ports.h
libhb/reader.c
libhb/render.c
libhb/sync.c
libhb/work.c

index a85beda..d760222 100644 (file)
@@ -287,7 +287,6 @@ struct hb_job_s
     hb_fifo_t     * fifo_mpeg4;   /* MPEG-4 video ES */
 
     hb_thread_t   * reader;
-    hb_thread_t   * muxer;
 
     hb_list_t     * list_work;
 
@@ -680,7 +679,8 @@ struct hb_work_object_s
 #endif
 };
 
-extern hb_work_object_t hb_sync;
+extern hb_work_object_t hb_sync_video;
+extern hb_work_object_t hb_sync_audio;
 extern hb_work_object_t hb_decmpeg2;
 extern hb_work_object_t hb_decvobsub;
 extern hb_work_object_t hb_encvobsub;
@@ -700,6 +700,7 @@ extern hb_work_object_t hb_declpcm;
 extern hb_work_object_t hb_encfaac;
 extern hb_work_object_t hb_enclame;
 extern hb_work_object_t hb_encvorbis;
+extern hb_work_object_t hb_muxer;
 extern hb_work_object_t hb_encca_aac;
 
 #define FILTER_OK      0
index 48f6459..781c940 100644 (file)
 #include <malloc.h>
 #endif
 
+#define FIFO_TIMEOUT 200
+
 /* Fifo */
 struct hb_fifo_s
 {
     hb_lock_t    * lock;
+    hb_cond_t    * cond_full;
+    int            wait_full;
+    hb_cond_t    * cond_empty;
+    int            wait_empty;
     uint32_t       capacity;
+    uint32_t       thresh;
     uint32_t       size;
     uint32_t       buffer_size;
     hb_buffer_t  * first;
@@ -51,7 +58,7 @@ void hb_buffer_pool_init( void )
     int i;
     for ( i = 10; i < 26; ++i )
     {
-        buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
+        buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS, 1);
         buffers.pool[i]->buffer_size = 1 << i;
     }
     /* requests smaller than 2^10 are satisfied from the 2^10 pool. */
@@ -215,12 +222,15 @@ void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src )
     dst->flags     = src->flags;
 }
 
-hb_fifo_t * hb_fifo_init( int capacity )
+hb_fifo_t * hb_fifo_init( int capacity, int thresh )
 {
     hb_fifo_t * f;
-    f           = calloc( sizeof( hb_fifo_t ), 1 );
-    f->lock     = hb_lock_init();
-    f->capacity = capacity;
+    f             = calloc( sizeof( hb_fifo_t ), 1 );
+    f->lock       = hb_lock_init();
+    f->cond_full  = hb_cond_init();
+    f->cond_empty = hb_cond_init();
+    f->capacity   = capacity;
+    f->thresh     = thresh;
     f->buffer_size = 0;
     return f;
 }
@@ -258,6 +268,35 @@ float hb_fifo_percent_full( hb_fifo_t * f )
     return ret;
 }
 
+hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * f )
+{
+    hb_buffer_t * b;
+
+    hb_lock( f->lock );
+    if( f->size < 1 )
+    {
+        f->wait_empty = 1;
+        hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT );
+        if( f->size < 1 )
+        {
+            hb_unlock( f->lock );
+            return NULL;
+        }
+    }
+    b         = f->first;
+    f->first  = b->next;
+    b->next   = NULL;
+    f->size  -= 1;
+    if( f->wait_full && f->size == f->capacity - f->thresh )
+    {
+        f->wait_full = 0;
+        hb_cond_signal( f->cond_full );
+    }
+    hb_unlock( f->lock );
+
+    return b;
+}
+
 hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
 {
     hb_buffer_t * b;
@@ -272,6 +311,32 @@ hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
     f->first  = b->next;
     b->next   = NULL;
     f->size  -= 1;
+    if( f->wait_full && f->size == f->capacity - f->thresh )
+    {
+        f->wait_full = 0;
+        hb_cond_signal( f->cond_full );
+    }
+    hb_unlock( f->lock );
+
+    return b;
+}
+
+hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * f )
+{
+    hb_buffer_t * b;
+
+    hb_lock( f->lock );
+    if( f->size < 1 )
+    {
+        f->wait_empty = 1;
+        hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT );
+        if( f->size < 1 )
+        {
+            hb_unlock( f->lock );
+            return NULL;
+        }
+    }
+    b = f->first;
     hb_unlock( f->lock );
 
     return b;
@@ -309,6 +374,42 @@ hb_buffer_t * hb_fifo_see2( hb_fifo_t * f )
     return b;
 }
 
+void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b )
+{
+    if( !b )
+    {
+        return;
+    }
+
+    hb_lock( f->lock );
+    if( f->size >= f->capacity )
+    {
+        f->wait_full = 1;
+        hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT );
+    }
+    if( f->size > 0 )
+    {
+        f->last->next = b;
+    }
+    else
+    {
+        f->first = b;
+    }
+    f->last  = b;
+    f->size += 1;
+    while( f->last->next )
+    {
+        f->size += 1;
+        f->last  = f->last->next;
+    }
+    if( f->wait_empty && f->size >= f->thresh )
+    {
+        f->wait_empty = 0;
+        hb_cond_signal( f->cond_empty );
+    }
+    hb_unlock( f->lock );
+}
+
 void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
 {
     if( !b )
@@ -332,6 +433,11 @@ void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
         f->size += 1;
         f->last  = f->last->next;
     }
+    if( f->wait_empty && f->size >= f->thresh )
+    {
+        f->wait_empty = 0;
+        hb_cond_signal( f->cond_empty );
+    }
     hb_unlock( f->lock );
 }
 
@@ -384,7 +490,25 @@ void hb_fifo_close( hb_fifo_t ** _f )
     }
 
     hb_lock_close( &f->lock );
+    hb_cond_close( &f->cond_empty );
+    hb_cond_close( &f->cond_full );
     free( f );
 
     *_f = NULL;
 }
+
+void hb_fifo_flush( hb_fifo_t * f )
+{
+    hb_buffer_t * b;
+
+    while( ( b = hb_fifo_get( f ) ) )
+    {
+        hb_buffer_close( &b );
+    }
+    hb_lock( f->lock );
+    hb_cond_signal( f->cond_empty );
+    hb_cond_signal( f->cond_full );
+    hb_unlock( f->lock );
+
+}
+
index 1b899e3..3db173f 100644 (file)
@@ -158,7 +158,8 @@ hb_handle_t * hb_init( int verbose, int update_check )
     h->die         = 0;
     h->main_thread = hb_thread_init( "libhb", thread_func, h,
                                      HB_NORMAL_PRIORITY );
-    hb_register( &hb_sync );
+    hb_register( &hb_sync_video );
+    hb_register( &hb_sync_audio );
        hb_register( &hb_decmpeg2 );
        hb_register( &hb_decvobsub );
     hb_register( &hb_encvobsub );
@@ -178,6 +179,7 @@ hb_handle_t * hb_init( int verbose, int update_check )
        hb_register( &hb_encfaac );
        hb_register( &hb_enclame );
        hb_register( &hb_encvorbis );
+       hb_register( &hb_muxer );
 #ifdef __APPLE__
        hb_register( &hb_encca_aac );
 #endif
@@ -255,7 +257,8 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
     h->main_thread = hb_thread_init( "libhb", thread_func, h,
                                      HB_NORMAL_PRIORITY );
 
-    hb_register( &hb_sync );
+    hb_register( &hb_sync_video );
+    hb_register( &hb_sync_audio );
        hb_register( &hb_decmpeg2 );
        hb_register( &hb_decvobsub );
     hb_register( &hb_encvobsub );
@@ -275,6 +278,7 @@ hb_handle_t * hb_init_dl( int verbose, int update_check )
        hb_register( &hb_encfaac );
        hb_register( &hb_enclame );
        hb_register( &hb_encvorbis );
+       hb_register( &hb_muxer );
 #ifdef __APPLE__
        hb_register( &hb_encca_aac );
 #endif
index 88c8ef9..8085374 100644 (file)
@@ -85,16 +85,20 @@ void          hb_buffer_close( hb_buffer_t ** );
 void          hb_buffer_copy_settings( hb_buffer_t * dst,
                                        const hb_buffer_t * src );
 
-hb_fifo_t   * hb_fifo_init();
+hb_fifo_t   * hb_fifo_init( int capacity, int thresh );
 int           hb_fifo_size( hb_fifo_t * );
 int           hb_fifo_is_full( hb_fifo_t * );
 float         hb_fifo_percent_full( hb_fifo_t * f );
 hb_buffer_t * hb_fifo_get( hb_fifo_t * );
+hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * );
 hb_buffer_t * hb_fifo_see( hb_fifo_t * );
+hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * );
 hb_buffer_t * hb_fifo_see2( hb_fifo_t * );
 void          hb_fifo_push( hb_fifo_t *, hb_buffer_t * );
+void          hb_fifo_push_wait( hb_fifo_t *, hb_buffer_t * );
 void          hb_fifo_push_head( hb_fifo_t *, hb_buffer_t * );
 void          hb_fifo_close( hb_fifo_t ** );
+void          hb_fifo_flush( hb_fifo_t * f );
 
 // this routine gets a buffer for an uncompressed YUV420 video frame
 // with dimensions width x height.
@@ -138,12 +142,17 @@ hb_thread_t * hb_scan_init( hb_handle_t *, volatile int * die,
 hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count,
                             volatile int * die, int * error, hb_job_t ** job );
 hb_thread_t  * hb_reader_init( hb_job_t * );
-hb_thread_t  * hb_muxer_init( hb_job_t * );
+hb_work_object_t * hb_muxer_init( hb_job_t * );
 hb_work_object_t * hb_get_work( int );
 hb_work_object_t * hb_codec_decoder( int );
 hb_work_object_t * hb_codec_encoder( int );
 
 /***********************************************************************
+ * sync.c
+ **********************************************************************/
+int hb_sync_init( hb_job_t * job );
+
+/***********************************************************************
  * mpegdemux.c
  **********************************************************************/
 typedef struct {
@@ -266,7 +275,8 @@ union hb_esconfig_u
 
 enum
 {
-    WORK_SYNC = 1,
+    WORK_SYNC_VIDEO = 1,
+    WORK_SYNC_AUDIO,
     WORK_DECMPEG2,
     WORK_DECCC608,
     WORK_DECVOBSUB,
@@ -286,7 +296,8 @@ enum
     WORK_ENCFAAC,
     WORK_ENCLAME,
     WORK_ENCVORBIS,
-    WORK_ENC_CA_AAC
+    WORK_ENC_CA_AAC,
+    WORK_MUX
 };
 
 enum
index 1f39df1..dbaa9c4 100644 (file)
@@ -21,7 +21,6 @@ typedef struct
 
 typedef struct
 {
-    hb_fifo_t     * fifo;
     hb_mux_data_t * mux_data;
     uint64_t        frames;
     uint64_t        bytes;
@@ -30,21 +29,31 @@ typedef struct
 
 typedef struct
 {
-    hb_job_t    *job;
-    double      pts;        // end time of next muxing chunk
-    double      interleave; // size (in 90KHz ticks) of media chunks we mux
-    uint32_t    ntracks;    // total number of tracks we're muxing
-    uint32_t    eof;        // bitmask of track with eof
-    uint32_t    rdy;        // bitmask of tracks ready to output
-    uint32_t    allEof;     // valid bits in eof (all tracks)
-    uint32_t    allRdy;     // valid bits in rdy (audio & video tracks)
-    hb_track_t  *track[32]; // array of tracks to mux ('ntrack' elements)
-                            // NOTE- this array could be dynamically allocated
-                            // but the eof & rdy logic has to be changed to
-                            // handle more than 32 tracks anyway so we keep
-                            // it simple and fast.
+    hb_lock_t       * mutex;
+    int               ref;
+    int               done;
+    hb_mux_object_t * m;
+    double            pts;        // end time of next muxing chunk
+    double            interleave; // size in 90KHz ticks of media chunks we mux
+    uint32_t          ntracks;    // total number of tracks we're muxing
+    uint32_t          eof;        // bitmask of track with eof
+    uint32_t          rdy;        // bitmask of tracks ready to output
+    uint32_t          allEof;     // valid bits in eof (all tracks)
+    uint32_t          allRdy;     // valid bits in rdy (audio & video tracks)
+    hb_track_t      * track[32];  // array of tracks to mux ('ntrack' elements)
+                                  // NOTE- this array could be dynamically 
+                                  // allocated but the eof & rdy logic has to 
+                                  // be changed to handle more than 32 tracks 
+                                  // anyway so we keep it simple and fast.
 } hb_mux_t;
 
+struct hb_work_private_s
+{
+    hb_job_t * job;
+    int        track;
+    hb_mux_t * mux;
+};
+
 // The muxer handles two different kinds of media: Video and audio tracks
 // are continuous: once they start they generate continuous, consecutive
 // sequence of bufs until they end. The muxer will time align all continuous
@@ -73,7 +82,7 @@ typedef struct
 // routine OutputTrackChunk). 'is_continuous' must be 1 for an audio or video
 // track and 0 otherwise (see above).
 
-static void add_mux_track( hb_mux_t *mux, hb_fifo_t *fifo, hb_mux_data_t *mux_data,
+static void add_mux_track( hb_mux_t *mux, hb_mux_data_t *mux_data,
                            int is_continuous )
 {
     int max_tracks = sizeof(mux->track) / sizeof(*(mux->track));
@@ -84,7 +93,6 @@ static void add_mux_track( hb_mux_t *mux, hb_fifo_t *fifo, hb_mux_data_t *mux_da
     }
 
     hb_track_t *track = calloc( sizeof( hb_track_t ), 1 );
-    track->fifo = fifo;
     track->mux_data = mux_data;
     track->mf.flen = 8;
     track->mf.fifo = calloc( sizeof(track->mf.fifo[0]), track->mf.flen );
@@ -147,46 +155,20 @@ static hb_buffer_t *mf_peek( hb_track_t *track )
                 NULL : track->mf.fifo[track->mf.out & (track->mf.flen - 1)];
 }
 
-static void MoveToInternalFifos( hb_mux_t *mux )
+static void MoveToInternalFifos( int tk, hb_mux_t *mux, hb_buffer_t * buf )
 {
-    int i;
-    int discard = mux->job->pass != 0 && mux->job->pass != 2;
-
-    for( i = 0; i < mux->ntracks; ++i )
+    hb_track_t *track = mux->track[tk];
+
+    // move all the buffers on the track's fifo to our internal
+    // fifo so that (a) we don't deadlock in the reader and
+    // (b) we can control how data from multiple tracks is
+    // interleaved in the output file.
+    mf_push( track, buf );
+    if ( buf->stop >= mux->pts )
     {
-        if ( ( mux->eof & (1 << i) ) == 0 )
-        {
-            hb_track_t *track = mux->track[i];
-            hb_buffer_t *buf;
-            
-            // move all the buffers on the track's fifo to our internal
-            // fifo so that (a) we don't deadlock in the reader and
-            // (b) we can control how data from multiple tracks is
-            // interleaved in the output file.
-            while ( ( buf = hb_fifo_get( track->fifo ) ) )
-            {
-                if ( buf->size <= 0 )
-                {
-                    // EOF - mark this track as done
-                    hb_buffer_close( &buf );
-                    mux->eof |= ( 1 << i );
-                    mux->rdy |= ( 1 << i );
-                    continue;
-                }
-                if ( discard )
-                {
-                    hb_buffer_close( &buf );
-                    continue;
-                }
-                mf_push( track, buf );
-                if ( buf->stop >= mux->pts )
-                {
-                    // buffer is past our next interleave point so
-                    // note that this track is ready to be output.
-                    mux->rdy |= ( 1 << i );
-                }
-            }
-        }
+        // buffer is past our next interleave point so
+        // note that this track is ready to be output.
+        mux->rdy |= ( 1 << tk );
     }
 }
 
@@ -203,125 +185,101 @@ static void OutputTrackChunk( hb_mux_t *mux, hb_track_t *track, hb_mux_object_t
     }
 }
 
-static void MuxerFunc( void * _mux )
+static int muxWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+                     hb_buffer_t ** buf_out )
 {
-    hb_mux_t    * mux = _mux;
-    hb_job_t    * job = mux->job;
-    hb_title_t  * title = job->title;
+    hb_work_private_t * pv = w->private_data;
+    hb_job_t    * job = pv->job;
+    hb_mux_t    * mux = pv->mux;
     hb_track_t  * track;
     int           i;
-    hb_mux_object_t * m = NULL;
+    hb_buffer_t * buf = *buf_in;
 
-    // set up to interleave track data in blocks of 1 video frame time.
-    // (the best case for buffering and playout latency). The container-
-    // specific muxers can reblock this into bigger chunks if necessary.
-    mux->interleave = 90000. * (double)job->vrate_base / (double)job->vrate;
-    mux->pts = mux->interleave;
-
-    /* Get a real muxer */
-    if( job->pass == 0 || job->pass == 2)
+    hb_lock( mux->mutex );
+    if ( mux->done )
     {
-        switch( job->mux )
-        {
-        case HB_MUX_MP4:
-        case HB_MUX_PSP:
-        case HB_MUX_IPOD:
-            m = hb_mux_mp4_init( job );
-            break;
-        case HB_MUX_AVI:
-            m = hb_mux_avi_init( job );
-            break;
-        case HB_MUX_OGM:
-            m = hb_mux_ogm_init( job );
-            break;
-        case HB_MUX_MKV:
-            m = hb_mux_mkv_init( job );
-            break;
-        default:
-            hb_error( "No muxer selected, exiting" );
-            *job->die = 1;
-        }
-        /* Create file, write headers */
-        if( m )
-        {
-            m->init( m );
-        }
+        hb_unlock( mux->mutex );
+        return HB_WORK_DONE;
     }
 
-    /* Build list of fifos we're interested in */
-
-    add_mux_track( mux, job->fifo_mpeg4, job->mux_data, 1 );
-
-    for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+    if ( buf->size <= 0 )
     {
-        hb_audio_t  *audio = hb_list_item( title->list_audio, i );
-        add_mux_track( mux, audio->priv.fifo_out, audio->priv.mux_data, 1 );
+        // EOF - mark this track as done
+        hb_buffer_close( &buf );
+        mux->eof |= ( 1 << pv->track );
+        mux->rdy |= ( 1 << pv->track );
     }
-
-    for( i = 0; i < hb_list_count( title->list_subtitle ); i++ )
+    else if ( ( job->pass != 0 && job->pass != 2 ) ||
+              ( mux->eof & (1 << pv->track) ) )
     {
-        hb_subtitle_t  *subtitle = hb_list_item( title->list_subtitle, i );
-
-        if (subtitle->config.dest != PASSTHRUSUB)
-            continue;
-        add_mux_track( mux, subtitle->fifo_out, subtitle->mux_data, 0 );
+        hb_buffer_close( &buf );
+    }
+    else
+    {
+        MoveToInternalFifos( pv->track, mux, buf );
     }
+    *buf_in = NULL;
 
-    // The following 'while' is the main muxing loop.
+    if ( ( mux->rdy & mux->allRdy ) != mux->allRdy )
+    {
+        hb_unlock( mux->mutex );
+        return HB_WORK_OK;
+    }
 
-    int thread_sleep_interval = 50;
-    while( !*job->die )
+    // all tracks have at least 'interleave' ticks of data. Output
+    // all that we can in 'interleave' size chunks.
+    while ( ( mux->rdy & mux->allRdy ) == mux->allRdy )
     {
-        MoveToInternalFifos( mux );
-        if ( ( mux->rdy & mux->allRdy ) != mux->allRdy )
+        for ( i = 0; i < mux->ntracks; ++i )
         {
-            hb_snooze( thread_sleep_interval );
-            continue;
+            track = mux->track[i];
+            OutputTrackChunk( mux, track, mux->m );
+
+            // if the track is at eof or still has data that's past
+            // our next interleave point then leave it marked as rdy.
+            // Otherwise clear rdy.
+            if ( ( mux->eof & (1 << i) ) == 0 &&
+                 ( track->mf.out == track->mf.in ||
+                   track->mf.fifo[(track->mf.in-1) & (track->mf.flen-1)]->stop
+                     < mux->pts + mux->interleave ) )
+            {
+                mux->rdy &=~ ( 1 << i );
+            }
         }
 
-        // all tracks have at least 'interleave' ticks of data. Output
-        // all that we can in 'interleave' size chunks.
-        while ( ( mux->rdy & mux->allRdy ) == mux->allRdy )
+        // if all the tracks are at eof we're just purging their
+        // remaining data -- keep going until all internal fifos are empty.
+        if ( mux->eof == mux->allEof )
         {
             for ( i = 0; i < mux->ntracks; ++i )
             {
-                track = mux->track[i];
-                OutputTrackChunk( mux, track, m );
-
-                // if the track is at eof or still has data that's past
-                // our next interleave point then leave it marked as rdy.
-                // Otherwise clear rdy.
-                if ( ( mux->eof & (1 << i) ) == 0 &&
-                     ( track->mf.out == track->mf.in ||
-                       track->mf.fifo[(track->mf.in-1) & (track->mf.flen-1)]->stop
-                         < mux->pts + mux->interleave ) )
+                if ( mux->track[i]->mf.out != mux->track[i]->mf.in )
                 {
-                    mux->rdy &=~ ( 1 << i );
+                    break;
                 }
             }
-
-            // if all the tracks are at eof we're just purging their
-            // remaining data -- keep going until all internal fifos are empty.
-            if ( mux->eof == mux->allEof )
+            if ( i >= mux->ntracks )
             {
-                for ( i = 0; i < mux->ntracks; ++i )
-                {
-                    if ( mux->track[i]->mf.out != mux->track[i]->mf.in )
-                    {
-                        break;
-                    }
-                }
-                if ( i >= mux->ntracks )
-                {
-                    goto finished;
-                }
+                mux->done = 1;
+                hb_unlock( mux->mutex );
+                return HB_WORK_DONE;
             }
-            mux->pts += mux->interleave;
         }
+        mux->pts += mux->interleave;
     }
+    hb_unlock( mux->mutex );
+    return HB_WORK_OK;
+}
+
+void muxClose( hb_work_object_t * w )
+{
+    hb_work_private_t * pv = w->private_data;
+    hb_mux_t    * mux = pv->mux;
+    hb_job_t    * job = pv->job;
+    hb_track_t  * track;
+    int           i;
 
     // we're all done muxing -- print final stats and cleanup.
-finished:
     if( job->pass == 0 || job->pass == 2 )
     {
         struct stat sb;
@@ -333,11 +291,6 @@ finished:
         state.param.muxing.progress = 0;
         hb_set_state( job->h, &state );
 
-        if( m )
-        {
-            m->end( m );
-        }
-
         if( !stat( job->file, &sb ) )
         {
             hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size );
@@ -370,29 +323,170 @@ finished:
         }
     }
     
-    if( m )
+    hb_lock( mux->mutex );
+    if ( --mux->ref == 0 )
+    {
+        if( mux->m )
+        {
+            mux->m->end( mux->m );
+            free( mux->m );
+        }
+
+        for( i = 0; i < mux->ntracks; ++i )
+        {
+            track = mux->track[i];
+            if( track->mux_data )
+            {
+                free( track->mux_data );
+                free( track->mf.fifo );
+            }
+            free( track );
+        }
+        hb_unlock( mux->mutex );
+        hb_lock_close( &mux->mutex );
+        free( mux );
+    }
+    else
     {
-        free( m );
+        hb_unlock( mux->mutex );
+    }
+    free( pv );
+    w->private_data = NULL;
+}
+
+static void mux_loop( void * _w )
+{
+    hb_work_object_t  * w = _w;
+    hb_work_private_t * pv = w->private_data;
+    hb_job_t          * job = pv->job;
+    hb_buffer_t       * buf_in;
+
+    while ( !*job->die && w->status != HB_WORK_DONE )
+    {
+        buf_in = hb_fifo_get_wait( w->fifo_in );
+        if ( pv->mux->done )
+            break;
+        if ( buf_in == NULL )
+            continue;
+        if ( *job->die )
+            break;
+
+        w->status = w->work( w, &buf_in, NULL );
     }
+}
+
+hb_work_object_t * hb_muxer_init( hb_job_t * job )
+{
+    hb_title_t  * title = job->title;
+    int           i;
+    hb_mux_t    * mux = calloc( sizeof( hb_mux_t ), 1 );
+    hb_work_object_t  * w;
+    hb_work_object_t  * muxer;
+
+    mux->mutex = hb_lock_init();
 
-    for( i = 0; i < mux->ntracks; ++i )
+    // set up to interleave track data in blocks of 1 video frame time.
+    // (the best case for buffering and playout latency). The container-
+    // specific muxers can reblock this into bigger chunks if necessary.
+    mux->interleave = 90000. * (double)job->vrate_base / (double)job->vrate;
+    mux->pts = mux->interleave;
+
+    /* Get a real muxer */
+    if( job->pass == 0 || job->pass == 2)
     {
-        track = mux->track[i];
-        if( track->mux_data )
+        switch( job->mux )
         {
-            free( track->mux_data );
-            free( track->mf.fifo );
+        case HB_MUX_MP4:
+        case HB_MUX_PSP:
+        case HB_MUX_IPOD:
+            mux->m = hb_mux_mp4_init( job );
+            break;
+        case HB_MUX_AVI:
+            mux->m = hb_mux_avi_init( job );
+            break;
+        case HB_MUX_OGM:
+            mux->m = hb_mux_ogm_init( job );
+            break;
+        case HB_MUX_MKV:
+            mux->m = hb_mux_mkv_init( job );
+            break;
+        default:
+            hb_error( "No muxer selected, exiting" );
+            *job->die = 1;
+            return NULL;
         }
-        free( track );
+        /* Create file, write headers */
+        if( mux->m )
+        {
+            mux->m->init( mux->m );
+        }
+    }
+
+    /* Initialize the work objects that will receive fifo data */
+
+    muxer = hb_get_work( WORK_MUX );
+    muxer->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+    muxer->private_data->job = job;
+    muxer->private_data->mux = mux;
+    mux->ref++;
+    muxer->private_data->track = mux->ntracks;
+    muxer->fifo_in = job->fifo_mpeg4;
+    add_mux_track( mux, job->mux_data, 1 );
+    muxer->done = &job->done;
+    muxer->thread = hb_thread_init( muxer->name, mux_loop, muxer, HB_NORMAL_PRIORITY );
+
+    for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+    {
+        hb_audio_t  *audio = hb_list_item( title->list_audio, i );
+
+        w = hb_get_work( WORK_MUX );
+        w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+        w->private_data->job = job;
+        w->private_data->mux = mux;
+        mux->ref++;
+        w->private_data->track = mux->ntracks;
+        w->fifo_in = audio->priv.fifo_out;
+        add_mux_track( mux, audio->priv.mux_data, 1 );
+        w->done = &job->done;
+        hb_list_add( job->list_work, w );
+        w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
     }
 
-    free( mux );
+    for( i = 0; i < hb_list_count( title->list_subtitle ); i++ )
+    {
+        hb_subtitle_t  *subtitle = hb_list_item( title->list_subtitle, i );
+
+        if (subtitle->config.dest != PASSTHRUSUB)
+            continue;
+
+        w = hb_get_work( WORK_MUX );
+        w->private_data = calloc( sizeof( hb_work_private_t ), 1 );
+        w->private_data->job = job;
+        w->private_data->mux = mux;
+        mux->ref++;
+        w->private_data->track = mux->ntracks;
+        w->fifo_in = subtitle->fifo_out;
+        add_mux_track( mux, subtitle->mux_data, 0 );
+        w->done = &job->done;
+        hb_list_add( job->list_work, w );
+        w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY );
+    }
+    return muxer;
 }
 
-hb_thread_t * hb_muxer_init( hb_job_t * job )
+// muxInit does nothing because the muxer has a special initializer
+// that takes care of initializing all muxer work objects
+static int muxInit( hb_work_object_t * w, hb_job_t * job )
 {
-    hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 );
-    mux->job = job;
-    return hb_thread_init( "muxer", MuxerFunc, mux,
-                           HB_NORMAL_PRIORITY );
+    return 0;
 }
+
+hb_work_object_t hb_muxer =
+{
+    WORK_MUX,
+    "Muxer",
+    muxInit,
+    muxWork,
+    muxClose
+};
+
index 1200386..10b598c 100644 (file)
@@ -604,6 +604,35 @@ void hb_cond_wait( hb_cond_t * c, hb_lock_t * lock )
 #endif
 }
 
+void hb_clock_gettime( struct timespec *tp )
+{
+    struct timeval tv;
+    time_t sec;
+
+    sec = time( NULL );
+    gettimeofday( &tv, NULL );
+    tp->tv_sec = tv.tv_sec;
+    tp->tv_nsec = tv.tv_usec * 1000;
+}
+
+void hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec )
+{
+#if defined( SYS_BEOS )
+    c->thread = find_thread( NULL );
+    release_sem( lock->sem );
+    suspend_thread( c->thread );
+    acquire_sem( lock->sem );
+    c->thread = -1;
+#elif USE_PTHREAD
+    struct timespec ts;
+    hb_clock_gettime(&ts);
+    ts.tv_nsec += (msec % 1000) * 1000000;
+    ts.tv_sec += msec / 1000 + (ts.tv_nsec / 1000000000);
+    ts.tv_nsec %= 1000000000;
+    pthread_cond_timedwait( &c->cond, &lock->mutex, &ts );
+#endif
+}
+
 void hb_cond_signal( hb_cond_t * c )
 {
 #if defined( SYS_BEOS )
index 47be2d7..318a7d7 100644 (file)
@@ -80,6 +80,7 @@ typedef struct hb_cond_s hb_cond_t;
 
 hb_cond_t * hb_cond_init();
 void        hb_cond_wait( hb_cond_t *, hb_lock_t * );
+void        hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec );
 void        hb_cond_signal( hb_cond_t * );
 void        hb_cond_close( hb_cond_t ** );
 
index 94ee77e..346cafd 100644 (file)
@@ -69,15 +69,7 @@ hb_thread_t * hb_reader_init( hb_job_t * job )
 
 static void push_buf( const hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf )
 {
-    while( !*r->die && !r->job->done && hb_fifo_is_full( fifo ) )
-    {
-        /*
-         * Loop until the incoming fifo is ready to receive
-         * this buffer.
-         */
-        hb_snooze( 50 );
-    }
-    hb_fifo_push( fifo, buf );
+    hb_fifo_push_wait( fifo, buf );
 }
 
 static int is_audio( hb_reader_t *r, int id )
@@ -466,20 +458,23 @@ static void ReaderFunc( void * _r )
 
   done:
     // send empty buffers downstream to video & audio decoders to signal we're done.
-    push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) );
-
-    hb_audio_t *audio;
-    for( n = 0; ( audio = hb_list_item( r->job->title->list_audio, n ) ); ++n )
+    if( !*r->die && !r->job->done )
     {
-        if ( audio->priv.fifo_in )
-            push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) );
-    }
+        push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) );
 
-    hb_subtitle_t *subtitle;
-    for( n = 0; ( subtitle = hb_list_item( r->job->title->list_subtitle, n ) ); ++n )
-    {
-        if ( subtitle->fifo_in && subtitle->source == VOBSUB)
-            push_buf( r, subtitle->fifo_in, hb_buffer_init(0) );
+        hb_audio_t *audio;
+        for( n = 0; (audio = hb_list_item( r->job->title->list_audio, n)); ++n )
+        {
+            if ( audio->priv.fifo_in )
+                push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) );
+        }
+
+        hb_subtitle_t *subtitle;
+        for( n = 0; (subtitle = hb_list_item( r->job->title->list_subtitle, n)); ++n )
+        {
+            if ( subtitle->fifo_in && subtitle->source == VOBSUB)
+                push_buf( r, subtitle->fifo_in, hb_buffer_init(0) );
+        }
     }
 
     hb_list_empty( &list );
index a4943b7..6888a1e 100644 (file)
@@ -720,8 +720,8 @@ int renderInit( hb_work_object_t * w, hb_job_t * job )
     }
 
     /* Setup FIFO queue for subtitle cache */
-    pv->subtitle_queue = hb_fifo_init( 8 );
-    pv->delay_queue = hb_fifo_init( 8 );
+    pv->subtitle_queue = hb_fifo_init( 8, 1 );
+    pv->delay_queue = hb_fifo_init( 8, 1 );
 
     /* VFR IVTC needs a bunch of time-keeping variables to track
       how many frames are dropped, how many are extended, what the
index 94b8411..e886ac1 100644 (file)
 
 typedef struct
 {
-    hb_audio_t * audio;
+    hb_lock_t * mutex;
+    int         ref;        /* Reference count to tell us when it's unused */
+    int         count_frames;
+    int64_t     audio_passthru_slip;
+    int64_t     video_pts_slip;
+} hb_sync_common_t;
 
+typedef struct
+{
     int64_t      next_start;    /* start time of next output frame */
-    int64_t      next_pts;      /* start time of next input frame */
-    int64_t      first_drop;    /* PTS of first 'went backwards' frame dropped */
-    int          drop_count;    /* count of 'time went backwards' drops */
+    int64_t      next_pts;     /* start time of next input frame */
+    int64_t      first_drop;   /* PTS of first 'went backwards' frame dropped */
+    int          drop_count;   /* count of 'time went backwards' drops */
 
     /* Raw */
     SRC_STATE  * state;
@@ -32,75 +39,84 @@ typedef struct
     /* AC-3 */
     int          ac3_size;
     uint8_t    * ac3_buf;
-
 } hb_sync_audio_t;
 
-struct hb_work_private_s
+typedef struct
 {
-    hb_job_t * job;
-    int        busy;            // bitmask with one bit for each active input
-                                // (bit 0 = video; 1 = audio 0, 2 = audio 1, ...
-                                // appropriate bit is cleared when input gets
-                                // an eof buf. syncWork returns done when all
-                                // bits are clear.
     /* Video */
-    int64_t pts_offset;
-    int64_t next_start;         /* start time of next output frame */
-    int64_t next_pts;           /* start time of next input frame */
-    int64_t first_drop;         /* PTS of first 'went backwards' frame dropped */
-    int drop_count;             /* count of 'time went backwards' drops */
-    int drops;                  /* frames dropped to make a cbr video stream */
-    int dups;                   /* frames duplicated to make a cbr video stream */
-    int video_sequence;
-    int count_frames;
-    int count_frames_max;
-    int chap_mark;              /* to propagate chapter mark across a drop */
-    hb_buffer_t * cur; /* The next picture to process */
-
-    /* Audio */
-    hb_sync_audio_t sync_audio[8];
-    int64_t audio_passthru_slip;
-    int64_t video_pts_slip;
+    int64_t    pts_offset;
+    int64_t    pts_skip;
+    int64_t    next_start;    /* start time of next output frame */
+    int64_t    next_pts;      /* start time of next input frame */
+    int64_t    first_drop;    /* PTS of first 'went backwards' frame dropped */
+    int        drop_count;    /* count of 'time went backwards' drops */
+    int        drops;         /* frames dropped to make a cbr video stream */
+    int        dups;          /* frames duplicated to make a cbr video stream */
+    int        video_sequence;
+    int        count_frames_max;
+    int        chap_mark;     /* to propagate chapter mark across a drop */
+    hb_buffer_t * cur;        /* The next picture to process */
 
     /* Statistics */
-    uint64_t st_counts[4];
-    uint64_t st_dates[4];
-    uint64_t st_first;
+    uint64_t   st_counts[4];
+    uint64_t   st_dates[4];
+    uint64_t   st_first;
+} hb_sync_video_t;
+
+struct hb_work_private_s
+{
+    hb_job_t * job;
+    hb_sync_common_t * common;
+    union
+    {
+        hb_sync_video_t video;
+        hb_sync_audio_t audio;
+    } type;
 };
 
 /***********************************************************************
  * Local prototypes
  **********************************************************************/
-static void InitAudio( hb_work_object_t * w, int i );
-static void SyncVideo( hb_work_object_t * w );
-static void SyncAudio( hb_work_object_t * w, int i );
-static void InsertSilence( hb_work_object_t * w, int i, int64_t d );
+static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i );
+static void InsertSilence( hb_work_object_t * w, int64_t d );
 static void UpdateState( hb_work_object_t * w );
+static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
+                                       hb_sync_audio_t *sync );
 
 /***********************************************************************
  * hb_work_sync_init
  ***********************************************************************
  * Initialize the work object
  **********************************************************************/
-int syncInit( hb_work_object_t * w, hb_job_t * job )
+int hb_sync_init( hb_job_t * job )
 {
-    hb_title_t       * title = job->title;
-    hb_chapter_t     * chapter;
-    int                i;
-    uint64_t           duration;
+    hb_title_t        * title = job->title;
+    hb_chapter_t      * chapter;
+    int                 i;
+    uint64_t            duration;
     hb_work_private_t * pv;
+    hb_sync_video_t   * sync;
+    hb_work_object_t  * w;
 
     pv = calloc( 1, sizeof( hb_work_private_t ) );
+    sync = &pv->type.video;
+    pv->common = calloc( 1, sizeof( hb_sync_common_t ) );
+    pv->common->ref++;
+    pv->common->mutex = hb_lock_init();
+
+    w = hb_get_work( WORK_SYNC_VIDEO );
     w->private_data = pv;
+    w->fifo_in = job->fifo_raw;
+    w->fifo_out = job->fifo_sync;
 
     pv->job            = job;
-    pv->pts_offset     = INT64_MIN;
+    sync->pts_offset   = INT64_MIN;
 
     if( job->pass == 2 )
     {
         /* We already have an accurate frame count from pass 1 */
         hb_interjob_t * interjob = hb_interjob_get( job->h );
-        pv->count_frames_max = interjob->frame_count;
+        sync->count_frames_max = interjob->frame_count;
     }
     else
     {
@@ -125,19 +141,18 @@ int syncInit( hb_work_object_t * w, hb_job_t * job )
             duration += 90000;
             /* 1 second safety so we're sure we won't miss anything */
         }
-        pv->count_frames_max = duration * title->rate / title->rate_base / 90000;
+        sync->count_frames_max = duration * title->rate / title->rate_base / 90000;
     }
+    hb_list_add( job->list_work, w );
 
-    hb_log( "sync: expecting %d video frames", pv->count_frames_max );
-    pv->busy |= 1;
+    hb_log( "sync: expecting %d video frames", sync->count_frames_max );
 
     /* Initialize libsamplerate for every audio track we have */
     if ( ! job->indepth_scan )
     {
         for( i = 0; i < hb_list_count( title->list_audio ) && i < 8; i++ )
         {
-            pv->busy |= ( 1 << (i + 1) );
-            InitAudio( w, i );
+            InitAudio( job, pv->common, i );
         }
     }
 
@@ -145,52 +160,50 @@ int syncInit( hb_work_object_t * w, hb_job_t * job )
 }
 
 /***********************************************************************
- * Close
+ * Close Video
  ***********************************************************************
  *
  **********************************************************************/
-void syncClose( hb_work_object_t * w )
+void syncVideoClose( hb_work_object_t * w )
 {
     hb_work_private_t * pv = w->private_data;
     hb_job_t          * job   = pv->job;
-    hb_title_t        * title = job->title;
-    hb_audio_t        * audio = NULL;
-    int i;
+    hb_sync_video_t   * sync = &pv->type.video;
 
-    if( pv->cur )
+    if( sync->cur )
     {
-        hb_buffer_close( &pv->cur );
+        hb_buffer_close( &sync->cur );
     }
 
     hb_log( "sync: got %d frames, %d expected",
-            pv->count_frames, pv->count_frames_max );
+            pv->common->count_frames, sync->count_frames_max );
 
     /* save data for second pass */
     if( job->pass == 1 )
     {
         /* Preserve frame count for better accuracy in pass 2 */
         hb_interjob_t * interjob = hb_interjob_get( job->h );
-        interjob->frame_count = pv->count_frames;
+        interjob->frame_count = pv->common->count_frames;
         interjob->last_job = job->sequence_id;
-        interjob->total_time = pv->next_start;
+        interjob->total_time = sync->next_start;
     }
 
-    if (pv->drops || pv->dups )
+    if (sync->drops || sync->dups )
     {
-        hb_log( "sync: %d frames dropped, %d duplicated", pv->drops, pv->dups );
+        hb_log( "sync: %d frames dropped, %d duplicated", 
+                sync->drops, sync->dups );
     }
 
-    for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+    hb_lock( pv->common->mutex );
+    if ( --pv->common->ref == 0 )
     {
-        audio = hb_list_item( title->list_audio, i );
-        if( audio->config.out.codec == HB_ACODEC_AC3 )
-        {
-            free( pv->sync_audio[i].ac3_buf );
-        }
-        else
-        {
-            src_delete( pv->sync_audio[i].state );
-        }
+        hb_unlock( pv->common->mutex );
+        hb_lock_close( &pv->common->mutex );
+        free( pv->common );
+    }
+    else
+    {
+        hb_unlock( pv->common->mutex );
     }
 
     free( pv );
@@ -198,123 +211,83 @@ void syncClose( hb_work_object_t * w )
 }
 
 /***********************************************************************
- * Work
+ * syncVideoWork
  ***********************************************************************
- * The root routine of this work abject
- *
- * The way this works is that we are syncing the audio to the PTS of
- * the last video that we processed. That's why we skip the audio sync
- * if we haven't got a valid PTS from the video yet.
  *
  **********************************************************************/
-int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
-              hb_buffer_t ** unused2 )
+int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+              hb_buffer_t ** buf_out )
 {
+    hb_buffer_t * cur, * next, * sub = NULL;
     hb_work_private_t * pv = w->private_data;
+    hb_job_t          * job = pv->job;
+    hb_subtitle_t     * subtitle;
+    hb_sync_video_t   * sync = &pv->type.video;
     int i;
 
-    if ( pv->busy & 1 )
-        SyncVideo( w );
-
-    for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
+    *buf_out = NULL;
+    if( !sync->cur )
     {
-        if ( pv->busy & ( 1 << (i + 1) ) )
-            SyncAudio( w, i );
-    }
-
-    return ( pv->busy? HB_WORK_OK : HB_WORK_DONE );
-}
-
-hb_work_object_t hb_sync =
-{
-    WORK_SYNC,
-    "Synchronization",
-    syncInit,
-    syncWork,
-    syncClose
-};
-
-static void InitAudio( hb_work_object_t * w, int i )
-{
-    hb_work_private_t * pv = w->private_data;
-    hb_job_t        * job   = pv->job;
-    hb_title_t      * title = job->title;
-    hb_sync_audio_t * sync;
-
-    sync        = &pv->sync_audio[i];
-    sync->audio = hb_list_item( title->list_audio, i );
-
-    if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
-    {
-        /* Have a silent AC-3 frame ready in case we have to fill a
-           gap */
-        AVCodec        * codec;
-        AVCodecContext * c;
-        short          * zeros;
-
-        codec = avcodec_find_encoder( CODEC_ID_AC3 );
-        c     = avcodec_alloc_context();
-
-        c->bit_rate    = sync->audio->config.in.bitrate;
-        c->sample_rate = sync->audio->config.in.samplerate;
-        c->channels    = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( sync->audio->config.in.channel_layout );
-
-        if( hb_avcodec_open( c, codec ) < 0 )
+        sync->cur = *buf_in;
+        if( sync->cur->size == 0 )
         {
-            hb_log( "sync: avcodec_open failed" );
-            return;
-        }
-
-        zeros          = calloc( AC3_SAMPLES_PER_FRAME *
-                                 sizeof( short ) * c->channels, 1 );
-        sync->ac3_size = sync->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
-                             sync->audio->config.in.samplerate / 8;
-        sync->ac3_buf  = malloc( sync->ac3_size );
+            /* we got an end-of-stream as our first video packet? 
+             * Feed it downstream & signal that we're done. 
+             */
+            *buf_out = hb_buffer_init( 0 );
 
-        if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
-                                  zeros ) != sync->ac3_size )
-        {
-            hb_log( "sync: avcodec_encode_audio failed" );
+            /*
+             * Push through any subtitle EOFs in case they 
+             * were not synced through.
+             */
+            for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
+            {
+                subtitle = hb_list_item( job->list_subtitle, i );
+                if( subtitle->config.dest == PASSTHRUSUB )
+                {
+                    hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
+                }
+            }
+            return HB_WORK_DONE;
         }
-
-        free( zeros );
-        hb_avcodec_close( c );
-        av_free( c );
+        *buf_in = NULL;
+        return HB_WORK_OK;
     }
-    else
+    next = *buf_in;
+    *buf_in = NULL;
+    cur = sync->cur;
+    if( job->frame_to_stop && pv->common->count_frames > job->frame_to_stop )
     {
-        /* Initialize libsamplerate */
-        int error;
-        sync->state             = src_new( SRC_SINC_MEDIUM_QUALITY, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->config.out.mixdown), &error );
-        sync->data.end_of_input = 0;
+        // Drop an empty buffer into our output to ensure that things
+        // get flushed all the way out.
+        hb_buffer_close( &sync->cur );
+        hb_buffer_close( &next );
+        *buf_out = hb_buffer_init( 0 );
+        hb_log( "sync: reached %d frames, exiting early",
+                pv->common->count_frames );
+        return HB_WORK_DONE;
     }
-}
 
-/***********************************************************************
- * SyncVideo
- ***********************************************************************
- *
- **********************************************************************/
-static void SyncVideo( hb_work_object_t * w )
-{
-    hb_work_private_t * pv = w->private_data;
-    hb_buffer_t * cur, * next, * sub = NULL;
-    hb_job_t * job = pv->job;
-    hb_subtitle_t *subtitle;
-    int i;
-    int64_t pts_skip;
-
-    if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
-    {
-        /* We haven't even got a frame yet */
-        return;
-    }
-    cur = pv->cur;
-    pts_skip = 0;
-    if( cur->size == 0 )
+    /* At this point we have a frame to process. Let's check
+        1) if we will be able to push into the fifo ahead
+        2) if the next frame is there already, since we need it to
+           compute the duration of the current frame*/
+    if( next->size == 0 )
     {
-        /* we got an end-of-stream. Feed it downstream & signal that we're done. */
-        hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
+        hb_buffer_close( &next );
+
+        cur->start = sync->next_start;
+        cur->stop = cur->start + 90000. / ((double)job->vrate / (double)job->vrate_base);
+
+        /* Push the frame to the renderer */
+        hb_fifo_push( job->fifo_sync, cur );
+        sync->cur = NULL;
+
+        /* we got an end-of-stream. Feed it downstream & signal that
+         * we're done. Note that this means we drop the final frame of
+         * video (we don't know its duration). On DVDs the final frame
+         * is often strange and dropping it seems to be a good idea. */
+        *buf_out = hb_buffer_init( 0 );
 
         /*
          * Push through any subtitle EOFs in case they were not synced through.
@@ -324,423 +297,638 @@ static void SyncVideo( hb_work_object_t * w )
             subtitle = hb_list_item( job->list_subtitle, i );
             if( subtitle->config.dest == PASSTHRUSUB )
             {
-                hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
+                if( subtitle->source == VOBSUB ) 
+                    hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
+                else
+                    hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
             }
         }
-
-        pv->busy &=~ 1;
-        return;
+        return HB_WORK_DONE;
     }
-
-    /* At this point we have a frame to process. Let's check
-        1) if we will be able to push into the fifo ahead
-        2) if the next frame is there already, since we need it to
-           compute the duration of the current frame*/
-    while( !hb_fifo_is_full( job->fifo_sync ) &&
-           ( next = hb_fifo_see( job->fifo_raw ) ) )
+    if( sync->pts_offset == INT64_MIN )
     {
-        hb_buffer_t * buf_tmp;
-
-        if( next->size == 0 )
+        /* This is our first frame */
+        sync->pts_offset = 0;
+        if ( cur->start != 0 )
         {
-            /* we got an end-of-stream. Feed it downstream & signal that
-             * we're done. Note that this means we drop the final frame of
-             * video (we don't know its duration). On DVDs the final frame
-             * is often strange and dropping it seems to be a good idea. */
-            hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
-
             /*
-             * Push through any subtitle EOFs in case they were not synced through.
+             * The first pts from a dvd should always be zero but
+             * can be non-zero with a transport or program stream since
+             * we're not guaranteed to start on an IDR frame. If we get
+             * a non-zero initial PTS extend its duration so it behaves
+             * as if it started at zero so that our audio timing will
+             * be in sync.
              */
-            for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
-            {
-                subtitle = hb_list_item( job->list_subtitle, i );
-                if( subtitle->config.dest == PASSTHRUSUB )
-                {
-                    hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
-                }
-            }
-            pv->busy &=~ 1;
-            return;
+            hb_log( "sync: first pts is %"PRId64, cur->start );
+            cur->start = 0;
         }
-        if( pv->pts_offset == INT64_MIN )
+    }
+
+    /*
+     * since the first frame is always 0 and the upstream reader code
+     * is taking care of adjusting for pts discontinuities, we just have
+     * to deal with the next frame's start being in the past. This can
+     * happen when the PTS is adjusted after data loss but video frame
+     * reordering causes some frames with the old clock to appear after
+     * the clock change. This creates frames that overlap in time which
+     * looks to us like time going backward. The downstream muxing code
+     * can deal with overlaps of up to a frame time but anything larger
+     * we handle by dropping frames here.
+     */
+    hb_lock( pv->common->mutex );
+    if ( (int64_t)( next->start - pv->common->video_pts_slip - cur->start ) <= 0 )
+    {
+        if ( sync->first_drop == 0 )
         {
-            /* This is our first frame */
-            pv->pts_offset = 0;
-            if ( cur->start != 0 )
-            {
-                /*
-                 * The first pts from a dvd should always be zero but
-                 * can be non-zero with a transport or program stream since
-                 * we're not guaranteed to start on an IDR frame. If we get
-                 * a non-zero initial PTS extend its duration so it behaves
-                 * as if it started at zero so that our audio timing will
-                 * be in sync.
-                 */
-                hb_log( "sync: first pts is %"PRId64, cur->start );
-                cur->start = 0;
-            }
+            sync->first_drop = next->start;
         }
-
-        /*
-         * since the first frame is always 0 and the upstream reader code
-         * is taking care of adjusting for pts discontinuities, we just have
-         * to deal with the next frame's start being in the past. This can
-         * happen when the PTS is adjusted after data loss but video frame
-         * reordering causes some frames with the old clock to appear after
-         * the clock change. This creates frames that overlap in time which
-         * looks to us like time going backward. The downstream muxing code
-         * can deal with overlaps of up to a frame time but anything larger
-         * we handle by dropping frames here.
-         */
-        if ( (int64_t)( next->start - pv->video_pts_slip - cur->start ) <= 0 )
+        ++sync->drop_count;
+        if (next->start - cur->start > 0)
         {
-            if ( pv->first_drop == 0 )
-            {
-                pv->first_drop = next->start;
-            }
-            ++pv->drop_count;
-            if (next->start - cur->start > 0)
-            {
-                pts_skip += next->start - cur->start;
-                pv->video_pts_slip -= next->start - cur->start;
-            }
-            buf_tmp = hb_fifo_get( job->fifo_raw );
-            if ( buf_tmp->new_chap )
-            {
-                // don't drop a chapter mark when we drop the buffer
-                pv->chap_mark = buf_tmp->new_chap;
-            }
-            hb_buffer_close( &buf_tmp );
-            continue;
+            sync->pts_skip += next->start - cur->start;
+            pv->common->video_pts_slip -= next->start - cur->start;
         }
-        if ( pv->first_drop )
+        hb_unlock( pv->common->mutex );
+        if ( next->new_chap )
         {
-            hb_log( "sync: video time didn't advance - dropped %d frames "
-                    "(delta %d ms, current %"PRId64", next %"PRId64", dur %d)",
-                    pv->drop_count, (int)( cur->start - pv->first_drop ) / 90,
-                    cur->start, next->start, (int)( next->start - cur->start ) );
-            pv->first_drop = 0;
-            pv->drop_count = 0;
+            // don't drop a chapter mark when we drop the buffer
+            sync->chap_mark = next->new_chap;
         }
+        hb_buffer_close( &next );
+        return HB_WORK_OK;
+    }
+    hb_unlock( pv->common->mutex );
+    if ( sync->first_drop )
+    {
+        hb_log( "sync: video time didn't advance - dropped %d frames "
+                "(delta %d ms, current %"PRId64", next %"PRId64", dur %d)",
+                sync->drop_count, (int)( cur->start - sync->first_drop ) / 90,
+                cur->start, next->start, (int)( next->start - cur->start ) );
+        sync->first_drop = 0;
+        sync->drop_count = 0;
+    }
 
-        /*
-         * Track the video sequence number localy so that we can sync the audio
-         * to it using the sequence number as well as the PTS.
-         */
-        pv->video_sequence = cur->sequence;
+    /*
+     * Track the video sequence number localy so that we can sync the audio
+     * to it using the sequence number as well as the PTS.
+     */
+    sync->video_sequence = cur->sequence;
+
+    /*
+     * Look for a subtitle for this frame.
+     *
+     * If found then it will be tagged onto a video buffer of the correct time and 
+     * sent in to the render pipeline. This only needs to be done for VOBSUBs which
+     * get rendered, other types of subtitles can just sit in their raw_queue until
+     * delt with at muxing.
+     */
+    for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
+    {
+        subtitle = hb_list_item( job->list_subtitle, i );
 
         /*
-         * Look for a subtitle for this frame.
-         *
-         * If found then it will be tagged onto a video buffer of the correct time and 
-         * sent in to the render pipeline. This only needs to be done for VOBSUBs which
-         * get rendered, other types of subtitles can just sit in their raw_queue until
-         * delt with at muxing.
+         * Rewrite timestamps on subtitles that need it (on raw queue).
          */
-        for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
+        if( subtitle->source == CC608SUB ||
+            subtitle->source == CC708SUB ||
+            subtitle->source == SRTSUB )
         {
-            subtitle = hb_list_item( job->list_subtitle, i );
-
             /*
-             * Rewrite timestamps on subtitles that need it (on raw queue).
+             * Rewrite timestamps on subtitles that came from Closed Captions
+             * since they are using the MPEG2 timestamps.
              */
-            if( subtitle->source == CC608SUB ||
-                subtitle->source == CC708SUB ||
-                subtitle->source == SRTSUB )
+            while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
             {
                 /*
-                 * Rewrite timestamps on subtitles that came from Closed Captions
-                 * since they are using the MPEG2 timestamps.
+                 * Rewrite the timestamps as and when the video
+                 * (cur->start) reaches the same timestamp as a
+                 * closed caption (sub->start).
+                 *
+                 * What about discontinuity boundaries - not delt
+                 * with here - Van?
+                 *
+                 * Bypass the sync fifo altogether.
                  */
-                while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
+                if( sub->size <= 0 )
                 {
+                    sub = hb_fifo_get( subtitle->fifo_raw );
+                    hb_fifo_push( subtitle->fifo_out, sub );
+                    sub = NULL;
+                    break;
+                } else {
                     /*
-                     * Rewrite the timestamps as and when the video
-                     * (cur->start) reaches the same timestamp as a
-                     * closed caption (sub->start).
+                     * Sync the subtitles to the incoming video, and use
+                     * the matching converted video timestamp.
                      *
-                     * What about discontinuity boundaries - not delt
-                     * with here - Van?
+                     * Note that it doesn't appear that we need to convert 
+                     * timestamps, I guess that they were already correct,
+                     * so just push them through for rendering.
                      *
-                     * Bypass the sync fifo altogether.
                      */
-                    if( sub->size <= 0 )
+                    if( sub->start < cur->start )
                     {
                         sub = hb_fifo_get( subtitle->fifo_raw );
                         hb_fifo_push( subtitle->fifo_out, sub );
+                    } else {
                         sub = NULL;
                         break;
-                    } else {
-                        /*
-                         * Sync the subtitles to the incoming video, and use
-                         * the matching converted video timestamp.
-                         *
-                         * Note that it doesn't appear that we need to convert 
-                         * timestamps, I guess that they were already correct,
-                         * so just push them through for rendering.
-                         *
-                         */
-                        if( sub->start < cur->start )
-                        {
-                            sub = hb_fifo_get( subtitle->fifo_raw );
-                            hb_fifo_push( subtitle->fifo_out, sub );
-                        } else {
-                            sub = NULL;
-                            break;
-                        }
                     }
                 }
             }
+        }
 
-            if( subtitle->source == VOBSUB ) 
+        if( subtitle->source == VOBSUB ) 
+        {
+            hb_buffer_t * sub2;
+            while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
             {
-                hb_buffer_t * sub2;
-                while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
+                if( sub->size == 0 )
                 {
-                    if( sub->size == 0 )
-                    {
-                        /*
-                         * EOF, pass it through immediately.
-                         */
-                        break;
-                    }
+                    /*
+                     * EOF, pass it through immediately.
+                     */
+                    break;
+                }
 
-                    /* If two subtitles overlap, make the first one stop
-                       when the second one starts */
-                    sub2 = hb_fifo_see2( subtitle->fifo_raw );
-                    if( sub2 && sub->stop > sub2->start )
-                    {
-                        sub->stop = sub2->start;
-                    }
-                    
-                    // hb_log("0x%x: video seq: %lld  subtitle sequence: %lld",
-                    //       sub, cur->sequence, sub->sequence);
-                    
-                    if( sub->sequence > cur->sequence )
-                    {
-                        /*
-                         * The video is behind where we are, so wait until
-                         * it catches up to the same reader point on the
-                         * DVD. Then our PTS should be in the same region
-                         * as the video.
-                         */
-                        sub = NULL;
-                        break;
-                    }
-                    
-                    if( sub->stop > cur->start ) {
-                        /*
-                         * The stop time is in the future, so fall through
-                         * and we'll deal with it in the next block of
-                         * code.
-                         */
+                /* If two subtitles overlap, make the first one stop
+                   when the second one starts */
+                sub2 = hb_fifo_see2( subtitle->fifo_raw );
+                if( sub2 && sub->stop > sub2->start )
+                {
+                    sub->stop = sub2->start;
+                }
+                
+                // hb_log("0x%x: video seq: %lld  subtitle sequence: %lld",
+                //       sub, cur->sequence, sub->sequence);
+                
+                if( sub->sequence > cur->sequence )
+                {
+                    /*
+                     * The video is behind where we are, so wait until
+                     * it catches up to the same reader point on the
+                     * DVD. Then our PTS should be in the same region
+                     * as the video.
+                     */
+                    sub = NULL;
+                    break;
+                }
+                
+                if( sub->stop > cur->start ) {
+                    /*
+                     * The stop time is in the future, so fall through
+                     * and we'll deal with it in the next block of
+                     * code.
+                     */
 
+                    /*
+                     * There is a valid subtitle, is it time to display it?
+                     */
+                    if( sub->stop > sub->start)
+                    {
                         /*
-                         * There is a valid subtitle, is it time to display it?
+                         * Normal subtitle which ends after it starts, 
+                         * check to see that the current video is between 
+                         * the start and end.
                          */
-                        if( sub->stop > sub->start)
+                        if( cur->start > sub->start &&
+                            cur->start < sub->stop )
                         {
                             /*
-                             * Normal subtitle which ends after it starts, 
-                             * check to see that the current video is between 
-                             * the start and end.
-                             */
-                            if( cur->start > sub->start &&
-                                cur->start < sub->stop )
+                            * We should be playing this, so leave the
+                            * subtitle in place.
+                            *
+                            * fall through to display
+                            */
+                            if( ( sub->stop - sub->start ) < ( 2 * 90000 ) )
                             {
                                 /*
-                                * We should be playing this, so leave the
-                                * subtitle in place.
-                                *
-                                * fall through to display
-                                */
-                                if( ( sub->stop - sub->start ) < ( 2 * 90000 ) )
+                                 * Subtitle is on for less than three 
+                                 * seconds, extend the time that it is 
+                                 * displayed to make it easier to read. 
+                                 * Make it 3 seconds or until the next
+                                 * subtitle is displayed.
+                                 *
+                                 * This is in response to Indochine which 
+                                 * only displays subs for 1 second - 
+                                 * too fast to read.
+                                 */
+                                sub->stop = sub->start + ( 2 * 90000 );
+                            
+                                sub2 = hb_fifo_see2( subtitle->fifo_raw );
+                            
+                                if( sub2 && sub->stop > sub2->start )
                                 {
-                                    /*
-                                     * Subtitle is on for less than three 
-                                     * seconds, extend the time that it is 
-                                     * displayed to make it easier to read. 
-                                     * Make it 3 seconds or until the next
-                                     * subtitle is displayed.
-                                     *
-                                     * This is in response to Indochine which 
-                                     * only displays subs for 1 second - 
-                                     * too fast to read.
-                                     */
-                                    sub->stop = sub->start + ( 2 * 90000 );
-                                
-                                    sub2 = hb_fifo_see2( subtitle->fifo_raw );
-                                
-                                    if( sub2 && sub->stop > sub2->start )
-                                    {
-                                        sub->stop = sub2->start;
-                                    }
+                                    sub->stop = sub2->start;
                                 }
                             }
-                            else
-                            {
-                                /*
-                                 * Defer until the play point is within 
-                                 * the subtitle
-                                 */
-                                sub = NULL;
-                            }
                         }
                         else
                         {
                             /*
-                             * The end of the subtitle is less than the start, 
-                             * this is a sign of a PTS discontinuity.
+                             * Defer until the play point is within 
+                             * the subtitle
                              */
-                            if( sub->start > cur->start )
-                            {
-                                /*
-                                 * we haven't reached the start time yet, or
-                                 * we have jumped backwards after having
-                                 * already started this subtitle.
-                                 */
-                                if( cur->start < sub->stop )
-                                {
-                                    /*
-                                     * We have jumped backwards and so should
-                                     * continue displaying this subtitle.
-                                     *
-                                     * fall through to display.
-                                     */
-                                }
-                                else
-                                {
-                                    /*
-                                     * Defer until the play point is 
-                                     * within the subtitle
-                                     */
-                                    sub = NULL;
-                                }
-                            } else {
-                                /*
-                                * Play this subtitle as the start is 
-                                * greater than our video point.
-                                *
-                                * fall through to display/
-                                */
-                            }
+                            sub = NULL;
                         }
-                       break;
                     }
                     else
                     {
-                    
                         /*
-                         * The subtitle is older than this picture, trash it
+                         * The end of the subtitle is less than the start, 
+                         * this is a sign of a PTS discontinuity.
                          */
-                        sub = hb_fifo_get( subtitle->fifo_raw );
-                        hb_buffer_close( &sub );
-                    }
-                }
-                
-                /* If we have a subtitle for this picture, copy it */
-                /* FIXME: we should avoid this memcpy */
-                if( sub )
-                {
-                    if( sub->size > 0 )
-                    {
-                        if( subtitle->config.dest == RENDERSUB )
+                        if( sub->start > cur->start )
                         {
-                            if ( cur->sub == NULL )
+                            /*
+                             * we haven't reached the start time yet, or
+                             * we have jumped backwards after having
+                             * already started this subtitle.
+                             */
+                            if( cur->start < sub->stop )
+                            {
+                                /*
+                                 * We have jumped backwards and so should
+                                 * continue displaying this subtitle.
+                                 *
+                                 * fall through to display.
+                                 */
+                            }
+                            else
                             {
                                 /*
-                                 * Tack onto the video buffer for rendering
+                                 * Defer until the play point is 
+                                 * within the subtitle
                                  */
-                                cur->sub         = hb_buffer_init( sub->size );
-                                cur->sub->x      = sub->x;
-                                cur->sub->y      = sub->y;
-                                cur->sub->width  = sub->width;
-                                cur->sub->height = sub->height;
-                                memcpy( cur->sub->data, sub->data, sub->size ); 
+                                sub = NULL;
                             }
                         } else {
                             /*
-                             * Pass-Through, pop it off of the raw queue, 
+                            * Play this subtitle as the start is 
+                            * greater than our video point.
+                            *
+                            * fall through to display/
+                            */
+                        }
+                    }
+                       break;
+                }
+                else
+                {
+                    
+                    /*
+                     * The subtitle is older than this picture, trash it
+                     */
+                    sub = hb_fifo_get( subtitle->fifo_raw );
+                    hb_buffer_close( &sub );
+                }
+            }
+            
+            /* If we have a subtitle for this picture, copy it */
+            /* FIXME: we should avoid this memcpy */
+            if( sub )
+            {
+                if( sub->size > 0 )
+                {
+                    if( subtitle->config.dest == RENDERSUB )
+                    {
+                        if ( cur->sub == NULL )
+                        {
+                            /*
+                             * Tack onto the video buffer for rendering
                              */
-                            sub = hb_fifo_get( subtitle->fifo_raw );
-                            hb_fifo_push( subtitle->fifo_sync, sub );
+                            cur->sub         = hb_buffer_init( sub->size );
+                            cur->sub->x      = sub->x;
+                            cur->sub->y      = sub->y;
+                            cur->sub->width  = sub->width;
+                            cur->sub->height = sub->height;
+                            memcpy( cur->sub->data, sub->data, sub->size ); 
                         }
                     } else {
                         /*
-                        * EOF - consume for rendered, else pass through
-                        */
-                        if( subtitle->config.dest == RENDERSUB )
-                        {
-                            sub = hb_fifo_get( subtitle->fifo_raw );
-                            hb_buffer_close( &sub );
-                        } else {
-                            sub = hb_fifo_get( subtitle->fifo_raw );
-                            hb_fifo_push( subtitle->fifo_out, sub );
-                        }
+                         * Pass-Through, pop it off of the raw queue, 
+                         */
+                        sub = hb_fifo_get( subtitle->fifo_raw );
+                        hb_fifo_push( subtitle->fifo_sync, sub );
+                    }
+                } else {
+                    /*
+                    * EOF - consume for rendered, else pass through
+                    */
+                    if( subtitle->config.dest == RENDERSUB )
+                    {
+                        sub = hb_fifo_get( subtitle->fifo_raw );
+                        hb_buffer_close( &sub );
+                    } else {
+                        sub = hb_fifo_get( subtitle->fifo_raw );
+                        hb_fifo_push( subtitle->fifo_sync, sub );
                     }
                 }
             }
-        } // end subtitles
+        }
+    } // end subtitles
+
+    /*
+     * Adjust the pts of the current frame so that it's contiguous
+     * with the previous frame. The start time of the current frame
+     * has to be the end time of the previous frame and the stop
+     * time has to be the start of the next frame.  We don't
+     * make any adjustments to the source timestamps other than removing
+     * the clock offsets (which also removes pts discontinuities).
+     * This means we automatically encode at the source's frame rate.
+     * MP2 uses an implicit duration (frames end when the next frame
+     * starts) but more advanced containers like MP4 use an explicit
+     * duration. Since we're looking ahead one frame we set the
+     * explicit stop time from the start time of the next frame.
+     */
+    *buf_out = cur;
+    sync->cur = cur = next;
+    cur->sub = NULL;
+    sync->next_pts = cur->start;
+    int64_t duration = cur->start - sync->pts_skip - (*buf_out)->start;
+    sync->pts_skip = 0;
+    if ( duration <= 0 )
+    {
+        hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
+                duration, (*buf_out)->start, next->start );
+    }
+
+    (*buf_out)->start = sync->next_start;
+    sync->next_start += duration;
+    (*buf_out)->stop = sync->next_start;
+
+    if ( sync->chap_mark )
+    {
+        // we have a pending chapter mark from a recent drop - put it on this
+        // buffer (this may make it one frame late but we can't do any better).
+        (*buf_out)->new_chap = sync->chap_mark;
+        sync->chap_mark = 0;
+    }
+
+    /* Update UI */
+    UpdateState( w );
+        
+    return HB_WORK_OK;
+}
+
+// sync*Init does nothing because sync has a special initializer
+// that takes care of initializing video and all audio tracks
+int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
+{
+    return 0;
+}
+
+hb_work_object_t hb_sync_video =
+{
+    WORK_SYNC_VIDEO,
+    "Video Synchronization",
+    syncVideoInit,
+    syncVideoWork,
+    syncVideoClose
+};
 
+/***********************************************************************
+ * Close Audio
+ ***********************************************************************
+ *
+ **********************************************************************/
+void syncAudioClose( hb_work_object_t * w )
+{
+    hb_work_private_t * pv    = w->private_data;
+    hb_sync_audio_t   * sync  = &pv->type.audio;
+
+    if( w->audio->config.out.codec == HB_ACODEC_AC3 )
+    {
+        free( sync->ac3_buf );
+    }
+    else
+    {
+        src_delete( sync->state );
+    }
+
+    hb_lock( pv->common->mutex );
+    if ( --pv->common->ref == 0 )
+    {
+        hb_unlock( pv->common->mutex );
+        hb_lock_close( &pv->common->mutex );
+        free( pv->common );
+    }
+    else
+    {
+        hb_unlock( pv->common->mutex );
+    }
+
+    free( pv );
+    w->private_data = NULL;
+}
+
+int syncAudioInit( hb_work_object_t * w, hb_job_t * job)
+{
+    return 0;
+}
+
+/***********************************************************************
+ * SyncAudio
+ ***********************************************************************
+ *
+ **********************************************************************/
+static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+                       hb_buffer_t ** buf_out )
+{
+    hb_work_private_t * pv = w->private_data;
+    hb_job_t        * job = pv->job;
+    hb_sync_audio_t * sync = &pv->type.audio;
+    hb_buffer_t     * buf;
+    //hb_fifo_t       * fifo;
+    int64_t start;
+
+    *buf_out = NULL;
+    buf = *buf_in;
+    *buf_in = NULL;
+    hb_lock( pv->common->mutex );
+    start = buf->start - pv->common->audio_passthru_slip;
+    hb_unlock( pv->common->mutex );
+    /* if the next buffer is an eof send it downstream */
+    if ( buf->size <= 0 )
+    {
+        hb_buffer_close( &buf );
+        *buf_out = hb_buffer_init( 0 );
+        return HB_WORK_DONE;
+    }
+    if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop )
+    {
+        hb_buffer_close( &buf );
+        *buf_out = hb_buffer_init( 0 );
+        return HB_WORK_DONE;
+    }
+    if ( (int64_t)( start - sync->next_pts ) < 0 )
+    {
+        // audio time went backwards.
+        // If our output clock is more than a half frame ahead of the
+        // input clock drop this frame to move closer to sync.
+        // Otherwise drop frames until the input clock matches the output clock.
+        if ( sync->first_drop || sync->next_start - start > 90*15 )
+        {
+            // Discard data that's in the past.
+            if ( sync->first_drop == 0 )
+            {
+                sync->first_drop = sync->next_pts;
+            }
+            ++sync->drop_count;
+            hb_buffer_close( &buf );
+            return HB_WORK_OK;
+        }
+        sync->next_pts = start;
+    }
+    if ( sync->first_drop )
+    {
+        // we were dropping old data but input buf time is now current
+        hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
+                "(next %"PRId64", current %"PRId64")", w->audio->id,
+                (int)( sync->next_pts - sync->first_drop ) / 90,
+                sync->drop_count, sync->first_drop, sync->next_pts );
+        sync->first_drop = 0;
+        sync->drop_count = 0;
+        sync->next_pts = start;
+    }
+    if ( start - sync->next_pts >= (90 * 70) )
+    {
+        if ( start - sync->next_pts > (90000LL * 60) )
+        {
+            // there's a gap of more than a minute between the last
+            // frame and this. assume we got a corrupted timestamp
+            // and just drop the next buf.
+            hb_log( "sync: %d minute time gap in audio %d - dropping buf"
+                    "  start %"PRId64", next %"PRId64,
+                    (int)((start - sync->next_pts) / (90000*60)),
+                    w->audio->id, start, sync->next_pts );
+            hb_buffer_close( &buf );
+            return HB_WORK_OK;
+        }
         /*
-         * Adjust the pts of the current frame so that it's contiguous
-         * with the previous frame. The start time of the current frame
-         * has to be the end time of the previous frame and the stop
-         * time has to be the start of the next frame.  We don't
-         * make any adjustments to the source timestamps other than removing
-         * the clock offsets (which also removes pts discontinuities).
-         * This means we automatically encode at the source's frame rate.
-         * MP2 uses an implicit duration (frames end when the next frame
-         * starts) but more advanced containers like MP4 use an explicit
-         * duration. Since we're looking ahead one frame we set the
-         * explicit stop time from the start time of the next frame.
+         * there's a gap of at least 70ms between the last
+         * frame we processed & the next. Fill it with silence.
+         * Or in the case of DCA, skip some frames from the
+         * other streams.
          */
-        buf_tmp = cur;
-        pv->cur = cur = hb_fifo_get( job->fifo_raw );
-        cur->sub = NULL;
-        pv->next_pts = cur->start;
-        int64_t duration = cur->start - pts_skip - buf_tmp->start;
-        pts_skip = 0;
-        if ( duration <= 0 )
+        if( w->audio->config.out.codec == HB_ACODEC_DCA )
         {
-            hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
-                    duration, buf_tmp->start, next->start );
+            hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
+                    "  start %"PRId64", next %"PRId64,
+                    (int)((start - sync->next_pts) / 90),
+                    w->audio->id, start, sync->next_pts );
+            hb_lock( pv->common->mutex );
+            pv->common->audio_passthru_slip += (start - sync->next_pts);
+            pv->common->video_pts_slip += (start - sync->next_pts);
+            hb_unlock( pv->common->mutex );
+            *buf_out = buf;
+            return HB_WORK_OK;
         }
+        hb_log( "sync: adding %d ms of silence to audio %d"
+                "  start %"PRId64", next %"PRId64,
+                (int)((start - sync->next_pts) / 90),
+                w->audio->id, start, sync->next_pts );
+        InsertSilence( w, start - sync->next_pts );
+        *buf_out = buf;
+        return HB_WORK_OK;
+    }
+
+    /*
+     * When we get here we've taken care of all the dups and gaps in the
+     * audio stream and are ready to inject the next input frame into
+     * the output stream.
+     */
+    *buf_out = OutputAudioFrame( w->audio, buf, sync );
+    return HB_WORK_OK;
+}
+
+hb_work_object_t hb_sync_audio =
+{
+    WORK_SYNC_AUDIO,
+    "AudioSynchronization",
+    syncAudioInit,
+    syncAudioWork,
+    syncAudioClose
+};
 
-        buf_tmp->start = pv->next_start;
-        pv->next_start += duration;
-        buf_tmp->stop = pv->next_start;
+static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
+{
+    hb_work_object_t  * w;
+    hb_work_private_t * pv;
+    hb_title_t        * title = job->title;
+    hb_sync_audio_t   * sync;
+
+    pv = calloc( 1, sizeof( hb_work_private_t ) );
+    sync = &pv->type.audio;
+    pv->job    = job;
+    pv->common = common;
+    pv->common->ref++;
 
-        if ( pv->chap_mark )
+    w = hb_get_work( WORK_SYNC_AUDIO );
+    w->private_data = pv;
+    w->audio = hb_list_item( title->list_audio, i );
+    w->fifo_in = w->audio->priv.fifo_raw;
+
+    if( w->audio->config.out.codec == HB_ACODEC_AC3 ||
+        w->audio->config.out.codec == HB_ACODEC_DCA )
+    {
+        w->fifo_out = w->audio->priv.fifo_out;
+    }
+    else
+    {
+        w->fifo_out = w->audio->priv.fifo_sync;
+    }
+
+    if( w->audio->config.out.codec == HB_ACODEC_AC3 )
+    {
+        /* Have a silent AC-3 frame ready in case we have to fill a
+           gap */
+        AVCodec        * codec;
+        AVCodecContext * c;
+        short          * zeros;
+
+        codec = avcodec_find_encoder( CODEC_ID_AC3 );
+        c     = avcodec_alloc_context();
+
+        c->bit_rate    = w->audio->config.in.bitrate;
+        c->sample_rate = w->audio->config.in.samplerate;
+        c->channels    = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( w->audio->config.in.channel_layout );
+
+        if( hb_avcodec_open( c, codec ) < 0 )
         {
-            // we have a pending chapter mark from a recent drop - put it on this
-            // buffer (this may make it one frame late but we can't do any better).
-            buf_tmp->new_chap = pv->chap_mark;
-            pv->chap_mark = 0;
+            hb_log( "sync: avcodec_open failed" );
+            return;
         }
 
-        /* Push the frame to the renderer */
-        hb_fifo_push( job->fifo_sync, buf_tmp );
+        zeros          = calloc( AC3_SAMPLES_PER_FRAME *
+                                 sizeof( short ) * c->channels, 1 );
+        sync->ac3_size = w->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
+                             w->audio->config.in.samplerate / 8;
+        sync->ac3_buf  = malloc( sync->ac3_size );
 
-        /* Update UI */
-        UpdateState( w );
-        
-        if( job->frame_to_stop && pv->count_frames > job->frame_to_stop )
+        if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
+                                  zeros ) != sync->ac3_size )
         {
-            // Drop an empty buffer into our output to ensure that things
-            // get flushed all the way out.
-            hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
-            pv->busy &=~ 1;
-            hb_log( "sync: reached %d frames, exiting early (%i busy)",
-                    pv->count_frames, pv->busy );
-            return;
+            hb_log( "sync: avcodec_encode_audio failed" );
         }
+
+        free( zeros );
+        hb_avcodec_close( c );
+        av_free( c );
     }
+    else
+    {
+        /* Initialize libsamplerate */
+        int error;
+        sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, 
+            HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
+                w->audio->config.out.mixdown), &error );
+        sync->data.end_of_input = 0;
+    }
+    hb_list_add( job->list_work, w );
 }
 
-static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf,
-                              hb_sync_audio_t *sync, hb_fifo_t *fifo, int i )
+static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
+                                       hb_sync_audio_t *sync )
 {
     int64_t start = sync->next_start;
     int64_t duration = buf->stop - buf->start;
@@ -788,7 +976,7 @@ static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf
         if( src_process( sync->state, &sync->data ) )
         {
             /* XXX If this happens, we're screwed */
-            hb_log( "sync: audio %d src_process failed", i );
+            hb_log( "sync: audio %d src_process failed", audio->id );
         }
         hb_buffer_close( &buf_raw );
 
@@ -800,136 +988,13 @@ static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf
     buf->start = start;
     buf->stop  = start + duration;
     sync->next_start = start + duration;
-    hb_fifo_push( fifo, buf );
-}
-
-/***********************************************************************
- * SyncAudio
- ***********************************************************************
- *
- **********************************************************************/
-static void SyncAudio( hb_work_object_t * w, int i )
-{
-    hb_work_private_t * pv = w->private_data;
-    hb_job_t        * job = pv->job;
-    hb_sync_audio_t * sync = &pv->sync_audio[i];
-    hb_audio_t      * audio = sync->audio;
-    hb_buffer_t     * buf;
-    hb_fifo_t       * fifo;
-    int64_t start;
-
-    if( audio->config.out.codec == HB_ACODEC_AC3 ||
-        audio->config.out.codec == HB_ACODEC_DCA )
-    {
-        fifo = audio->priv.fifo_out;
-    }
-    else
-    {
-        fifo = audio->priv.fifo_sync;
-    }
-
-    while( !hb_fifo_is_full( fifo ) && ( buf = hb_fifo_see( audio->priv.fifo_raw ) ) )
-    {
-        start = buf->start - pv->audio_passthru_slip;
-        /* if the next buffer is an eof send it downstream */
-        if ( buf->size <= 0 )
-        {
-            buf = hb_fifo_get( audio->priv.fifo_raw );
-            hb_fifo_push( fifo, buf );
-            pv->busy &=~ (1 << (i + 1) );
-            return;
-        }
-        if( job->frame_to_stop && pv->count_frames >= job->frame_to_stop )
-        {
-            hb_fifo_push( fifo, hb_buffer_init(0) );
-            pv->busy &=~ (1 << (i + 1) );
-            return;
-        }
-        if ( (int64_t)( start - sync->next_pts ) < 0 )
-        {
-            // audio time went backwards.
-            // If our output clock is more than a half frame ahead of the
-            // input clock drop this frame to move closer to sync.
-            // Otherwise drop frames until the input clock matches the output clock.
-            if ( sync->first_drop || sync->next_start - start > 90*15 )
-            {
-                // Discard data that's in the past.
-                if ( sync->first_drop == 0 )
-                {
-                    sync->first_drop = sync->next_pts;
-                }
-                ++sync->drop_count;
-                buf = hb_fifo_get( audio->priv.fifo_raw );
-                hb_buffer_close( &buf );
-                continue;
-            }
-            sync->next_pts = start;
-        }
-        if ( sync->first_drop )
-        {
-            // we were dropping old data but input buf time is now current
-            hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
-                    "(next %"PRId64", current %"PRId64")", i,
-                    (int)( sync->next_pts - sync->first_drop ) / 90,
-                    sync->drop_count, sync->first_drop, sync->next_pts );
-            sync->first_drop = 0;
-            sync->drop_count = 0;
-            sync->next_pts = start;
-        }
-        if ( start - sync->next_pts >= (90 * 70) )
-        {
-            if ( start - sync->next_pts > (90000LL * 60) )
-            {
-                // there's a gap of more than a minute between the last
-                // frame and this. assume we got a corrupted timestamp
-                // and just drop the next buf.
-                hb_log( "sync: %d minute time gap in audio %d - dropping buf"
-                        "  start %"PRId64", next %"PRId64,
-                        (int)((start - sync->next_pts) / (90000*60)),
-                        i, start, sync->next_pts );
-                buf = hb_fifo_get( audio->priv.fifo_raw );
-                hb_buffer_close( &buf );
-                continue;
-            }
-            /*
-             * there's a gap of at least 70ms between the last
-             * frame we processed & the next. Fill it with silence.
-             * Or in the case of DCA, skip some frames from the
-             * other streams.
-             */
-            if( sync->audio->config.out.codec == HB_ACODEC_DCA )
-            {
-                hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
-                        "  start %"PRId64", next %"PRId64,
-                        (int)((start - sync->next_pts) / 90),
-                        i, start, sync->next_pts );
-                pv->audio_passthru_slip += (start - sync->next_pts);
-                pv->video_pts_slip += (start - sync->next_pts);
-                return;
-            }
-            hb_log( "sync: adding %d ms of silence to audio %d"
-                    "  start %"PRId64", next %"PRId64,
-                    (int)((start - sync->next_pts) / 90),
-                    i, start, sync->next_pts );
-            InsertSilence( w, i, start - sync->next_pts );
-            return;
-        }
-
-        /*
-         * When we get here we've taken care of all the dups and gaps in the
-         * audio stream and are ready to inject the next input frame into
-         * the output stream.
-         */
-        buf = hb_fifo_get( audio->priv.fifo_raw );
-        OutputAudioFrame( job, audio, buf, sync, fifo, i );
-    }
+    return buf;
 }
 
-static void InsertSilence( hb_work_object_t * w, int i, int64_t duration )
+static void InsertSilence( hb_work_object_t * w, int64_t duration )
 {
     hb_work_private_t * pv = w->private_data;
-    hb_job_t        *job = pv->job;
-    hb_sync_audio_t *sync = &pv->sync_audio[i];
+    hb_sync_audio_t *sync = &pv->type.audio;
     hb_buffer_t     *buf;
     hb_fifo_t       *fifo;
 
@@ -938,72 +1003,74 @@ static void InsertSilence( hb_work_object_t * w, int i, int64_t duration )
     // of the AC3 frame duration we will truncate or round up depending on
     // which minimizes the timing error.
     const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
-                          sync->audio->config.in.samplerate;
+                          w->audio->config.in.samplerate;
     int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
 
     while ( --frame_count >= 0 )
     {
-        if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
+        if( w->audio->config.out.codec == HB_ACODEC_AC3 )
         {
             buf        = hb_buffer_init( sync->ac3_size );
             buf->start = sync->next_pts;
             buf->stop  = buf->start + frame_dur;
             memcpy( buf->data, sync->ac3_buf, buf->size );
-            fifo = sync->audio->priv.fifo_out;
+            fifo = w->audio->priv.fifo_out;
         }
         else
         {
             buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
                                      HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
-                                         sync->audio->config.out.mixdown) );
+                                         w->audio->config.out.mixdown) );
             buf->start = sync->next_pts;
             buf->stop  = buf->start + frame_dur;
             memset( buf->data, 0, buf->size );
-            fifo = sync->audio->priv.fifo_sync;
+            fifo = w->audio->priv.fifo_sync;
         }
-        OutputAudioFrame( job, sync->audio, buf, sync, fifo, i );
+        buf = OutputAudioFrame( w->audio, buf, sync );
+        hb_fifo_push( fifo, buf );
     }
 }
 
 static void UpdateState( hb_work_object_t * w )
 {
     hb_work_private_t * pv = w->private_data;
+    hb_sync_video_t   * sync = &pv->type.video;
     hb_state_t state;
 
-    if( !pv->count_frames )
+    if( !pv->common->count_frames )
     {
-        pv->st_first = hb_get_date();
+        sync->st_first = hb_get_date();
         pv->job->st_pause_date = -1;
         pv->job->st_paused = 0;
     }
-    pv->count_frames++;
+    pv->common->count_frames++;
 
-    if( hb_get_date() > pv->st_dates[3] + 1000 )
+    if( hb_get_date() > sync->st_dates[3] + 1000 )
     {
-        memmove( &pv->st_dates[0], &pv->st_dates[1],
+        memmove( &sync->st_dates[0], &sync->st_dates[1],
                  3 * sizeof( uint64_t ) );
-        memmove( &pv->st_counts[0], &pv->st_counts[1],
+        memmove( &sync->st_counts[0], &sync->st_counts[1],
                  3 * sizeof( uint64_t ) );
-        pv->st_dates[3]  = hb_get_date();
-        pv->st_counts[3] = pv->count_frames;
+        sync->st_dates[3]  = hb_get_date();
+        sync->st_counts[3] = pv->common->count_frames;
     }
 
 #define p state.param.working
     state.state = HB_STATE_WORKING;
-    p.progress  = (float) pv->count_frames / (float) pv->count_frames_max;
+    p.progress  = (float) pv->common->count_frames / (float) sync->count_frames_max;
     if( p.progress > 1.0 )
     {
         p.progress = 1.0;
     }
     p.rate_cur   = 1000.0 *
-        (float) ( pv->st_counts[3] - pv->st_counts[0] ) /
-        (float) ( pv->st_dates[3] - pv->st_dates[0] );
-    if( hb_get_date() > pv->st_first + 4000 )
+        (float) ( sync->st_counts[3] - sync->st_counts[0] ) /
+        (float) ( sync->st_dates[3] - sync->st_dates[0] );
+    if( hb_get_date() > sync->st_first + 4000 )
     {
         int eta;
-        p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
-            (float) ( pv->st_dates[3] - pv->st_first - pv->job->st_paused);
-        eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
+        p.rate_avg = 1000.0 * (float) sync->st_counts[3] /
+            (float) ( sync->st_dates[3] - sync->st_first - pv->job->st_paused);
+        eta = (float) ( sync->count_frames_max - sync->st_counts[3] ) /
             p.rate_avg;
         p.hours   = eta / 3600;
         p.minutes = ( eta % 3600 ) / 60;
index 6bd9700..ba4ea95 100644 (file)
@@ -23,7 +23,10 @@ static void work_func();
 static void do_job( hb_job_t *, int cpu_count );
 static void work_loop( void * );
 
-#define FIFO_CPU_MULT 8
+#define FIFO_LARGE 32
+#define FIFO_LARGE_WAKE 16
+#define FIFO_SMALL 16
+#define FIFO_SMALL_WAKE 15
 
 /**
  * Allocates work object and launches work thread with work_func.
@@ -366,7 +369,6 @@ void correct_framerate( hb_job_t * job )
     job->vrate = job->vrate_base * ( (double)real_frames * 90000 / interjob->total_time );
 }
 
-
 /**
  * Job initialization rountine.
  * Initializes fifos.
@@ -383,6 +385,7 @@ static void do_job( hb_job_t * job, int cpu_count )
     hb_title_t    * title;
     int             i, j;
     hb_work_object_t * w;
+    hb_work_object_t * muxer;
     hb_interjob_t * interjob;
 
     hb_audio_t   * audio;
@@ -459,163 +462,15 @@ static void do_job( hb_job_t * job, int cpu_count )
         job->vrate_base = title->rate_base;
     }
 
-    job->fifo_mpeg2  = hb_fifo_init( 256 );
-    job->fifo_raw    = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-    job->fifo_sync   = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-    job->fifo_render = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-    job->fifo_mpeg4  = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-
-    /* Synchronization */
-    hb_list_add( job->list_work, ( w = hb_get_work( WORK_SYNC ) ) );
-    w->fifo_in  = NULL;
-    w->fifo_out = NULL;
-
-    /* Video decoder */
-    int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
-    hb_list_add( job->list_work, ( w = hb_get_work( vcodec ) ) );
-    w->codec_param = title->video_codec_param;
-    w->fifo_in  = job->fifo_mpeg2;
-    w->fifo_out = job->fifo_raw;
-
-    /* Video renderer */
-    hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) );
-    w->fifo_in  = job->fifo_sync;
-    w->fifo_out = job->fifo_render;
-
-    if( !job->indepth_scan )
-    {
-
-        /* Video encoder */
-        switch( job->vcodec )
-        {
-        case HB_VCODEC_FFMPEG:
-            w = hb_get_work( WORK_ENCAVCODEC );
-            break;
-        case HB_VCODEC_X264:
-            w = hb_get_work( WORK_ENCX264 );
-            break;
-        case HB_VCODEC_THEORA:
-            w = hb_get_work( WORK_ENCTHEORA );
-            break;
-        }
-        w->fifo_in  = job->fifo_render;
-        w->fifo_out = job->fifo_mpeg4;
-        w->config   = &job->config;
-
-        hb_list_add( job->list_work, w );
-    }
+    job->fifo_mpeg2  = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
+    job->fifo_raw    = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+    job->fifo_sync   = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+    job->fifo_render = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+    job->fifo_mpeg4  = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
 
     /*
-     * Look for the scanned subtitle in the existing subtitle list
+     * Audio fifos must be initialized before sync
      */
-    if ( !job->indepth_scan && interjob->select_subtitle &&
-         ( job->pass == 0 || job->pass == 2 ) )
-    {
-        /*
-         * Disable forced subtitles if we didn't find any in the scan
-         * so that we display normal subtitles instead.
-         *
-         * select_subtitle implies that we did a scan.
-         */
-        if( interjob->select_subtitle->config.force && 
-            interjob->select_subtitle->forced_hits == 0 )
-        {
-            interjob->select_subtitle->config.force = 0;
-        }
-        for( i=0; i < hb_list_count(title->list_subtitle); i++ )
-        {
-            subtitle =  hb_list_item( title->list_subtitle, i );
-
-            if( subtitle )
-            {
-                /*
-                * Disable forced subtitles if we didn't find any in the scan
-                * so that we display normal subtitles instead.
-                *
-                * select_subtitle implies that we did a scan.
-                */
-                if( interjob->select_subtitle->id == subtitle->id )
-                {
-                    *subtitle = *(interjob->select_subtitle);
-                    free( interjob->select_subtitle );
-                    interjob->select_subtitle = NULL;
-                }
-            }
-        }
-
-        if( interjob->select_subtitle )
-        {
-            /*
-             * Its not in the existing list
-             *
-             * Must be second pass of a two pass with subtitle scan enabled, so
-             * add the subtitle that we found on the first pass for use in this
-             * pass.
-             */
-            hb_list_add( title->list_subtitle, interjob->select_subtitle );
-            interjob->select_subtitle = NULL;
-        }
-    }
-
-
-    for( i=0; i < hb_list_count(title->list_subtitle); i++ )
-    {
-        subtitle =  hb_list_item( title->list_subtitle, i );
-
-        if( subtitle )
-        {
-            subtitle->fifo_in   = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-            subtitle->fifo_raw  = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-            subtitle->fifo_sync = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-            subtitle->fifo_out  = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-
-            if( (!job->indepth_scan || job->select_subtitle_config.force) && 
-                subtitle->source == VOBSUB ) {
-                /*
-                 * Don't add threads for subtitles when we are scanning, unless
-                 * looking for forced subtitles.
-                 */
-                w = hb_get_work( WORK_DECVOBSUB );
-                w->fifo_in  = subtitle->fifo_in;
-                w->fifo_out = subtitle->fifo_raw;
-                w->subtitle = subtitle;
-                hb_list_add( job->list_work, w );
-            }
-
-            if( !job->indepth_scan && subtitle->source == CC608SUB )
-            {
-                w = hb_get_work( WORK_DECCC608 );
-                w->fifo_in  = subtitle->fifo_in;
-                w->fifo_out = subtitle->fifo_raw;
-                hb_list_add( job->list_work, w );
-            }
-
-            if( !job->indepth_scan && subtitle->source == SRTSUB )
-            {
-                w = hb_get_work( WORK_DECSRTSUB );
-                w->fifo_in  = subtitle->fifo_in;
-                w->fifo_out = subtitle->fifo_raw;
-                w->subtitle = subtitle;
-                hb_list_add( job->list_work, w );
-            }
-
-            if( !job->indepth_scan && 
-                subtitle->format == PICTURESUB
-                && subtitle->config.dest == PASSTHRUSUB )
-            {
-                /*
-                 * Passing through a subtitle picture, this will have to
-                 * be rle encoded before muxing.
-                 */
-                w = hb_get_work( WORK_ENCVOBSUB );
-                w->fifo_in  = subtitle->fifo_sync;
-                w->fifo_out = subtitle->fifo_out;
-                w->subtitle = subtitle;
-                hb_list_add( job->list_work, w );
-            }
-        }
-    }
-
     if( !job->indepth_scan )
     {
     // if we are doing passthru, and the input codec is not the same as the output
@@ -819,54 +674,214 @@ static void do_job( hb_job_t * job, int cpu_count )
             audio->priv.config.vorbis.language = audio->config.lang.simple;
 
         /* set up the audio work structures */
-        audio->priv.fifo_in   = hb_fifo_init( 32 );
-        audio->priv.fifo_raw  = hb_fifo_init( FIFO_CPU_MULT * cpu_count );
-        audio->priv.fifo_sync = hb_fifo_init( 32 );
-        audio->priv.fifo_out  = hb_fifo_init( 8 * FIFO_CPU_MULT * cpu_count );
+        audio->priv.fifo_in   = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
+        audio->priv.fifo_raw  = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+        audio->priv.fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+        audio->priv.fifo_out  = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
+    }
 
+    }
+    /* Synchronization */
+    if( hb_sync_init( job ) )
+    {
+        hb_error( "Failure to initialise sync" );
+        *job->die = 1;
+        goto cleanup;
+    }
 
-        /*
-         * Audio Decoder Thread
-         */
-        if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL )
+    /* Video decoder */
+    int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2;
+    hb_list_add( job->list_work, ( w = hb_get_work( vcodec ) ) );
+    w->codec_param = title->video_codec_param;
+    w->fifo_in  = job->fifo_mpeg2;
+    w->fifo_out = job->fifo_raw;
+
+    /* Video renderer */
+    hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) );
+    w->fifo_in  = job->fifo_sync;
+    w->fifo_out = job->fifo_render;
+
+    if( !job->indepth_scan )
+    {
+
+        /* Video encoder */
+        switch( job->vcodec )
         {
-            hb_error("Invalid input codec: %d", audio->config.in.codec);
-            *job->die = 1;
-            goto cleanup;
+        case HB_VCODEC_FFMPEG:
+            w = hb_get_work( WORK_ENCAVCODEC );
+            break;
+        case HB_VCODEC_X264:
+            w = hb_get_work( WORK_ENCX264 );
+            break;
+        case HB_VCODEC_THEORA:
+            w = hb_get_work( WORK_ENCTHEORA );
+            break;
         }
-        w->fifo_in  = audio->priv.fifo_in;
-        w->fifo_out = audio->priv.fifo_raw;
-        w->config   = &audio->priv.config;
-        w->audio    = audio;
-        w->codec_param = audio->config.in.codec_param;
+        w->fifo_in  = job->fifo_render;
+        w->fifo_out = job->fifo_mpeg4;
+        w->config   = &job->config;
 
         hb_list_add( job->list_work, w );
+    }
 
+    /*
+     * Look for the scanned subtitle in the existing subtitle list
+     */
+    if ( !job->indepth_scan && interjob->select_subtitle &&
+         ( job->pass == 0 || job->pass == 2 ) )
+    {
         /*
-         * Audio Encoder Thread
+         * Disable forced subtitles if we didn't find any in the scan
+         * so that we display normal subtitles instead.
+         *
+         * select_subtitle implies that we did a scan.
          */
-        if( audio->config.out.codec != HB_ACODEC_AC3 &&
-            audio->config.out.codec != HB_ACODEC_DCA )
+        if( interjob->select_subtitle->config.force && 
+            interjob->select_subtitle->forced_hits == 0 )
+        {
+            interjob->select_subtitle->config.force = 0;
+        }
+        for( i=0; i < hb_list_count(title->list_subtitle); i++ )
+        {
+            subtitle =  hb_list_item( title->list_subtitle, i );
+
+            if( subtitle )
+            {
+                /*
+                * Disable forced subtitles if we didn't find any in the scan
+                * so that we display normal subtitles instead.
+                *
+                * select_subtitle implies that we did a scan.
+                */
+                if( interjob->select_subtitle->id == subtitle->id )
+                {
+                    *subtitle = *(interjob->select_subtitle);
+                    free( interjob->select_subtitle );
+                    interjob->select_subtitle = NULL;
+                }
+            }
+        }
+
+        if( interjob->select_subtitle )
         {
             /*
-             * Add the encoder thread if not doing AC-3 pass through
+             * Its not in the existing list
+             *
+             * Must be second pass of a two pass with subtitle scan enabled, so
+             * add the subtitle that we found on the first pass for use in this
+             * pass.
              */
-            if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL )
+            hb_list_add( title->list_subtitle, interjob->select_subtitle );
+            interjob->select_subtitle = NULL;
+        }
+    }
+
+
+    for( i=0; i < hb_list_count(title->list_subtitle); i++ )
+    {
+        subtitle =  hb_list_item( title->list_subtitle, i );
+
+        if( subtitle )
+        {
+            subtitle->fifo_in   = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+            subtitle->fifo_raw  = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+            subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+            subtitle->fifo_out  = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+
+            if( (!job->indepth_scan || job->select_subtitle_config.force) && 
+                subtitle->source == VOBSUB ) {
+                /*
+                 * Don't add threads for subtitles when we are scanning, unless
+                 * looking for forced subtitles.
+                 */
+                w = hb_get_work( WORK_DECVOBSUB );
+                w->fifo_in  = subtitle->fifo_in;
+                w->fifo_out = subtitle->fifo_raw;
+                w->subtitle = subtitle;
+                hb_list_add( job->list_work, w );
+            }
+
+            if( !job->indepth_scan && subtitle->source == CC608SUB )
+            {
+                w = hb_get_work( WORK_DECCC608 );
+                w->fifo_in  = subtitle->fifo_in;
+                w->fifo_out = subtitle->fifo_raw;
+                hb_list_add( job->list_work, w );
+            }
+
+            if( !job->indepth_scan && subtitle->source == SRTSUB )
+            {
+                w = hb_get_work( WORK_DECSRTSUB );
+                w->fifo_in  = subtitle->fifo_in;
+                w->fifo_out = subtitle->fifo_raw;
+                w->subtitle = subtitle;
+                hb_list_add( job->list_work, w );
+            }
+
+            if( !job->indepth_scan && 
+                subtitle->format == PICTURESUB
+                && subtitle->config.dest == PASSTHRUSUB )
+            {
+                /*
+                 * Passing through a subtitle picture, this will have to
+                 * be rle encoded before muxing.
+                 */
+                w = hb_get_work( WORK_ENCVOBSUB );
+                w->fifo_in  = subtitle->fifo_sync;
+                w->fifo_out = subtitle->fifo_out;
+                w->subtitle = subtitle;
+                hb_list_add( job->list_work, w );
+            }
+        }
+    }
+
+    if( !job->indepth_scan )
+    {
+        for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+        {
+            audio = hb_list_item( title->list_audio, i );
+
+            /*
+            * Audio Decoder Thread
+            */
+            if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL )
             {
-                hb_error("Invalid audio codec: %#x", audio->config.out.codec);
-                w = NULL;
+                hb_error("Invalid input codec: %d", audio->config.in.codec);
                 *job->die = 1;
                 goto cleanup;
             }
-            w->fifo_in  = audio->priv.fifo_sync;
-            w->fifo_out = audio->priv.fifo_out;
+            w->fifo_in  = audio->priv.fifo_in;
+            w->fifo_out = audio->priv.fifo_raw;
             w->config   = &audio->priv.config;
             w->audio    = audio;
+            w->codec_param = audio->config.in.codec_param;
 
             hb_list_add( job->list_work, w );
-        }
-    }
 
+            /*
+            * Audio Encoder Thread
+            */
+            if( audio->config.out.codec != HB_ACODEC_AC3 &&
+                audio->config.out.codec != HB_ACODEC_DCA )
+            {
+                /*
+                * Add the encoder thread if not doing AC-3 pass through
+                */
+                if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL )
+                {
+                    hb_error("Invalid audio codec: %#x", audio->config.out.codec);
+                    w = NULL;
+                    *job->die = 1;
+                    goto cleanup;
+                }
+                w->fifo_in  = audio->priv.fifo_sync;
+                w->fifo_out = audio->priv.fifo_out;
+                w->config   = &audio->priv.config;
+                w->audio    = audio;
+
+                hb_list_add( job->list_work, w );
+            }
+        }
     }
 
     /* Display settings */
@@ -896,18 +911,29 @@ static void do_job( hb_job_t * job, int cpu_count )
 
     // The muxer requires track information that's set up by the encoder
     // init routines so we have to init the muxer last.
-    job->muxer = job->indepth_scan? NULL : hb_muxer_init( job );
+    muxer = job->indepth_scan? NULL : hb_muxer_init( job );
 
     w = hb_list_item( job->list_work, 0 );
-    w->thread_sleep_interval = 10;
-    w->init( w, job );
-    while( !*job->die )
+    while( !*job->die && w->status != HB_WORK_DONE )
     {
-        if ( ( w->status = w->work( w, NULL, NULL ) ) == HB_WORK_DONE )
-        {
+        hb_buffer_t      * buf_in, * buf_out;
+
+        buf_in = hb_fifo_get_wait( w->fifo_in );
+        if ( buf_in == NULL )
+            continue;
+        if ( *job->die )
             break;
+
+        w->status = w->work( w, &buf_in, &buf_out );
+
+        if( buf_in )
+        {
+            hb_buffer_close( &buf_in );
+        }
+        if( buf_out )
+        {
+            hb_fifo_push_wait( w->fifo_out, buf_out );
         }
-        hb_snooze( w->thread_sleep_interval );
     }
     hb_list_rem( job->list_work, w );
     w->close( w );
@@ -921,8 +947,11 @@ static void do_job( hb_job_t * job, int cpu_count )
 
 cleanup:
     /* Stop the write thread (thread_close will block until the muxer finishes) */
-    if( job->muxer != NULL )
-        hb_thread_close( &job->muxer );
+    if( muxer != NULL )
+    {
+        hb_thread_close( &muxer->thread );
+        muxer->close( muxer );
+    }
 
     job->done = 1;
 
@@ -1085,21 +1114,13 @@ static void work_loop( void * _w )
     hb_work_object_t * w = _w;
     hb_buffer_t      * buf_in, * buf_out;
 
-    while( !*w->done )
+    while( !*w->done && w->status != HB_WORK_DONE )
     {
-#if 0
-        hb_lock( job->pause );
-        hb_unlock( job->pause );
-#endif
-        if( hb_fifo_is_full( w->fifo_out ) ||
-//        if( (hb_fifo_percent_full( w->fifo_out ) > 0.8) ||
-            !( buf_in = hb_fifo_get( w->fifo_in ) ) )
-        {
-            hb_snooze( w->thread_sleep_interval );
-//                     w->thread_sleep_interval += 1;
+        buf_in = hb_fifo_get_wait( w->fifo_in );
+        if ( buf_in == NULL )
             continue;
-        }
-//             w->thread_sleep_interval = MAX(1, (w->thread_sleep_interval - 1));
+        if ( *w->done )
+            break;
 
         w->status = w->work( w, &buf_in, &buf_out );
 
@@ -1121,7 +1142,7 @@ static void work_loop( void * _w )
         }
         if( buf_out )
         {
-            hb_fifo_push( w->fifo_out, buf_out );
+            hb_fifo_push_wait( w->fifo_out, buf_out );
         }
     }
 }