diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/common.h | 5 | ||||
-rw-r--r-- | libhb/fifo.c | 134 | ||||
-rw-r--r-- | libhb/hb.c | 8 | ||||
-rw-r--r-- | libhb/internal.h | 19 | ||||
-rw-r--r-- | libhb/muxcommon.c | 420 | ||||
-rw-r--r-- | libhb/ports.c | 29 | ||||
-rw-r--r-- | libhb/ports.h | 1 | ||||
-rw-r--r-- | libhb/reader.c | 37 | ||||
-rw-r--r-- | libhb/render.c | 4 | ||||
-rw-r--r-- | libhb/sync.c | 1355 | ||||
-rw-r--r-- | libhb/work.c | 433 |
11 files changed, 1396 insertions, 1049 deletions
diff --git a/libhb/common.h b/libhb/common.h index a85beda01..d76022238 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -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 diff --git a/libhb/fifo.c b/libhb/fifo.c index 48f645970..781c9400b 100644 --- a/libhb/fifo.c +++ b/libhb/fifo.c @@ -10,11 +10,18 @@ #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 ); + +} + diff --git a/libhb/hb.c b/libhb/hb.c index 1b899e39c..3db173f0a 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -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 diff --git a/libhb/internal.h b/libhb/internal.h index 88c8ef983..808537448 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -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 diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c index 1f39df11d..dbaa9c478 100644 --- a/libhb/muxcommon.c +++ b/libhb/muxcommon.c @@ -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 +}; + diff --git a/libhb/ports.c b/libhb/ports.c index 120038618..10b598c37 100644 --- a/libhb/ports.c +++ b/libhb/ports.c @@ -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 ) diff --git a/libhb/ports.h b/libhb/ports.h index 47be2d76e..318a7d717 100644 --- a/libhb/ports.h +++ b/libhb/ports.h @@ -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 ** ); diff --git a/libhb/reader.c b/libhb/reader.c index 94ee77efb..346cafd5a 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -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 ); diff --git a/libhb/render.c b/libhb/render.c index a4943b776..6888a1e0a 100644 --- a/libhb/render.c +++ b/libhb/render.c @@ -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 diff --git a/libhb/sync.c b/libhb/sync.c index 94b8411b4..e886ac168 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -18,12 +18,19 @@ 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; diff --git a/libhb/work.c b/libhb/work.c index 6bd9700f7..ba4ea956e 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -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 ); } } } |