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 */
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 */
/***********************************************************************
* 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 );
***********************************************************************
* 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;
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 )
{
}
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;
}
/***********************************************************************
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 );
***********************************************************************
*
**********************************************************************/
+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 )
{
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?
*/
*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.
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
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;
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
* 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;
}
/*
* 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
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;
}
*/
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
*/
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
* 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 ) ) )
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 );
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 )
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
/*
* 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
*
* 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
{
* 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
}
/* 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 {
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 );
}
}
*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;
/* 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)
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 );
}
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 )
{
*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
// 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;
}
* 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 );
}
/*
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;
}
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 */
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
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;
}
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;
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;
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;
+}