X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fsync.c;h=b44765e08b5bbd1afe44f891d1bfebe1fbfe94df;hb=814438ce8ec7b400d211ef9050278e283d2bba5f;hp=81e0e35ab610cc43ee0a2533c4b44537dd593048;hpb=ef7415f0dbe174b6dd8fd2c16f8de3bdcccea550;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/sync.c b/libhb/sync.c index 81e0e35a..b44765e0 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -21,14 +21,22 @@ typedef struct 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 audio_pts_slip; int64_t video_pts_slip; + int64_t pts_offset; + + /* Frame based point-to-point support */ + int64_t audio_pts_thresh; + int start_found; + hb_cond_t * next_frame; + int pts_count; + int64_t * first_pts; } 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 */ + int index; + double next_start; /* start time of next output frame */ int64_t first_drop; /* PTS of first 'went backwards' frame dropped */ int drop_count; /* count of 'time went backwards' drops */ @@ -44,10 +52,9 @@ typedef struct typedef struct { /* Video */ - int64_t pts_offset; + int first_frame; 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 */ @@ -77,9 +84,12 @@ 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 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 void UpdateSearchState( hb_work_object_t * w, int64_t start ); static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf, hb_sync_audio_t *sync ); @@ -88,7 +98,7 @@ static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf, *********************************************************************** * Initialize the work object **********************************************************************/ -int hb_sync_init( hb_job_t * job ) +hb_work_object_t * hb_sync_init( hb_job_t * job ) { hb_title_t * title = job->title; hb_chapter_t * chapter; @@ -97,20 +107,33 @@ int hb_sync_init( hb_job_t * job ) hb_work_private_t * pv; hb_sync_video_t * sync; hb_work_object_t * w; + hb_work_object_t * ret = NULL; 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(); + pv->common->audio_pts_thresh = -1; + pv->common->next_frame = hb_cond_init(); + pv->common->pts_count = 1; + if ( job->frame_to_start || job->pts_to_start ) + { + pv->common->start_found = 0; + } + else + { + pv->common->start_found = 1; + } - w = hb_get_work( WORK_SYNC_VIDEO ); + ret = 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; - sync->pts_offset = INT64_MIN; + pv->common->pts_offset = INT64_MIN; + sync->first_frame = 1; if( job->pass == 2 ) { @@ -143,20 +166,22 @@ int hb_sync_init( hb_job_t * job ) } 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", 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++ ) + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { InitAudio( job, pv->common, i ); } } + pv->common->first_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; - return 0; + return ret; } /*********************************************************************** @@ -170,6 +195,11 @@ void syncVideoClose( hb_work_object_t * w ) hb_job_t * job = pv->job; hb_sync_video_t * sync = &pv->type.video; + // 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 ); + if( sync->cur ) { hb_buffer_close( &sync->cur ); @@ -215,6 +245,8 @@ void syncVideoClose( hb_work_object_t * w ) *********************************************************************** * **********************************************************************/ +static hb_buffer_t * copy_subtitle( hb_buffer_t * src ); + int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { @@ -224,12 +256,108 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_subtitle_t * subtitle; hb_sync_video_t * sync = &pv->type.video; int i; + int64_t start, 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 ) + { + pv->common->first_pts[0] = next->start; + hb_lock( pv->common->mutex ); + while( pv->common->pts_offset == INT64_MIN ) + { + // 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 ); + } + hb_unlock( pv->common->mutex ); + } + + hb_lock( pv->common->mutex ); + next_start = next->start - pv->common->video_pts_slip; + hb_unlock( pv->common->mutex ); + + /* Wait for start of point-to-point encoding */ + if( !pv->common->start_found ) + { + hb_sync_video_t * sync = &pv->type.video; + + if( next->size == 0 ) + { + *buf_out = next; + pv->common->start_found = 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 ) + { + 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 ) ); + } + } + return HB_WORK_DONE; + } + if ( pv->common->count_frames < job->frame_to_start || + next->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++) + { + subtitle = hb_list_item( job->list_subtitle, i ); + while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) ) + { + if ( sub->start > next->start ) + break; + sub = hb_fifo_get( subtitle->fifo_raw ); + hb_buffer_close( &sub ); + } + } + hb_lock( pv->common->mutex ); + // Tell the audio threads what must be dropped + pv->common->audio_pts_thresh = next_start + pv->common->video_pts_slip; + hb_cond_broadcast( pv->common->next_frame ); + hb_unlock( pv->common->mutex ); + + UpdateSearchState( w, next_start ); + hb_buffer_close( &next ); + + 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 ); + sync->st_first = 0; + } + if( !sync->cur ) { - sync->cur = *buf_in; - *buf_in = NULL; + sync->cur = next; if( sync->cur->size == 0 ) { /* we got an end-of-stream as our first video packet? @@ -237,6 +365,9 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, */ *buf_out = hb_buffer_init( 0 ); + pv->common->start_found = 1; + hb_cond_broadcast( pv->common->next_frame ); + /* * Push through any subtitle EOFs in case they * were not synced through. @@ -246,28 +377,17 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, 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 ) ); } } return HB_WORK_DONE; } return HB_WORK_OK; } - next = *buf_in; - *buf_in = NULL; cur = sync->cur; - 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 ); - 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 @@ -279,6 +399,9 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, cur->start = sync->next_start; cur->stop = cur->start + 90000. / ((double)job->vrate / (double)job->vrate_base); + /* Make sure last frame is reflected in frame count */ + pv->common->count_frames++; + /* Push the frame to the renderer */ hb_fifo_push( job->fifo_sync, cur ); sync->cur = NULL; @@ -303,13 +426,74 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, 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; } - if( sync->pts_offset == INT64_MIN ) + + /* Check for end of point-to-point frame encoding */ + 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 ); + if( subtitle->config.dest == PASSTHRUSUB ) + { + 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 ) ); + } + } + return HB_WORK_DONE; + } + + hb_lock( pv->common->mutex ); + start = cur->start - pv->common->video_pts_slip; + hb_unlock( pv->common->mutex ); + + /* Check for end of point-to-point pts encoding */ + if( job->pts_to_stop && sync->next_start >= job->pts_to_stop ) + { + // 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", 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 ); + if( subtitle->config.dest == PASSTHRUSUB ) + { + 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 ) ); + } + } + return HB_WORK_DONE; + } + + if( sync->first_frame ) { /* This is our first frame */ - sync->pts_offset = 0; - if ( cur->start != 0 ) + if ( start > 0 ) { /* * The first pts from a dvd should always be zero but @@ -319,9 +503,10 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * 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; + hb_log( "sync: first pts is %"PRId64, start ); + start = 0; } + sync->first_frame = 0; } /* @@ -335,20 +520,13 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * 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 ( next_start - start <= 0 ) { if ( sync->first_drop == 0 ) { - sync->first_drop = next->start; + sync->first_drop = next_start; } ++sync->drop_count; - if (next->start - cur->start > 0) - { - sync->pts_skip += next->start - cur->start; - pv->common->video_pts_slip -= next->start - cur->start; - } - hb_unlock( pv->common->mutex ); if ( next->new_chap ) { // don't drop a chapter mark when we drop the buffer @@ -357,13 +535,12 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, 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->drop_count, (int)( start - sync->first_drop ) / 90, + start, next_start, (int)( next_start - start ) ); sync->first_drop = 0; sync->drop_count = 0; } @@ -384,14 +561,16 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { + int64_t sub_start, sub_stop, duration; subtitle = hb_list_item( job->list_subtitle, i ); /* * Rewrite timestamps on subtitles that need it (on raw queue). */ - if( subtitle->source == CC608SUB || - subtitle->source == CC708SUB || - subtitle->source == SRTSUB ) + // NOTE: It's probably fine to use this logic for passthru VOBSUBs as well, + // but I am currently preserving backwards compatibility with the old + // VOBSUB behavior, which uses the more complex logic following this if-statement. + if( subtitle->config.dest == PASSTHRUSUB && subtitle->source != VOBSUB ) { /* * Rewrite timestamps on subtitles that came from Closed Captions @@ -399,6 +578,12 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, */ while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) ) { + hb_lock( pv->common->mutex ); + sub_start = sub->start - pv->common->video_pts_slip; + hb_unlock( pv->common->mutex ); + duration = sub->stop - sub->start; + sub_stop = sub_start + duration; + /* * Rewrite the timestamps as and when the video * (cur->start) reaches the same timestamp as a @@ -425,19 +610,26 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * so just push them through for rendering. * */ - if( sub->start < cur->start ) + if( sub_start <= start ) { sub = hb_fifo_get( subtitle->fifo_raw ); + sub->start = sub_start; + sub->stop = sub_stop; hb_fifo_push( subtitle->fifo_out, sub ); } else { + // sub too early. Leave it in the fifo. sub = NULL; break; } } } + + continue; } - if( subtitle->source == VOBSUB ) + // For rendered subtitles (and, for backward compatibility, passthru VOBSUBs), + // delay pushing subtitle packets through the pipeline until the video catches up + if( subtitle->config.dest == RENDERSUB || subtitle->source == VOBSUB ) { hb_buffer_t * sub2; while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) ) @@ -450,6 +642,12 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, break; } + hb_lock( pv->common->mutex ); + sub_start = sub->start - pv->common->video_pts_slip; + hb_unlock( pv->common->mutex ); + duration = sub->stop - sub->start; + sub_stop = sub_start + duration; + /* If two subtitles overlap, make the first one stop when the second one starts */ sub2 = hb_fifo_see2( subtitle->fifo_raw ); @@ -458,7 +656,7 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, sub->stop = sub2->start; } - // hb_log("0x%x: video seq: %lld subtitle sequence: %lld", + // hb_log("0x%x: video seq: %"PRId64" subtitle sequence: %"PRId64, // sub, cur->sequence, sub->sequence); if( sub->sequence > cur->sequence ) @@ -473,7 +671,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, break; } - if( sub->stop > cur->start ) { + if( sub_stop > start ) + { /* * The stop time is in the future, so fall through * and we'll deal with it in the next block of @@ -483,15 +682,15 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, /* * There is a valid subtitle, is it time to display it? */ - if( sub->stop > sub->start) + if( sub_stop > sub_start) { /* * 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 ) + if( start > sub_start && + start < sub_stop ) { /* * We should be playing this, so leave the @@ -499,28 +698,6 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * * 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 ) - { - sub->stop = sub2->start; - } - } } else { @@ -537,14 +714,14 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * The end of the subtitle is less than the start, * this is a sign of a PTS discontinuity. */ - if( sub->start > cur->start ) + if( sub_start > 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 ) + if( start < sub_stop ) { /* * We have jumped backwards and so should @@ -584,30 +761,33 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, } /* 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 ) { + // Only allow one subtitle to be showing at once; ignore others if ( cur->sub == NULL ) { /* * Tack onto the video buffer for rendering */ - 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 ); + /* FIXME: we should avoid this memcpy */ + cur->sub = copy_subtitle( sub ); + cur->sub->start = sub_start; + cur->sub->stop = sub_stop; + + // Leave the subtitle on the raw queue + // (until it no longer needs to be displayed) } } else { /* * Pass-Through, pop it off of the raw queue, */ sub = hb_fifo_get( subtitle->fifo_raw ); + sub->start = sub_start; + sub->stop = sub_stop; hb_fifo_push( subtitle->fifo_sync, sub ); } } else { @@ -620,6 +800,8 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_close( &sub ); } else { sub = hb_fifo_get( subtitle->fifo_raw ); + sub->start = sub_start; + sub->stop = sub_stop; hb_fifo_push( subtitle->fifo_sync, sub ); } } @@ -643,13 +825,12 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, *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; + int64_t duration = next_start - 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 ); + duration, start, next_start ); } (*buf_out)->start = sync->next_start; @@ -666,10 +847,33 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, /* Update UI */ UpdateState( w ); - + return HB_WORK_OK; } +static hb_buffer_t * copy_subtitle( hb_buffer_t * src_list ) +{ + hb_buffer_t * dst_list = NULL; + + hb_buffer_t * src; + hb_buffer_t * dst; + hb_buffer_t ** dst_ptr = &dst_list; + for ( src = src_list, dst_ptr = &dst_list; + src; + src = src->next_subpicture, dst_ptr = &dst->next_subpicture ) + { + (*dst_ptr) = hb_buffer_init( src->size ); + dst = (*dst_ptr); + dst->x = src->x; + dst->y = src->y; + dst->width = src->width; + dst->height = src->height; + memcpy( dst->data, src->data, src->size ); + } + + return dst_list; +} + // 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) @@ -696,7 +900,7 @@ 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 ) + if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ) { free( sync->ac3_buf ); } @@ -738,15 +942,11 @@ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, 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 ) { @@ -754,13 +954,77 @@ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, *buf_out = hb_buffer_init( 0 ); return HB_WORK_DONE; } + + /* Wait till we can determine the initial pts of all streams */ + if( pv->common->pts_offset == INT64_MIN ) + { + pv->common->first_pts[sync->index+1] = buf->start; + hb_lock( pv->common->mutex ); + while( pv->common->pts_offset == INT64_MIN ) + { + // 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 ); + } + + /* Wait for start frame if doing point-to-point */ + hb_lock( pv->common->mutex ); + start = buf->start - pv->common->audio_pts_slip; + while ( !pv->common->start_found ) + { + if ( pv->common->audio_pts_thresh < 0 ) + { + // 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->start < pv->common->audio_pts_thresh ) + { + hb_buffer_close( &buf ); + hb_unlock( pv->common->mutex ); + return HB_WORK_OK; + } + while ( !pv->common->start_found && + buf->start >= pv->common->audio_pts_thresh ) + { + hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 ); + } + start = buf->start - pv->common->audio_pts_slip; + } + if ( start < 0 ) + { + hb_buffer_close( &buf ); + hb_unlock( pv->common->mutex ); + return HB_WORK_OK; + } + hb_unlock( pv->common->mutex ); + 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 ) + + if( job->pts_to_stop && sync->next_start >= job->pts_to_stop ) + { + hb_buffer_close( &buf ); + *buf_out = hb_buffer_init( 0 ); + return HB_WORK_DONE; + } + + if ( start - sync->next_start < 0 ) { // audio time went backwards. // If our output clock is more than a half frame ahead of the @@ -771,36 +1035,34 @@ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, // Discard data that's in the past. if ( sync->first_drop == 0 ) { - sync->first_drop = sync->next_pts; + sync->first_drop = sync->next_start; } ++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 ); + (int)( sync->next_start - sync->first_drop ) / 90, + sync->drop_count, sync->first_drop, (int64_t)sync->next_start ); sync->first_drop = 0; sync->drop_count = 0; - sync->next_pts = start; } - if ( start - sync->next_pts >= (90 * 70) ) + if ( start - sync->next_start >= (90 * 70) ) { - if ( start - sync->next_pts > (90000LL * 60) ) + if ( start - sync->next_start > (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 ); + (int)((start - sync->next_start) / (90000*60)), + w->audio->id, start, (int64_t)sync->next_start ); hb_buffer_close( &buf ); return HB_WORK_OK; } @@ -810,26 +1072,24 @@ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, * Or in the case of DCA, skip some frames from the * other streams. */ - if( w->audio->config.out.codec == HB_ACODEC_DCA ) + if( w->audio->config.out.codec == HB_ACODEC_DCA_PASS ) { 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 ); + (int)((start - sync->next_start) / 90), + w->audio->id, start, (int64_t)sync->next_start ); hb_lock( pv->common->mutex ); - pv->common->audio_passthru_slip += (start - sync->next_pts); - pv->common->video_pts_slip += (start - sync->next_pts); + pv->common->audio_pts_slip += (start - sync->next_start); + pv->common->video_pts_slip += (start - sync->next_start); 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; + (int)((start - sync->next_start) / 90), + w->audio->id, start, (int64_t)sync->next_start ); + InsertSilence( w, start - sync->next_start ); } /* @@ -859,17 +1119,19 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ) pv = calloc( 1, sizeof( hb_work_private_t ) ); sync = &pv->type.audio; + sync->index = i; pv->job = job; pv->common = common; pv->common->ref++; + pv->common->pts_count++; 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 ) + if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS || + w->audio->config.out.codec == HB_ACODEC_DCA_PASS ) { w->fifo_out = w->audio->priv.fifo_out; } @@ -878,7 +1140,7 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ) w->fifo_out = w->audio->priv.fifo_sync; } - if( w->audio->config.out.codec == HB_ACODEC_AC3 ) + if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ) { /* Have a silent AC-3 frame ready in case we have to fill a gap */ @@ -930,14 +1192,12 @@ static void InitAudio( hb_job_t * job, hb_sync_common_t * common, 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; - - sync->next_pts += duration; + int64_t start = (int64_t)sync->next_start; + double duration = buf->stop - buf->start; if( audio->config.in.samplerate == audio->config.out.samplerate || - audio->config.out.codec == HB_ACODEC_AC3 || - audio->config.out.codec == HB_ACODEC_DCA ) + audio->config.out.codec == HB_ACODEC_AC3_PASS || + audio->config.out.codec == HB_ACODEC_DCA_PASS ) { /* * If we don't have to do sample rate conversion or this audio is @@ -981,13 +1241,13 @@ static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf, hb_buffer_close( &buf_raw ); buf->size = sync->data.output_frames_gen * channel_count; - duration = ( sync->data.output_frames_gen * 90000 ) / + duration = (double)( sync->data.output_frames_gen * 90000 ) / audio->config.out.samplerate; } buf->frametype = HB_FRAME_AUDIO; buf->start = start; - buf->stop = start + duration; - sync->next_start = start + duration; + sync->next_start += duration; + buf->stop = (int64_t)sync->next_start; return buf; } @@ -1008,10 +1268,10 @@ static void InsertSilence( hb_work_object_t * w, int64_t duration ) while ( --frame_count >= 0 ) { - if( w->audio->config.out.codec == HB_ACODEC_AC3 ) + if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ) { buf = hb_buffer_init( sync->ac3_size ); - buf->start = sync->next_pts; + buf->start = sync->next_start; buf->stop = buf->start + frame_dur; memcpy( buf->data, sync->ac3_buf, buf->size ); fifo = w->audio->priv.fifo_out; @@ -1021,7 +1281,7 @@ static void InsertSilence( hb_work_object_t * w, int64_t duration ) buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT( w->audio->config.out.mixdown) ); - buf->start = sync->next_pts; + buf->start = sync->next_start; buf->stop = buf->start + frame_dur; memset( buf->data, 0, buf->size ); fifo = w->audio->priv.fifo_sync; @@ -1087,3 +1347,92 @@ static void UpdateState( hb_work_object_t * w ) hb_set_state( pv->job->h, &state ); } + +static void UpdateSearchState( hb_work_object_t * w, int64_t start ) +{ + hb_work_private_t * pv = w->private_data; + hb_sync_video_t * sync = &pv->type.video; + hb_state_t state; + uint64_t now; + double avg; + + now = hb_get_date(); + if( !pv->common->count_frames ) + { + sync->st_first = now; + pv->job->st_pause_date = -1; + pv->job->st_paused = 0; + } + pv->common->count_frames++; + +#define p state.param.working + state.state = HB_STATE_SEARCHING; + if ( pv->job->frame_to_start ) + 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; + else + p.progress = 0; + if( p.progress > 1.0 ) + { + p.progress = 1.0; + } + if (now > sync->st_first) + { + int eta; + + if ( pv->job->frame_to_start ) + { + avg = 1000.0 * (double)pv->common->count_frames / (now - sync->st_first); + eta = ( pv->job->frame_to_start - pv->common->count_frames ) / avg; + } + else if ( pv->job->pts_to_start ) + { + avg = 1000.0 * (double)start / (now - sync->st_first); + eta = ( pv->job->pts_to_start - start ) / avg; + } + p.hours = eta / 3600; + p.minutes = ( eta % 3600 ) / 60; + p.seconds = eta % 60; + } + else + { + p.rate_avg = 0.0; + p.hours = -1; + p.minutes = -1; + p.seconds = -1; + } +#undef p + + hb_set_state( pv->job->h, &state ); +} + +static void getPtsOffset( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + int i ; + int64_t first_pts = INT64_MAX; + + for( i = 0; i < pv->common->pts_count; i++ ) + { + if ( pv->common->first_pts[i] < first_pts ) + first_pts = pv->common->first_pts[i]; + } + 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; +}