+ }
+ } // end subtitles
+
+ /*
+ * Adjust the pts of the current frame so that it's contiguous
+ * with the previous frame. The start time of the current frame
+ * has to be the end time of the previous frame and the stop
+ * time has to be the start of the next frame. We don't
+ * make any adjustments to the source timestamps other than removing
+ * the clock offsets (which also removes pts discontinuities).
+ * This means we automatically encode at the source's frame rate.
+ * MP2 uses an implicit duration (frames end when the next frame
+ * starts) but more advanced containers like MP4 use an explicit
+ * duration. Since we're looking ahead one frame we set the
+ * explicit stop time from the start time of the next frame.
+ */
+ *buf_out = cur;
+ sync->cur = cur = next;
+ cur->sub = NULL;
+ sync->next_pts = cur->start;
+ int64_t duration = cur->start - sync->pts_skip - (*buf_out)->start;
+ sync->pts_skip = 0;
+ if ( duration <= 0 )
+ {
+ hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
+ duration, (*buf_out)->start, next->start );
+ }
+
+ (*buf_out)->start = sync->next_start;
+ sync->next_start += duration;
+ (*buf_out)->stop = sync->next_start;
+
+ if ( sync->chap_mark )
+ {
+ // we have a pending chapter mark from a recent drop - put it on this
+ // buffer (this may make it one frame late but we can't do any better).
+ (*buf_out)->new_chap = sync->chap_mark;
+ sync->chap_mark = 0;
+ }
+
+ /* Update UI */
+ UpdateState( w );
+
+ return HB_WORK_OK;
+}
+
+// sync*Init does nothing because sync has a special initializer
+// that takes care of initializing video and all audio tracks
+int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
+{
+ return 0;
+}
+
+hb_work_object_t hb_sync_video =
+{
+ WORK_SYNC_VIDEO,
+ "Video Synchronization",
+ syncVideoInit,
+ syncVideoWork,
+ syncVideoClose
+};
+
+/***********************************************************************
+ * Close Audio
+ ***********************************************************************
+ *
+ **********************************************************************/
+void syncAudioClose( hb_work_object_t * w )
+{
+ hb_work_private_t * pv = w->private_data;
+ hb_sync_audio_t * sync = &pv->type.audio;
+
+ if( w->audio->config.out.codec == HB_ACODEC_AC3 )
+ {
+ free( sync->ac3_buf );
+ }
+ else
+ {
+ src_delete( sync->state );
+ }
+
+ hb_lock( pv->common->mutex );
+ if ( --pv->common->ref == 0 )
+ {
+ hb_unlock( pv->common->mutex );
+ hb_lock_close( &pv->common->mutex );
+ free( pv->common );
+ }
+ else
+ {
+ hb_unlock( pv->common->mutex );
+ }
+
+ free( pv );
+ w->private_data = NULL;
+}
+
+int syncAudioInit( hb_work_object_t * w, hb_job_t * job)
+{
+ return 0;
+}
+
+/***********************************************************************
+ * SyncAudio
+ ***********************************************************************
+ *
+ **********************************************************************/
+static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out )
+{
+ hb_work_private_t * pv = w->private_data;
+ hb_job_t * job = pv->job;
+ hb_sync_audio_t * sync = &pv->type.audio;
+ hb_buffer_t * buf;
+ 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 );
+ return HB_WORK_DONE;
+ }
+
+ /* Wait for start frame if doing point-to-point */
+ hb_lock( pv->common->mutex );
+ 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 );
+ }
+ }
+ if ( buf->start < pv->common->audio_pts_thresh )
+ {
+ hb_buffer_close( &buf );
+ hb_unlock( pv->common->mutex );
+ return HB_WORK_OK;
+ }
+ hb_unlock( pv->common->mutex );
+
+ /* 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 );
+ }