diff options
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/fifo.c | 18 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/ports.c | 5 | ||||
-rw-r--r-- | libhb/ports.h | 2 | ||||
-rw-r--r-- | libhb/sync.c | 575 | ||||
-rw-r--r-- | libhb/work.c | 19 |
7 files changed, 291 insertions, 330 deletions
diff --git a/libhb/common.h b/libhb/common.h index ca5f25178..c9cf50798 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -1105,6 +1105,7 @@ struct hb_work_object_s hb_work_private_t * private_data; hb_thread_t * thread; + int yield; volatile int * done; int status; int codec_param; diff --git a/libhb/fifo.c b/libhb/fifo.c index 3be686500..f88c43bfb 100644 --- a/libhb/fifo.c +++ b/libhb/fifo.c @@ -26,6 +26,7 @@ struct hb_fifo_s int wait_full; hb_cond_t * cond_empty; int wait_empty; + hb_cond_t * cond_alert_full; uint32_t capacity; uint32_t thresh; uint32_t size; @@ -864,6 +865,11 @@ hb_fifo_t * hb_fifo_init( int capacity, int thresh ) return f; } +void hb_fifo_register_full_cond( hb_fifo_t * f, hb_cond_t * c ) +{ + f->cond_alert_full = c; +} + int hb_fifo_size_bytes( hb_fifo_t * f ) { int ret = 0; @@ -1055,6 +1061,8 @@ void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b ) if( f->size >= f->capacity ) { f->wait_full = 1; + if (f->cond_alert_full != NULL) + hb_cond_broadcast( f->cond_alert_full ); hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT ); } if( f->size > 0 ) @@ -1089,6 +1097,11 @@ void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b ) } hb_lock( f->lock ); + if (f->size >= f->capacity && + f->cond_alert_full != NULL) + { + hb_cond_broadcast( f->cond_alert_full ); + } if( f->size > 0 ) { f->last->next = b; @@ -1124,6 +1137,11 @@ void hb_fifo_push_head( hb_fifo_t * f, hb_buffer_t * b ) } hb_lock( f->lock ); + if (f->size >= f->capacity && + f->cond_alert_full != NULL) + { + hb_cond_broadcast( f->cond_alert_full ); + } /* * If there are a chain of buffers prepend the lot diff --git a/libhb/internal.h b/libhb/internal.h index 727bf8ce3..7337a1387 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -179,6 +179,7 @@ hb_image_t * hb_image_init(int pix_fmt, int width, int height); hb_image_t * hb_buffer_to_image(hb_buffer_t *buf); hb_fifo_t * hb_fifo_init( int capacity, int thresh ); +void hb_fifo_register_full_cond( hb_fifo_t * f, hb_cond_t * c ); int hb_fifo_size( hb_fifo_t * ); int hb_fifo_size_bytes( hb_fifo_t * ); int hb_fifo_is_full( hb_fifo_t * ); diff --git a/libhb/ports.c b/libhb/ports.c index c75a7c943..2a9ffd971 100644 --- a/libhb/ports.c +++ b/libhb/ports.c @@ -1076,6 +1076,11 @@ void hb_clock_gettime( struct timespec *tp ) tp->tv_nsec = tv.tv_usec * 1000; } +void hb_yield(void) +{ + pthread_yield(); +} + void hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec ) { #if defined( SYS_BEOS ) diff --git a/libhb/ports.h b/libhb/ports.h index 42cdede6a..9a3da7a6a 100644 --- a/libhb/ports.h +++ b/libhb/ports.h @@ -133,6 +133,8 @@ hb_thread_t * hb_thread_init( const char * name, thread_func_t *function, void hb_thread_close( hb_thread_t ** ); int hb_thread_has_exited( hb_thread_t * ); +void hb_yield(void); + /************************************************************************ * Mutexes ***********************************************************************/ diff --git a/libhb/sync.c b/libhb/sync.c index c672b3d4a..d11fbd228 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -17,21 +17,26 @@ #endif #define INT64_MIN (-9223372036854775807LL-1) +#define ABS(a) ((a) < 0 ? -(a) : (a)) + typedef struct { + /* Audio/Video sync thread synchronization */ hb_lock_t * mutex; + hb_cond_t * next_frame; + int64_t volatile * last_pts; + int pts_count; + int ref; /* Reference count to tell us when it's unused */ - int count_frames; + + /* PTS synchronization */ int64_t audio_pts_slip; int64_t video_pts_slip; int64_t pts_offset; - /* Frame based point-to-point support */ - int64_t audio_pts_thresh; + /* point-to-point support */ int start_found; - hb_cond_t * next_frame; - int pts_count; - int64_t * first_pts; + int count_frames; } hb_sync_common_t; typedef struct @@ -98,8 +103,8 @@ struct hb_work_private_s /*********************************************************************** * Local prototypes **********************************************************************/ -static void getPtsOffset( hb_work_object_t * w ); -static int checkPtsOffset( hb_work_object_t * w ); +static void setSyncPTS(hb_work_private_t * pv, int64_t pts, int index); +static void getPtsOffset( hb_work_private_t * pv ); static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ); static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int i ); static void InsertSilence( hb_work_object_t * w, int64_t d ); @@ -129,10 +134,9 @@ hb_work_object_t * hb_sync_init( hb_job_t * job ) pv->common = calloc( 1, sizeof( hb_sync_common_t ) ); pv->common->ref++; pv->common->mutex = hb_lock_init(); - pv->common->audio_pts_thresh = AV_NOPTS_VALUE; pv->common->next_frame = hb_cond_init(); pv->common->pts_count = 1; - if ( job->frame_to_start || job->pts_to_start ) + if (job->frame_to_start || job->pts_to_start) { pv->common->start_found = 0; } @@ -144,6 +148,9 @@ hb_work_object_t * hb_sync_init( hb_job_t * job ) ret = w = hb_get_work( job->h, WORK_SYNC_VIDEO ); w->private_data = pv; w->fifo_in = job->fifo_raw; + // Register condition with fifo to wake us up immediately if + // the fifo becomes full + hb_fifo_register_full_cond(w->fifo_in, pv->common->next_frame); // When doing subtitle indepth scan, the pipeline ends at sync if ( !job->indepth_scan ) @@ -171,8 +178,8 @@ hb_work_object_t * hb_sync_init( hb_job_t * job ) else if( job->frame_to_stop ) { /* Set the duration to a rough estimate */ - duration = (int64_t)job->frame_to_stop * title->vrate.den * 90000 / - title->vrate.num; + duration = (int64_t)(job->frame_to_stop + 1) * + title->vrate.den * 90000 / title->vrate.num; } else { @@ -196,9 +203,11 @@ hb_work_object_t * hb_sync_init( hb_job_t * job ) InitAudio( job, pv->common, i ); } } - pv->common->first_pts = malloc( sizeof(int64_t) * pv->common->pts_count ); + pv->common->last_pts = malloc( sizeof(int64_t) * pv->common->pts_count ); for ( i = 0; i < pv->common->pts_count; i++ ) - pv->common->first_pts[i] = INT64_MAX; + { + pv->common->last_pts[i] = AV_NOPTS_VALUE; + } int count = hb_list_count(job->list_subtitle); sync->subtitle_sanitizer = calloc(count, sizeof(subtitle_sanitizer_t)); @@ -252,7 +261,8 @@ void syncVideoClose( hb_work_object_t * w ) // Wake up audio sync if it's still waiting on condition. pv->common->pts_offset = 0; pv->common->start_found = 1; - hb_cond_broadcast( pv->common->next_frame ); + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, 0); if( sync->cur ) { @@ -273,7 +283,7 @@ void syncVideoClose( hb_work_object_t * w ) if (sync->drops || sync->dups ) { - hb_log( "sync: %d frames dropped, %d duplicated", + hb_log( "sync: %d frames dropped, %d duplicated", sync->drops, sync->dups ); } @@ -290,8 +300,8 @@ void syncVideoClose( hb_work_object_t * w ) hb_unlock( pv->common->mutex ); hb_cond_close( &pv->common->next_frame ); hb_lock_close( &pv->common->mutex ); - free( pv->common->first_pts ); - free( pv->common ); + free((void*)pv->common->last_pts); + free(pv->common); } else { @@ -302,8 +312,6 @@ void syncVideoClose( hb_work_object_t * w ) w->private_data = NULL; } -#define ABS(a) ((a) < 0 ? -(a) : (a)) - static hb_buffer_t * merge_ssa(hb_buffer_t *a, hb_buffer_t *b) { int len, ii; @@ -482,13 +490,11 @@ static hb_buffer_t * sanitizeSubtitle( return mergeSubtitles(sanitizer, 1); } - hb_lock( pv->common->mutex ); sub->s.start -= pv->common->video_pts_slip; if (sub->s.stop != AV_NOPTS_VALUE) sub->s.stop -= pv->common->video_pts_slip; if (sub->s.renderOffset != AV_NOPTS_VALUE) sub->s.renderOffset -= pv->common->video_pts_slip; - hb_unlock( pv->common->mutex ); if (sanitizer->last != NULL && sanitizer->last->s.stop == AV_NOPTS_VALUE) { @@ -517,6 +523,88 @@ static hb_buffer_t * sanitizeSubtitle( return mergeSubtitles(sanitizer, 0); } +static void setSyncPTS(hb_work_private_t * pv, int64_t pts, int index) +{ + hb_lock(pv->common->mutex); + pv->common->last_pts[index] = pts; + hb_unlock(pv->common->mutex); + hb_cond_broadcast(pv->common->next_frame); +} + +static void resetSync(hb_work_private_t * pv) +{ + int ii; + + hb_lock(pv->common->mutex); + for (ii = 0; ii < pv->common->pts_count; ii++) + { + // Unblock any sync thread that are waiting for a PTS + pv->common->last_pts[ii] = INT64_MAX; + } + hb_unlock(pv->common->mutex); + hb_cond_broadcast(pv->common->next_frame); + hb_yield(); +} + +// Keeps sync tasks "in sync". I.e. the lowest pts will always be +// output first. +static int waitForSync(hb_work_object_t * w, int64_t pts, int index, + hb_fifo_t *fifo) +{ + hb_work_private_t * pv = w->private_data; + + setSyncPTS(pv, pts, index); + + int ii; + hb_lock(pv->common->mutex); + for (ii = 0; ii < pv->common->pts_count; ii++) + { + while (pts > pv->common->last_pts[ii]) + { + // wait for other streams to catch up + // since fifos can become full and clog up the works, + // check if our fifo is full when waking. + // Also check if encoding was canceled. + hb_cond_timedwait(pv->common->next_frame, pv->common->mutex, 200); + if (*w->done || + (pts > pv->common->last_pts[ii] && hb_fifo_is_full(fifo))) + { + hb_unlock(pv->common->mutex); + getPtsOffset(pv); + return 0; + } + } + } + hb_unlock(pv->common->mutex); + getPtsOffset(pv); + return 1; +} + +static void flushSubtitles(hb_work_private_t *pv) +{ + hb_job_t * job = pv->job; + hb_subtitle_t * subtitle; + int ii; + + /* + * Push through any subtitle EOFs in case they were not synced through. + */ + for (ii = 0; ii < hb_list_count(job->list_subtitle); ii++) + { + subtitle = hb_list_item(job->list_subtitle, ii); + // flush out any pending subtitle buffers in the sanitizer + hb_buffer_t *out = sanitizeSubtitle(pv, ii, NULL); + if (out != NULL) + { + hb_fifo_push(subtitle->fifo_out, out); + } + if (subtitle->config.dest == PASSTHRUSUB) + { + hb_fifo_push(subtitle->fifo_out, hb_buffer_init(0)); + } + } +} + /*********************************************************************** * syncVideoWork *********************************************************************** @@ -533,65 +621,63 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, int i; int64_t next_start; - *buf_out = NULL; next = *buf_in; *buf_in = NULL; - /* Wait till we can determine the initial pts of all streams */ - if( next->size != 0 && pv->common->pts_offset == INT64_MIN ) + if (next->size == 0) { - pv->common->first_pts[0] = next->s.start; - hb_lock( pv->common->mutex ); - while( pv->common->pts_offset == INT64_MIN && !*w->done ) + if (sync->cur != NULL) { - // Full fifos will make us wait forever, so get the - // pts offset from the available streams if full - if ( hb_fifo_is_full( job->fifo_raw ) ) - { - getPtsOffset( w ); - hb_cond_broadcast( pv->common->next_frame ); - } - else if ( checkPtsOffset( w ) ) - hb_cond_broadcast( pv->common->next_frame ); - else - hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 ); + cur = sync->cur; + cur->s.start = sync->next_start; + cur->s.stop = cur->s.start + 90000L * + job->vrate.den / job->vrate.num; + + /* Make sure last frame is reflected in frame count */ + pv->common->count_frames++; + + /* Push the frame to the renderer */ + *buf_out = 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)->next = next; } - hb_unlock( pv->common->mutex ); + else + { + *buf_out = next; + } + flushSubtitles(pv); + pv->common->start_found = 1; + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, 0); + return HB_WORK_DONE; } - hb_lock( pv->common->mutex ); next_start = next->s.start - pv->common->video_pts_slip; - hb_unlock( pv->common->mutex ); + if (pv->common->pts_offset == INT64_MIN || !pv->common->start_found || + job->frame_to_stop > 0) + { + waitForSync(w, next_start, 0, w->fifo_in); + // video_pts_slip may change in during waitForSync + next_start = next->s.start - pv->common->video_pts_slip; + } + else + { + setSyncPTS(pv, next_start, 0); + } /* Wait for start of point-to-point encoding */ - if( !pv->common->start_found ) + if (!pv->common->start_found) { - hb_sync_video_t * sync = &pv->type.video; - - if( next->size == 0 ) + if (pv->common->count_frames < job->frame_to_start || + next->s.start < job->pts_to_start) { - *buf_out = next; - pv->common->start_found = 1; - pv->common->first_pts[0] = INT64_MAX - 1; - hb_cond_broadcast( pv->common->next_frame ); + UpdateSearchState( w, next_start ); - /* - * 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; - } - if ( pv->common->count_frames < job->frame_to_start || - next->s.start < job->pts_to_start ) - { // Flush any subtitles that have pts prior to the // current frame for( i = 0; i < hb_list_count( job->list_subtitle ); i++) @@ -605,19 +691,6 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_close( &sub ); } } - hb_lock( pv->common->mutex ); - if (job->frame_to_start > 0) - { - // When doing frame based p-to-p we must update the audio - // start point with each frame skipped. - // - // Tell the audio threads what must be dropped - pv->common->audio_pts_thresh = next->s.start; - } - hb_cond_broadcast( pv->common->next_frame ); - hb_unlock( pv->common->mutex ); - - UpdateSearchState( w, next_start ); #ifdef USE_QSV // reclaim QSV resources before dropping the buffer // when decoding without QSV, the QSV atom will be NULL @@ -646,121 +719,29 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, return HB_WORK_OK; } hb_lock( pv->common->mutex ); - pv->common->audio_pts_thresh = 0; pv->common->audio_pts_slip += next_start; pv->common->video_pts_slip += next_start; - next_start = 0; pv->common->start_found = 1; pv->common->count_frames = 0; - hb_cond_broadcast( pv->common->next_frame ); hb_unlock( pv->common->mutex ); + next_start = 0; sync->st_first = 0; - } - - if( !sync->cur ) - { - sync->cur = next; - if (next->size == 0) - { - /* we got an end-of-stream as our first video packet? - * Feed it downstream & signal that we're done. - */ - *buf_out = next; - sync->cur = NULL; - - pv->common->start_found = 1; - pv->common->first_pts[0] = INT64_MAX - 1; - hb_cond_broadcast( pv->common->next_frame ); - - /* - * 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; - } - return HB_WORK_OK; - } - cur = sync->cur; - /* 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 ) - { - hb_buffer_close( &next ); - - pv->common->first_pts[0] = INT64_MAX - 1; - cur->s.start = sync->next_start; - cur->s.stop = cur->s.start + 90000L * job->vrate.den / job->vrate.num; - sync->next_start += cur->s.stop - cur->s.start;; - - /* Make sure last frame is reflected in frame count */ - pv->common->count_frames++; - - /* Push the frame to the renderer */ - *buf_out = 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)->next = hb_buffer_init( 0 ); - - /* - * 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 ); - // flush out any pending subtitle buffers in the sanitizer - hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); - if (out != NULL) - hb_fifo_push( subtitle->fifo_out, out ); - if( subtitle->config.dest == PASSTHRUSUB ) - { - hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); - } - } - pv->common->start_found = 1; - hb_cond_broadcast( pv->common->next_frame ); - return HB_WORK_DONE; + resetSync(pv); } /* Check for end of point-to-point frame encoding */ - if( job->frame_to_stop && pv->common->count_frames > job->frame_to_stop ) + if (job->frame_to_stop && pv->common->count_frames > job->frame_to_stop) { // 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 ); - - /* - * 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 ); - // flush out any pending subtitle buffers in the sanitizer - hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); - if (out != NULL) - hb_fifo_push( subtitle->fifo_out, out ); - if( subtitle->config.dest == PASSTHRUSUB ) - { - hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); - } - } + hb_log("sync: reached %d frames, exiting early", + pv->common->count_frames); + hb_buffer_close(&sync->cur); + hb_buffer_close(&next); + *buf_out = hb_buffer_init(0); + flushSubtitles(pv); + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, 0); return HB_WORK_DONE; } @@ -769,33 +750,30 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, { // Drop an empty buffer into our output to ensure that things // get flushed all the way out. - hb_log( "sync: reached pts %"PRId64", exiting early", cur->s.start ); - hb_buffer_close( &sync->cur ); + hb_log("sync: reached pts %"PRId64", exiting early", + sync->cur->s.start); + hb_buffer_close(&sync->cur); hb_buffer_close( &next ); - *buf_out = hb_buffer_init( 0 ); - - /* - * 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 ); - // flush out any pending subtitle buffers in the sanitizer - hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); - if (out != NULL) - hb_fifo_push( subtitle->fifo_out, out ); - if( subtitle->config.dest == PASSTHRUSUB ) - { - hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); - } - } + *buf_out = hb_buffer_init(0); + flushSubtitles(pv); + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, 0); return HB_WORK_DONE; } - if( sync->first_frame ) + if (sync->cur == NULL) + { + sync->cur = next; + return HB_WORK_OK; + } + + // At this point, we have 2 video frames wich allows us to set the + // duration of the first and output it. + cur = sync->cur; + if (sync->first_frame) { /* This is our first frame */ - if ( cur->s.start > 0 ) + if (cur->s.start > 0) { /* * The first pts from a dvd should always be zero but @@ -877,13 +855,13 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * to it using the sequence number as well as the PTS. */ sync->video_sequence = cur->sequence; - + /* Process subtitles that apply to this video frame */ // NOTE: There is no logic in either subtitle-sync algorithm that waits // for the subtitle-decoder if it is lagging behind the video-decoder. - // - // Therefore there is the implicit assumption that the subtitle-decoder - // is always faster than the video-decoder. This assumption is definitely + // + // Therefore there is the implicit assumption that the subtitle-decoder + // is always faster than the video-decoder. This assumption is definitely // incorrect in some cases where the SSA subtitle decoder is used. for( i = 0; i < hb_list_count( job->list_subtitle ); i++) @@ -891,8 +869,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t *out; subtitle = hb_list_item( job->list_subtitle, i ); - - // Sanitize subtitle start and stop times, then pass to + + // Sanitize subtitle start and stop times, then pass to // muxer or renderer filter. while ( ( sub = hb_fifo_get( subtitle->fifo_raw ) ) != NULL ) { @@ -982,6 +960,9 @@ void syncAudioClose( hb_work_object_t * w ) hb_work_private_t * pv = w->private_data; hb_sync_audio_t * sync = &pv->type.audio; + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, sync->index+1); + if( sync->silence_buf ) { free( sync->silence_buf ); @@ -997,8 +978,8 @@ void syncAudioClose( hb_work_object_t * w ) hb_unlock( pv->common->mutex ); hb_cond_close( &pv->common->next_frame ); hb_lock_close( &pv->common->mutex ); - free( pv->common->first_pts ); - free( pv->common ); + free((void*)pv->common->last_pts); + free(pv->common); } else { @@ -1028,124 +1009,72 @@ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t * buf; int64_t start; - *buf_out = NULL; buf = *buf_in; *buf_in = NULL; + /* if the next buffer is an eof send it downstream */ if ( buf->size <= 0 ) { - hb_buffer_close( &buf ); - *buf_out = hb_buffer_init( 0 ); - pv->common->first_pts[sync->index+1] = INT64_MAX - 1; + *buf_out = buf; + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, sync->index+1); return HB_WORK_DONE; } - /* Wait till we can determine the initial pts of all streams */ - if( pv->common->pts_offset == INT64_MIN ) + start = buf->s.start - pv->common->audio_pts_slip; + if (pv->common->pts_offset == INT64_MIN || !pv->common->start_found || + job->frame_to_stop > 0) { - pv->common->first_pts[sync->index+1] = buf->s.start; - hb_lock( pv->common->mutex ); - while( pv->common->pts_offset == INT64_MIN && !*w->done) - { - // Full fifos will make us wait forever, so get the - // pts offset from the available streams if full - if (hb_fifo_is_full(w->fifo_in)) - { - getPtsOffset( w ); - hb_cond_broadcast( pv->common->next_frame ); - } - else if ( checkPtsOffset( w ) ) - hb_cond_broadcast( pv->common->next_frame ); - else - hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 ); - } - hb_unlock( pv->common->mutex ); + waitForSync(w, start, sync->index+1, w->fifo_in); + // audio_pts_slip may change in waitForSync() + start = buf->s.start - pv->common->audio_pts_slip; + } + else + { + setSyncPTS(pv, start, sync->index+1); } // Wait for start frame if doing point-to-point // - // When doing p-to-p, video leads the way. The video thead will set + // When doing frame p-to-p, video leads the way. The video thead will set // start_found when we have reached the start point. - // - // When doing frame based p-to-p, as each video frame is processed - // it advances audio_pts_thresh which informs us how much audio must - // be dropped. - hb_lock( pv->common->mutex ); - while ( !pv->common->start_found && !*w->done ) + if (!pv->common->start_found) { - if ( pv->common->audio_pts_thresh < 0 ) + if (job->pts_to_start > 0 && buf->s.start >= job->pts_to_start) { - // I would initialize this in hb_sync_init, but - // job->pts_to_start can be modified by reader - // after hb_sync_init is called. - pv->common->audio_pts_thresh = job->pts_to_start; - } - if ( buf->s.start < pv->common->audio_pts_thresh ) - { - hb_buffer_close( &buf ); + hb_lock( pv->common->mutex ); + pv->common->start_found = 1; + pv->common->audio_pts_slip += start; + pv->common->video_pts_slip += start; + pv->common->count_frames = 0; hb_unlock( pv->common->mutex ); - return HB_WORK_OK; + resetSync(pv); + start = 0; } - - // We should only get here when doing frame based p-to-p. - // In frame based p-to-p, the video sync thread updates - // audio_pts_thresh as it discards frames. So wait here - // until the current audio frame needs to be discarded - // or start point is found. - while ( !pv->common->start_found && - buf->s.start >= pv->common->audio_pts_thresh && !*w->done ) + else { - hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 10 ); - // There is an unfortunate unavoidable deadlock that can occur. - // Since we need to wait for a specific frame in syncVideoWork, - // syncAudioWork can be stalled indefinitely. The video decoder - // often drops multiple of the initial frames after starting - // because they require references that have not been decoded yet. - // This allows a lot of audio to be queued in the fifo and the - // audio fifo fills before we get a single video frame. So we - // must drop some audio to unplug the pipeline and allow the first - // video frame to be decoded. - if ( hb_fifo_is_full(w->fifo_in) ) - { - hb_buffer_t *tmp; - tmp = buf = hb_fifo_get( w->fifo_in ); - while ( tmp ) - { - tmp = hb_fifo_get( w->fifo_in ); - if ( tmp ) - { - hb_buffer_close( &buf ); - buf = tmp; - } - } - } + // For frame-to-frame encoding, the video sync thread will + // tell us when it is ok to start + hb_buffer_close(&buf); + return HB_WORK_OK; } } - start = buf->s.start - pv->common->audio_pts_slip; - hb_unlock( pv->common->mutex ); - // When doing p-to-p, video determines when the start point has been - // found. Since audio and video are processed asynchronously, there - // may yet be audio packets in the pipe that are before the start point. - // These packets will have negative start times after applying - // audio_pts_slip. - if (start < 0) - { - hb_buffer_close(&buf); - return HB_WORK_OK; - } - - if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop ) + // Check for p-to-p end time + if (job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop) { hb_buffer_close( &buf ); - *buf_out = hb_buffer_init( 0 ); + *buf_out = hb_buffer_init(0); + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, sync->index+1); return HB_WORK_DONE; } - - if( job->pts_to_stop && sync->next_start >= job->pts_to_stop ) + if (job->pts_to_stop && sync->next_start >= job->pts_to_stop) { hb_buffer_close( &buf ); - *buf_out = hb_buffer_init( 0 ); + *buf_out = hb_buffer_init(0); + // Unblock anybody waiting on this threads last PTS + setSyncPTS(pv, INT64_MAX, sync->index+1); return HB_WORK_DONE; } @@ -1250,6 +1179,9 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ) w->private_data = pv; w->audio = hb_list_item( job->list_audio, i ); w->fifo_in = w->audio->priv.fifo_raw; + // Register condition with fifo to wake us up immediately if + // the fifo becomes full + hb_fifo_register_full_cond(w->fifo_in, pv->common->next_frame); if ( w->audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { @@ -1639,7 +1571,7 @@ static void UpdateState( hb_work_object_t * w ) hb_state_t state; hb_get_state2( pv->job->h, &state ); - if( !pv->common->count_frames ) + if ( !pv->common->count_frames ) { sync->st_first = hb_get_date(); pv->job->st_pause_date = -1; @@ -1726,7 +1658,7 @@ static void UpdateSearchState( hb_work_object_t * w, int64_t start ) #define p state.param.working state.state = HB_STATE_SEARCHING; if ( pv->job->frame_to_start ) - p.progress = (float) pv->common->count_frames / + p.progress = (float) pv->common->count_frames / (float) pv->job->frame_to_start; else if ( pv->job->pts_to_start ) p.progress = (float) start / (float) pv->job->pts_to_start; @@ -1766,31 +1698,22 @@ static void UpdateSearchState( hb_work_object_t * w, int64_t start ) hb_set_state( pv->job->h, &state ); } -static void getPtsOffset( hb_work_object_t * w ) +static void getPtsOffset(hb_work_private_t * pv) { - hb_work_private_t * pv = w->private_data; - int i ; - int64_t first_pts = INT64_MAX; + if (pv->common->pts_offset != INT64_MIN) + return; - for( i = 0; i < pv->common->pts_count; i++ ) + int64_t first_pts = INT64_MAX; + int ii; + for (ii = 0; ii < pv->common->pts_count; ii++) { - if ( pv->common->first_pts[i] < first_pts ) - first_pts = pv->common->first_pts[i]; + if (pv->common->last_pts[ii] != AV_NOPTS_VALUE && + pv->common->last_pts[ii] < first_pts) + { + first_pts = pv->common->last_pts[ii]; + } } - pv->common->video_pts_slip = pv->common->audio_pts_slip = pv->common->pts_offset = first_pts; + pv->common->video_pts_slip = pv->common->audio_pts_slip = + pv->common->pts_offset = first_pts; return; } - -static int checkPtsOffset( hb_work_object_t * w ) -{ - hb_work_private_t * pv = w->private_data; - int i ; - - for( i = 0; i < pv->common->pts_count; i++ ) - { - if ( pv->common->first_pts[i] == INT64_MAX ) - return 0; - } - getPtsOffset( w ); - return 1; -} diff --git a/libhb/work.c b/libhb/work.c index 582b2a5ef..3168e1fde 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -163,16 +163,22 @@ hb_work_object_t * hb_get_work( hb_handle_t *h, int id ) hb_work_object_t* hb_codec_decoder(hb_handle_t *h, int codec) { + hb_work_object_t * w = NULL; if (codec & HB_ACODEC_FF_MASK) { - return hb_get_work(h, WORK_DECAVCODEC); + w = hb_get_work(h, WORK_DECAVCODEC); + w->yield = 1; // decoders yield to keep sync fifos more even } switch (codec) { - case HB_ACODEC_LPCM: return hb_get_work(h, WORK_DECLPCM); - default: break; + case HB_ACODEC_LPCM: + w = hb_get_work(h, WORK_DECLPCM); + w->yield = 1; // decoders yield to keep sync fifos more even + break; + default: + break; } - return NULL; + return w; } hb_work_object_t* hb_codec_encoder(hb_handle_t *h, int codec) @@ -1228,6 +1234,7 @@ static void do_job(hb_job_t *job) goto cleanup; } hb_list_add(job->list_work, (w = hb_get_work(job->h, title->video_codec))); + w->yield = 1; // decoders yield to keep sync fifos more even w->codec_param = title->video_codec_param; w->fifo_in = job->fifo_mpeg2; w->fifo_out = job->fifo_raw; @@ -1767,6 +1774,10 @@ static void work_loop( void * _w ) } } } + if (w->yield) + { + hb_yield(); + } } if ( buf_out ) { |