} hb_sync_audio_t;
-struct hb_work_object_s
+struct hb_work_private_s
{
- HB_WORK_COMMON;
-
hb_job_t * job;
int done;
* Local prototypes
**********************************************************************/
static void InitAudio( hb_work_object_t * w, int i );
-static void Close( hb_work_object_t ** _w );
-static int Work( hb_work_object_t * w, hb_buffer_t ** unused1,
- hb_buffer_t ** unused2 );
static int SyncVideo( hb_work_object_t * w );
static void SyncAudio( hb_work_object_t * w, int i );
static int NeedSilence( hb_work_object_t * w, hb_audio_t * );
***********************************************************************
* Initialize the work object
**********************************************************************/
-hb_work_object_t * hb_work_sync_init( hb_job_t * job )
+int syncInit( hb_work_object_t * w, hb_job_t * job )
{
- hb_work_object_t * w;
hb_title_t * title = job->title;
hb_chapter_t * chapter;
int i;
uint64_t duration;
+ hb_work_private_t * pv;
- w = calloc( sizeof( hb_work_object_t ), 1 );
- w->name = strdup( "Synchronization" );
- w->work = Work;
- w->close = Close;
+ pv = calloc( 1, sizeof( hb_work_private_t ) );
+ w->private_data = pv;
- w->job = job;
- w->pts_offset = INT64_MIN;
- w->pts_offset_old = INT64_MIN;
- w->count_frames = 0;
+ pv->job = job;
+ pv->pts_offset = INT64_MIN;
+ pv->pts_offset_old = INT64_MIN;
+ pv->count_frames = 0;
/* Calculate how many video frames we are expecting */
duration = 0;
}
duration += 90000;
/* 1 second safety so we're sure we won't miss anything */
- w->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
+ pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
- hb_log( "sync: expecting %lld video frames", w->count_frames_max );
+ hb_log( "sync: expecting %lld video frames", pv->count_frames_max );
/* Initialize libsamplerate for every audio track we have */
for( i = 0; i < hb_list_count( title->list_audio ); i++ )
}
/* Get subtitle info, if any */
- w->subtitle = hb_list_item( title->list_subtitle, 0 );
+ pv->subtitle = hb_list_item( title->list_subtitle, 0 );
+
+ return 0;
+}
+
+/***********************************************************************
+ * Close
+ ***********************************************************************
+ *
+ **********************************************************************/
+void syncClose( 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;
+
+ int i;
- return w;
+ if( pv->cur ) hb_buffer_close( &pv->cur );
+
+ for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+ {
+ if( job->acodec & HB_ACODEC_AC3 )
+ {
+ free( pv->sync_audio[i].ac3_buf );
+ }
+ else
+ {
+ src_delete( pv->sync_audio[i].state );
+ }
+ }
+
+ free( pv );
+ w->private_data = NULL;
}
+/***********************************************************************
+ * Work
+ ***********************************************************************
+ * The root routine of this work abject
+ **********************************************************************/
+int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
+ hb_buffer_t ** unused2 )
+{
+ hb_work_private_t * pv = w->private_data;
+ int i;
+
+ /* If we ever got a video frame, handle audio now */
+ if( pv->pts_offset != INT64_MIN )
+ {
+ for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
+ {
+ SyncAudio( w, i );
+ }
+ }
+
+ /* Handle video */
+ return SyncVideo( w );
+}
+
+hb_work_object_t hb_sync =
+{
+ WORK_SYNC,
+ "Synchronization",
+ syncInit,
+ syncWork,
+ syncClose
+};
+
static void InitAudio( hb_work_object_t * w, int i )
{
- hb_job_t * job = w->job;
+ 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 = &w->sync_audio[i];
+ sync = &pv->sync_audio[i];
sync->audio = hb_list_item( title->list_audio, i );
if( job->acodec & HB_ACODEC_AC3 )
c->bit_rate = sync->audio->bitrate;
c->sample_rate = sync->audio->rate;
- c->channels = sync->audio->channels;
+ c->channels = 2;
if( avcodec_open( c, codec ) < 0 )
{
{
/* Initialize libsamplerate */
int error;
- sync->state = src_new( SRC_LINEAR, 2, &error );
+ sync->state = src_new( SRC_LINEAR, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown), &error );
sync->data.end_of_input = 0;
}
}
-/***********************************************************************
- * Close
- ***********************************************************************
- *
- **********************************************************************/
-static void Close( hb_work_object_t ** _w )
-{
- hb_work_object_t * w = *_w;
- hb_job_t * job = w->job;
- hb_title_t * title = job->title;
-
- int i;
-
- if( w->cur ) hb_buffer_close( &w->cur );
-
- for( i = 0; i < hb_list_count( title->list_audio ); i++ )
- {
- if( job->acodec & HB_ACODEC_AC3 )
- {
- free( w->sync_audio[i].ac3_buf );
- }
- else
- {
- src_delete( w->sync_audio[i].state );
- }
- }
-
- free( w->name );
- free( w );
- *_w = NULL;
-}
-
-/***********************************************************************
- * Work
- ***********************************************************************
- * The root routine of this work abject
- **********************************************************************/
-static int Work( hb_work_object_t * w, hb_buffer_t ** unused1,
- hb_buffer_t ** unused2 )
-{
- int i;
- /* If we ever got a video frame, handle audio now */
- if( w->pts_offset != INT64_MIN )
- {
- for( i = 0; i < hb_list_count( w->job->title->list_audio ); i++ )
- {
- SyncAudio( w, i );
- }
- }
- /* Handle video */
- return SyncVideo( w );
-}
+#define PTS_DISCONTINUITY_TOLERANCE 90000
/***********************************************************************
* SyncVideo
**********************************************************************/
static int 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 = w->job;
+ hb_job_t * job = pv->job;
int64_t pts_expected;
- if( w->done )
+ if( pv->done )
{
return HB_WORK_DONE;
}
/* All video data has been processed already, we won't get
more */
hb_log( "sync: got %lld frames, %lld expected",
- w->count_frames, w->count_frames_max );
- w->done = 1;
+ pv->count_frames, pv->count_frames_max );
+ pv->done = 1;
+
+ hb_buffer_t * buf_tmp;
+
+ // Drop an empty buffer into our output to ensure that things
+ // get flushed all the way out.
+ buf_tmp = hb_buffer_init(0); // Empty end buffer
+ hb_fifo_push( job->fifo_sync, buf_tmp );
+
return HB_WORK_DONE;
}
- if( !w->cur && !( w->cur = hb_fifo_get( job->fifo_raw ) ) )
+ if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
{
/* We haven't even got a frame yet */
return HB_WORK_OK;
}
- cur = w->cur;
+ cur = pv->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
{
hb_buffer_t * buf_tmp;
- if( w->pts_offset == INT64_MIN )
+ if( pv->pts_offset == INT64_MIN )
{
/* This is our first frame */
hb_log( "sync: first pts is %lld", cur->start );
- w->pts_offset = cur->start;
+ pv->pts_offset = cur->start;
}
/* Check for PTS jumps over 0.5 second */
- if( next->start < cur->start - 45000 ||
- next->start > cur->start + 45000 )
+ if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE ||
+ next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE )
{
hb_log( "PTS discontinuity (%lld, %lld)",
cur->start, next->start );
/* Trash all subtitles */
- if( w->subtitle )
+ if( pv->subtitle )
{
- while( ( sub = hb_fifo_get( w->subtitle->fifo_raw ) ) )
+ while( ( sub = hb_fifo_get( pv->subtitle->fifo_raw ) ) )
{
hb_buffer_close( &sub );
}
/* Trash current picture */
hb_buffer_close( &cur );
- w->cur = cur = hb_fifo_get( job->fifo_raw );
+ pv->cur = cur = hb_fifo_get( job->fifo_raw );
/* Calculate new offset */
- w->pts_offset_old = w->pts_offset;
- w->pts_offset = cur->start -
- w->count_frames * w->job->vrate_base / 300;
+ pv->pts_offset_old = pv->pts_offset;
+ pv->pts_offset = cur->start -
+ pv->count_frames * pv->job->vrate_base / 300;
continue;
}
/* Look for a subtitle for this frame */
- if( w->subtitle )
+ if( pv->subtitle )
{
- /* Trash subtitles older than this picture */
- while( ( sub = hb_fifo_see( w->subtitle->fifo_raw ) ) &&
- sub->stop < cur->start )
+ hb_buffer_t * sub2;
+ while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
{
- sub = hb_fifo_get( w->subtitle->fifo_raw );
+ /* If two subtitles overlap, make the first one stop
+ when the second one starts */
+ sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
+ if( sub2 && sub->stop > sub2->start )
+ sub->stop = sub2->start;
+
+ if( sub->stop > cur->start )
+ break;
+
+ /* The subtitle is older than this picture, trash it */
+ sub = hb_fifo_get( pv->subtitle->fifo_raw );
hb_buffer_close( &sub );
}
}
/* The PTS of the frame we are expecting now */
- pts_expected = w->pts_offset +
- w->count_frames * w->job->vrate_base / 300;
+ pts_expected = pv->pts_offset +
+ pv->count_frames * pv->job->vrate_base / 300;
- if( cur->start < pts_expected - w->job->vrate_base / 300 / 2 &&
- next->start < pts_expected + w->job->vrate_base / 300 / 2 )
+ if( cur->start < pts_expected - pv->job->vrate_base / 300 / 2 &&
+ next->start < pts_expected + pv->job->vrate_base / 300 / 2 )
{
/* The current frame is too old but the next one matches,
let's trash */
hb_buffer_close( &cur );
- w->cur = cur = hb_fifo_get( job->fifo_raw );
+ pv->cur = cur = hb_fifo_get( job->fifo_raw );
continue;
}
- if( next->start > pts_expected + 3 * w->job->vrate_base / 300 / 2 )
+ if( next->start > pts_expected + 3 * pv->job->vrate_base / 300 / 2 )
{
/* We'll need the current frame more than one time. Make a
copy of it and keep it */
/* The frame has the expected date and won't have to be
duplicated, just put it through */
buf_tmp = cur;
- w->cur = cur = hb_fifo_get( job->fifo_raw );
+ pv->cur = cur = hb_fifo_get( job->fifo_raw );
}
/* Replace those MPEG-2 dates with our dates */
- buf_tmp->start = (uint64_t) w->count_frames *
- w->job->vrate_base / 300;
- buf_tmp->stop = (uint64_t) ( w->count_frames + 1 ) *
- w->job->vrate_base / 300;
+ buf_tmp->start = (uint64_t) pv->count_frames *
+ pv->job->vrate_base / 300;
+ buf_tmp->stop = (uint64_t) ( pv->count_frames + 1 ) *
+ pv->job->vrate_base / 300;
/* If we have a subtitle for this picture, copy it */
/* FIXME: we should avoid this memcpy */
UpdateState( w );
/* Make sure we won't get more frames then expected */
- if( w->count_frames >= w->count_frames_max )
+ if( pv->count_frames >= pv->count_frames_max )
{
- hb_log( "sync: got %lld frames", w->count_frames );
- w->done = 1;
+ hb_log( "sync: got %lld frames", pv->count_frames );
+ pv->done = 1;
+
+ // Drop an empty buffer into our output to ensure that things
+ // get flushed all the way out.
+ buf_tmp = hb_buffer_init(0); // Empty end buffer
+ hb_fifo_push( job->fifo_sync, buf_tmp );
+
break;
}
}
**********************************************************************/
static void SyncAudio( hb_work_object_t * w, int i )
{
+ hb_work_private_t * pv = w->private_data;
hb_job_t * job;
hb_audio_t * audio;
hb_buffer_t * buf;
int64_t pts_expected;
int64_t start;
- job = w->job;
- sync = &w->sync_audio[i];
+ job = pv->job;
+ sync = &pv->sync_audio[i];
audio = sync->audio;
if( job->acodec & HB_ACODEC_AC3 )
( buf = hb_fifo_see( audio->fifo_raw ) ) )
{
/* The PTS of the samples we are expecting now */
- pts_expected = w->pts_offset + sync->count_frames * 90000 / rate;
+ pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
- if( ( buf->start > pts_expected + 45000 ||
- buf->start < pts_expected - 45000 ) &&
- w->pts_offset_old > INT64_MIN )
+ if( ( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
+ buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE ) &&
+ pv->pts_offset_old > INT64_MIN )
{
/* There has been a PTS discontinuity, and this frame might
be from before the discontinuity */
- pts_expected = w->pts_offset_old + sync->count_frames *
+ pts_expected = pv->pts_offset_old + sync->count_frames *
90000 / rate;
- if( buf->start > pts_expected + 45000 ||
- buf->start < pts_expected - 45000 )
+ if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
+ buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
{
/* There is really nothing we can do with it */
buf = hb_fifo_get( audio->fifo_raw );
}
/* Use the older offset */
- start = pts_expected - w->pts_offset_old;
+ start = pts_expected - pv->pts_offset_old;
}
else
{
- start = pts_expected - w->pts_offset;
+ start = pts_expected - pv->pts_offset;
}
- if( ( buf->start + buf->stop ) / 2 < pts_expected )
+ /* Tolerance: 100 ms */
+ if( buf->start < pts_expected - 9000 )
{
/* Late audio, trash it */
+ hb_log( "sync: trashing late audio" );
buf = hb_fifo_get( audio->fifo_raw );
hb_buffer_close( &buf );
continue;
}
-
- if( buf->start > pts_expected + ( buf->stop - buf->start ) / 2 )
+ else if( buf->start > pts_expected + 9000 )
{
- /* Audio push, send a frame of silence */
+ /* Missing audio, send a frame of silence */
InsertSilence( w, i );
continue;
}
int count_in, count_out;
- count_in = buf_raw->size / 2 / sizeof( float );
- count_out = ( buf->stop - pts_expected ) * job->arate / 90000;
+ count_in = buf_raw->size / HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) / sizeof( float );
+ count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000;
+ if( buf->start < pts_expected - 1500 )
+ count_out--;
+ else if( buf->start > pts_expected + 1500 )
+ count_out++;
sync->data.data_in = (float *) buf_raw->data;
sync->data.input_frames = count_in;
-
- if( buf->start < pts_expected - ( buf->stop - buf->start ) / 5 )
- {
- /* Avoid too heavy downsampling, trash the beginning of
- the buffer instead */
- int drop;
- drop = count_in * ( pts_expected - buf->start ) /
- ( buf->stop - buf->start );
- sync->data.data_in += 2 * drop;
- sync->data.input_frames -= drop;
- hb_log( "dropping %d of %d samples", drop, count_in );
- }
-
sync->data.output_frames = count_out;
+
sync->data.src_ratio = (double) sync->data.output_frames /
(double) sync->data.input_frames;
- buf = hb_buffer_init( sync->data.output_frames * 2 *
+ buf = hb_buffer_init( sync->data.output_frames * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) *
sizeof( float ) );
sync->data.data_out = (float *) buf->data;
if( src_process( sync->state, &sync->data ) )
}
hb_buffer_close( &buf_raw );
- buf->size = sync->data.output_frames_gen * 2 * sizeof( float );
+ buf->size = sync->data.output_frames_gen * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) * sizeof( float );
/* Set dates for resampled data */
buf->start = start;
static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
{
- hb_job_t * job = w->job;
+ hb_work_private_t * pv = w->private_data;
+ hb_job_t * job = pv->job;
if( hb_fifo_size( audio->fifo_in ) ||
hb_fifo_size( audio->fifo_raw ) ||
static void InsertSilence( hb_work_object_t * w, int i )
{
+ hb_work_private_t * pv = w->private_data;
hb_job_t * job;
hb_sync_audio_t * sync;
hb_buffer_t * buf;
- job = w->job;
- sync = &w->sync_audio[i];
+ job = pv->job;
+ sync = &pv->sync_audio[i];
if( job->acodec & HB_ACODEC_AC3 )
{
}
else
{
- buf = hb_buffer_init( 2 * job->arate / 20 *
+ buf = hb_buffer_init( HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown) * job->arate / 20 *
sizeof( float ) );
buf->start = sync->count_frames * 90000 / job->arate;
buf->stop = buf->start + 90000 / 20;
static void UpdateState( hb_work_object_t * w )
{
+ hb_work_private_t * pv = w->private_data;
hb_state_t state;
- if( !w->count_frames )
+ if( !pv->count_frames )
{
- w->st_first = hb_get_date();
+ pv->st_first = hb_get_date();
}
- w->count_frames++;
+ pv->count_frames++;
- if( hb_get_date() > w->st_dates[3] + 1000 )
+ if( hb_get_date() > pv->st_dates[3] + 1000 )
{
- memmove( &w->st_dates[0], &w->st_dates[1],
+ memmove( &pv->st_dates[0], &pv->st_dates[1],
3 * sizeof( uint64_t ) );
- memmove( &w->st_counts[0], &w->st_counts[1],
+ memmove( &pv->st_counts[0], &pv->st_counts[1],
3 * sizeof( uint64_t ) );
- w->st_dates[3] = hb_get_date();
- w->st_counts[3] = w->count_frames;
+ pv->st_dates[3] = hb_get_date();
+ pv->st_counts[3] = pv->count_frames;
}
#define p state.param.working
state.state = HB_STATE_WORKING;
- p.progress = (float) w->count_frames / (float) w->count_frames_max;
+ p.progress = (float) pv->count_frames / (float) pv->count_frames_max;
if( p.progress > 1.0 )
{
p.progress = 1.0;
}
p.rate_cur = 1000.0 *
- (float) ( w->st_counts[3] - w->st_counts[0] ) /
- (float) ( w->st_dates[3] - w->st_dates[0] );
- if( hb_get_date() > w->st_first + 4000 )
+ (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 )
{
int eta;
- p.rate_avg = 1000.0 * (float) w->st_counts[3] /
- (float) ( w->st_dates[3] - w->st_first );
- eta = (float) ( w->count_frames_max - w->st_counts[3] ) /
+ p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
+ (float) ( pv->st_dates[3] - pv->st_first );
+ eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
p.rate_avg;
p.hours = eta / 3600;
p.minutes = ( eta % 3600 ) / 60;
}
#undef p
- hb_set_state( w->job->h, &state );
+ hb_set_state( pv->job->h, &state );
}