OSDN Git Service

fix a couple A/V/Sub sync issues
[handbrake-jp/handbrake-jp-git.git] / libhb / sync.c
1 /* $Id: sync.c,v 1.38 2005/04/14 21:57:58 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "hb.h"
8 #include "hbffmpeg.h"
9 #include <stdio.h>
10 #include "samplerate.h"
11
12 #ifdef INT64_MIN
13 #undef INT64_MIN /* Because it isn't defined correctly in Zeta */
14 #endif
15 #define INT64_MIN (-9223372036854775807LL-1)
16
17 #define AC3_SAMPLES_PER_FRAME 1536
18
19 typedef struct
20 {
21     hb_lock_t * mutex;
22     int         ref;        /* Reference count to tell us when it's unused */
23     int         count_frames;
24     int64_t     audio_pts_slip;
25     int64_t     video_pts_slip;
26     int64_t     pts_offset;
27
28     /* Frame based point-to-point support */
29     int64_t     audio_pts_thresh;
30     int         start_found;
31     hb_cond_t * next_frame;
32     int         pts_count;
33     int64_t   * first_pts;
34 } hb_sync_common_t;
35
36 typedef struct
37 {
38     int          index;
39     double       next_start;   /* start time of next output frame */
40     int64_t      first_drop;   /* PTS of first 'went backwards' frame dropped */
41     int          drop_count;   /* count of 'time went backwards' drops */
42
43     /* Raw */
44     SRC_STATE  * state;
45     SRC_DATA     data;
46
47     /* AC-3 */
48     int          ac3_size;
49     uint8_t    * ac3_buf;
50 } hb_sync_audio_t;
51
52 typedef struct
53 {
54     /* Video */
55     int        first_frame;
56     int64_t    pts_skip;
57     int64_t    next_start;    /* start time of next output frame */
58     int64_t    first_drop;    /* PTS of first 'went backwards' frame dropped */
59     int        drop_count;    /* count of 'time went backwards' drops */
60     int        drops;         /* frames dropped to make a cbr video stream */
61     int        dups;          /* frames duplicated to make a cbr video stream */
62     int        video_sequence;
63     int        count_frames_max;
64     int        chap_mark;     /* to propagate chapter mark across a drop */
65     hb_buffer_t * cur;        /* The next picture to process */
66
67     /* Statistics */
68     uint64_t   st_counts[4];
69     uint64_t   st_dates[4];
70     uint64_t   st_first;
71 } hb_sync_video_t;
72
73 struct hb_work_private_s
74 {
75     hb_job_t * job;
76     hb_sync_common_t * common;
77     union
78     {
79         hb_sync_video_t video;
80         hb_sync_audio_t audio;
81     } type;
82 };
83
84 /***********************************************************************
85  * Local prototypes
86  **********************************************************************/
87 static void getPtsOffset( hb_work_object_t * w );
88 static int  checkPtsOffset( hb_work_object_t * w );
89 static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i );
90 static void InsertSilence( hb_work_object_t * w, int64_t d );
91 static void UpdateState( hb_work_object_t * w );
92 static void UpdateSearchState( hb_work_object_t * w, int64_t start );
93 static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
94                                        hb_sync_audio_t *sync );
95
96 /***********************************************************************
97  * hb_work_sync_init
98  ***********************************************************************
99  * Initialize the work object
100  **********************************************************************/
101 hb_work_object_t * hb_sync_init( hb_job_t * job )
102 {
103     hb_title_t        * title = job->title;
104     hb_chapter_t      * chapter;
105     int                 i;
106     uint64_t            duration;
107     hb_work_private_t * pv;
108     hb_sync_video_t   * sync;
109     hb_work_object_t  * w;
110     hb_work_object_t  * ret = NULL;
111
112     pv = calloc( 1, sizeof( hb_work_private_t ) );
113     sync = &pv->type.video;
114     pv->common = calloc( 1, sizeof( hb_sync_common_t ) );
115     pv->common->ref++;
116     pv->common->mutex = hb_lock_init();
117     pv->common->audio_pts_thresh = -1;
118     pv->common->next_frame = hb_cond_init();
119     pv->common->pts_count = 1;
120     if ( job->frame_to_start || job->pts_to_start )
121     {
122         pv->common->start_found = 0;
123     }
124     else
125     {
126         pv->common->start_found = 1;
127     }
128
129     ret = w = hb_get_work( WORK_SYNC_VIDEO );
130     w->private_data = pv;
131     w->fifo_in = job->fifo_raw;
132     w->fifo_out = job->fifo_sync;
133
134     pv->job            = job;
135     pv->common->pts_offset   = INT64_MIN;
136     sync->first_frame = 1;
137
138     if( job->pass == 2 )
139     {
140         /* We already have an accurate frame count from pass 1 */
141         hb_interjob_t * interjob = hb_interjob_get( job->h );
142         sync->count_frames_max = interjob->frame_count;
143     }
144     else
145     {
146         /* Calculate how many video frames we are expecting */
147         if ( job->pts_to_stop )
148         {
149             duration = job->pts_to_stop + 90000;
150         }
151         else if( job->frame_to_stop )
152         {
153             /* Set the duration to a rough estimate */
154             duration = ( job->frame_to_stop / ( title->rate / title->rate_base ) ) * 90000;
155         }
156         else
157         {
158             duration = 0;
159             for( i = job->chapter_start; i <= job->chapter_end; i++ )
160             {
161                 chapter   = hb_list_item( title->list_chapter, i - 1 );
162                 duration += chapter->duration;
163             }
164             duration += 90000;
165             /* 1 second safety so we're sure we won't miss anything */
166         }
167         sync->count_frames_max = duration * title->rate / title->rate_base / 90000;
168     }
169
170     hb_log( "sync: expecting %d video frames", sync->count_frames_max );
171
172     /* Initialize libsamplerate for every audio track we have */
173     if ( ! job->indepth_scan )
174     {
175         for( i = 0; i < hb_list_count( title->list_audio ); i++ )
176         {
177             InitAudio( job, pv->common, i );
178         }
179     }
180     pv->common->first_pts = malloc( sizeof(int64_t) * pv->common->pts_count );
181     for ( i = 0; i < pv->common->pts_count; i++ )
182         pv->common->first_pts[i] = INT64_MAX;
183
184     return ret;
185 }
186
187 /***********************************************************************
188  * Close Video
189  ***********************************************************************
190  *
191  **********************************************************************/
192 void syncVideoClose( hb_work_object_t * w )
193 {
194     hb_work_private_t * pv = w->private_data;
195     hb_job_t          * job   = pv->job;
196     hb_sync_video_t   * sync = &pv->type.video;
197
198     // Wake up audio sync if it's still waiting on condition.
199     pv->common->pts_offset = 0;
200     pv->common->start_found = 1;
201     hb_cond_broadcast( pv->common->next_frame );
202
203     if( sync->cur )
204     {
205         hb_buffer_close( &sync->cur );
206     }
207
208     hb_log( "sync: got %d frames, %d expected",
209             pv->common->count_frames, sync->count_frames_max );
210
211     /* save data for second pass */
212     if( job->pass == 1 )
213     {
214         /* Preserve frame count for better accuracy in pass 2 */
215         hb_interjob_t * interjob = hb_interjob_get( job->h );
216         interjob->frame_count = pv->common->count_frames;
217         interjob->last_job = job->sequence_id;
218         interjob->total_time = sync->next_start;
219     }
220
221     if (sync->drops || sync->dups )
222     {
223         hb_log( "sync: %d frames dropped, %d duplicated", 
224                 sync->drops, sync->dups );
225     }
226
227     hb_lock( pv->common->mutex );
228     if ( --pv->common->ref == 0 )
229     {
230         hb_unlock( pv->common->mutex );
231         hb_lock_close( &pv->common->mutex );
232         free( pv->common );
233     }
234     else
235     {
236         hb_unlock( pv->common->mutex );
237     }
238
239     free( pv );
240     w->private_data = NULL;
241 }
242
243 /***********************************************************************
244  * syncVideoWork
245  ***********************************************************************
246  *
247  **********************************************************************/
248 static hb_buffer_t * copy_subtitle( hb_buffer_t * src );
249
250 int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
251               hb_buffer_t ** buf_out )
252 {
253     hb_buffer_t * cur, * next, * sub = NULL;
254     hb_work_private_t * pv = w->private_data;
255     hb_job_t          * job = pv->job;
256     hb_subtitle_t     * subtitle;
257     hb_sync_video_t   * sync = &pv->type.video;
258     int i;
259     int64_t start, next_start;
260
261     *buf_out = NULL;
262     next = *buf_in;
263     *buf_in = NULL;
264
265     /* Wait till we can determine the initial pts of all streams */
266     if( next->size != 0 && pv->common->pts_offset == INT64_MIN )
267     {
268         pv->common->first_pts[0] = next->start;
269         hb_lock( pv->common->mutex );
270         while( pv->common->pts_offset == INT64_MIN )
271         {
272             // Full fifos will make us wait forever, so get the
273             // pts offset from the available streams if full
274             if ( hb_fifo_is_full( job->fifo_raw ) )
275             {
276                 getPtsOffset( w );
277                 hb_cond_broadcast( pv->common->next_frame );
278             }
279             else if ( checkPtsOffset( w ) )
280                 hb_cond_broadcast( pv->common->next_frame );
281             else
282                 hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
283         }
284         hb_unlock( pv->common->mutex );
285     }
286
287     hb_lock( pv->common->mutex );
288     next_start = next->start - pv->common->video_pts_slip;
289     hb_unlock( pv->common->mutex );
290
291     /* Wait for start of point-to-point encoding */
292     if( !pv->common->start_found )
293     {
294         hb_sync_video_t   * sync = &pv->type.video;
295
296         if( next->size == 0 )
297         {
298             *buf_out = next;
299             pv->common->start_found = 1;
300             hb_cond_broadcast( pv->common->next_frame );
301
302             /*
303              * Push through any subtitle EOFs in case they 
304              * were not synced through.
305              */
306             for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
307             {
308                 subtitle = hb_list_item( job->list_subtitle, i );
309                 if( subtitle->config.dest == PASSTHRUSUB )
310                 {
311                     if( subtitle->source == VOBSUB ) 
312                         hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
313                     else
314                         hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
315                 }
316             }
317             return HB_WORK_DONE;
318         }
319         if ( pv->common->count_frames < job->frame_to_start ||
320              next_start < job->pts_to_start )
321         {
322             // Flush any subtitles that have pts prior to the
323             // current frame
324             for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
325             {
326                 subtitle = hb_list_item( job->list_subtitle, i );
327                 while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
328                 {
329                     if ( sub->start > next->start )
330                         break;
331                     sub = hb_fifo_get( subtitle->fifo_raw );
332                     hb_buffer_close( &sub );
333                 }
334             }
335             hb_lock( pv->common->mutex );
336             // Tell the audio threads what must be dropped
337             pv->common->audio_pts_thresh = next_start;
338             hb_cond_broadcast( pv->common->next_frame );
339             hb_unlock( pv->common->mutex );
340
341             UpdateSearchState( w, next->start );
342             hb_buffer_close( &next );
343
344             return HB_WORK_OK;
345         }
346         hb_lock( pv->common->mutex );
347         pv->common->audio_pts_thresh = 0;
348         pv->common->audio_pts_slip = next_start;
349         pv->common->video_pts_slip = next_start;
350         next_start = 0;
351         pv->common->start_found = 1;
352         pv->common->count_frames = 0;
353         hb_cond_broadcast( pv->common->next_frame );
354         hb_unlock( pv->common->mutex );
355         sync->st_first = 0;
356     }
357
358     if( !sync->cur )
359     {
360         sync->cur = next;
361         if( sync->cur->size == 0 )
362         {
363             /* we got an end-of-stream as our first video packet? 
364              * Feed it downstream & signal that we're done. 
365              */
366             *buf_out = hb_buffer_init( 0 );
367
368             pv->common->start_found = 1;
369             hb_cond_broadcast( pv->common->next_frame );
370
371             /*
372              * Push through any subtitle EOFs in case they 
373              * were not synced through.
374              */
375             for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
376             {
377                 subtitle = hb_list_item( job->list_subtitle, i );
378                 if( subtitle->config.dest == PASSTHRUSUB )
379                 {
380                     if( subtitle->source == VOBSUB ) 
381                         hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
382                     else
383                         hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
384                 }
385             }
386             return HB_WORK_DONE;
387         }
388         return HB_WORK_OK;
389     }
390     cur = sync->cur;
391     /* At this point we have a frame to process. Let's check
392         1) if we will be able to push into the fifo ahead
393         2) if the next frame is there already, since we need it to
394            compute the duration of the current frame*/
395     if( next->size == 0 )
396     {
397         hb_buffer_close( &next );
398
399         cur->start = sync->next_start;
400         cur->stop = cur->start + 90000. / ((double)job->vrate / (double)job->vrate_base);
401
402         /* Make sure last frame is reflected in frame count */
403         pv->common->count_frames++;
404
405         /* Push the frame to the renderer */
406         hb_fifo_push( job->fifo_sync, cur );
407         sync->cur = NULL;
408
409         /* we got an end-of-stream. Feed it downstream & signal that
410          * we're done. Note that this means we drop the final frame of
411          * video (we don't know its duration). On DVDs the final frame
412          * is often strange and dropping it seems to be a good idea. */
413         *buf_out = hb_buffer_init( 0 );
414
415         /*
416          * Push through any subtitle EOFs in case they were not synced through.
417          */
418         for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
419         {
420             subtitle = hb_list_item( job->list_subtitle, i );
421             if( subtitle->config.dest == PASSTHRUSUB )
422             {
423                 if( subtitle->source == VOBSUB ) 
424                     hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
425                 else
426                     hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
427             }
428         }
429         pv->common->start_found = 1;
430         hb_cond_broadcast( pv->common->next_frame );
431         return HB_WORK_DONE;
432     }
433
434     /* Check for end of point-to-point frame encoding */
435     if( job->frame_to_stop && pv->common->count_frames > job->frame_to_stop )
436     {
437         // Drop an empty buffer into our output to ensure that things
438         // get flushed all the way out.
439         hb_buffer_close( &sync->cur );
440         hb_buffer_close( &next );
441         *buf_out = hb_buffer_init( 0 );
442         hb_log( "sync: reached %d frames, exiting early",
443                 pv->common->count_frames );
444
445         /*
446          * Push through any subtitle EOFs in case they were not synced through.
447          */
448         for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
449         {
450             subtitle = hb_list_item( job->list_subtitle, i );
451             if( subtitle->config.dest == PASSTHRUSUB )
452             {
453                 if( subtitle->source == VOBSUB ) 
454                     hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
455                 else
456                     hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
457             }
458         }
459         return HB_WORK_DONE;
460     }
461
462     hb_lock( pv->common->mutex );
463     start = cur->start - pv->common->video_pts_slip;
464     hb_unlock( pv->common->mutex );
465
466     /* Check for end of point-to-point pts encoding */
467     if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
468     {
469         // Drop an empty buffer into our output to ensure that things
470         // get flushed all the way out.
471         hb_log( "sync: reached pts %"PRId64", exiting early", start );
472         hb_buffer_close( &sync->cur );
473         hb_buffer_close( &next );
474         *buf_out = hb_buffer_init( 0 );
475
476         /*
477          * Push through any subtitle EOFs in case they were not synced through.
478          */
479         for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
480         {
481             subtitle = hb_list_item( job->list_subtitle, i );
482             if( subtitle->config.dest == PASSTHRUSUB )
483             {
484                 if( subtitle->source == VOBSUB ) 
485                     hb_fifo_push( subtitle->fifo_sync, hb_buffer_init( 0 ) );
486                 else
487                     hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) );
488             }
489         }
490         return HB_WORK_DONE;
491     }
492
493     if( sync->first_frame )
494     {
495         /* This is our first frame */
496         if ( start > 0 )
497         {
498             /*
499              * The first pts from a dvd should always be zero but
500              * can be non-zero with a transport or program stream since
501              * we're not guaranteed to start on an IDR frame. If we get
502              * a non-zero initial PTS extend its duration so it behaves
503              * as if it started at zero so that our audio timing will
504              * be in sync.
505              */
506             hb_log( "sync: first pts is %"PRId64, start );
507             start = 0;
508         }
509         sync->first_frame = 0;
510     }
511
512     /*
513      * since the first frame is always 0 and the upstream reader code
514      * is taking care of adjusting for pts discontinuities, we just have
515      * to deal with the next frame's start being in the past. This can
516      * happen when the PTS is adjusted after data loss but video frame
517      * reordering causes some frames with the old clock to appear after
518      * the clock change. This creates frames that overlap in time which
519      * looks to us like time going backward. The downstream muxing code
520      * can deal with overlaps of up to a frame time but anything larger
521      * we handle by dropping frames here.
522      */
523     if ( next_start - start <= 0 )
524     {
525         if ( sync->first_drop == 0 )
526         {
527             sync->first_drop = next_start;
528         }
529         ++sync->drop_count;
530         if ( next->new_chap )
531         {
532             // don't drop a chapter mark when we drop the buffer
533             sync->chap_mark = next->new_chap;
534         }
535         hb_buffer_close( &next );
536         return HB_WORK_OK;
537     }
538     if ( sync->first_drop )
539     {
540         hb_log( "sync: video time didn't advance - dropped %d frames "
541                 "(delta %d ms, current %"PRId64", next %"PRId64", dur %d)",
542                 sync->drop_count, (int)( start - sync->first_drop ) / 90,
543                 start, next_start, (int)( next_start - start ) );
544         sync->first_drop = 0;
545         sync->drop_count = 0;
546     }
547
548     /*
549      * Track the video sequence number localy so that we can sync the audio
550      * to it using the sequence number as well as the PTS.
551      */
552     sync->video_sequence = cur->sequence;
553
554     /*
555      * Look for a subtitle for this frame.
556      *
557      * If found then it will be tagged onto a video buffer of the correct time and 
558      * sent in to the render pipeline. This only needs to be done for VOBSUBs which
559      * get rendered, other types of subtitles can just sit in their raw_queue until
560      * delt with at muxing.
561      */
562     for( i = 0; i < hb_list_count( job->list_subtitle ); i++)
563     {
564         int64_t sub_start, sub_stop, duration;
565         subtitle = hb_list_item( job->list_subtitle, i );
566
567         /*
568          * Rewrite timestamps on subtitles that need it (on raw queue).
569          */
570         // NOTE: It's probably fine to use this logic for passthru VOBSUBs as well,
571         //       but I am currently preserving backwards compatibility with the old
572         //       VOBSUB behavior, which uses the more complex logic following this if-statement.
573         if( subtitle->config.dest == PASSTHRUSUB && subtitle->source != VOBSUB )
574         {
575             /*
576              * Rewrite timestamps on subtitles that came from Closed Captions
577              * since they are using the MPEG2 timestamps.
578              */
579             while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
580             {
581                 hb_lock( pv->common->mutex );
582                 sub_start = sub->start - pv->common->video_pts_slip;
583                 hb_unlock( pv->common->mutex );
584                 duration = sub->stop - sub->start;
585                 sub_stop = sub_start + duration;
586
587                 /*
588                  * Rewrite the timestamps as and when the video
589                  * (cur->start) reaches the same timestamp as a
590                  * closed caption (sub->start).
591                  *
592                  * What about discontinuity boundaries - not delt
593                  * with here - Van?
594                  *
595                  * Bypass the sync fifo altogether.
596                  */
597                 if( sub->size <= 0 )
598                 {
599                     sub = hb_fifo_get( subtitle->fifo_raw );
600                     hb_fifo_push( subtitle->fifo_out, sub );
601                     sub = NULL;
602                     break;
603                 } else {
604                     /*
605                      * Sync the subtitles to the incoming video, and use
606                      * the matching converted video timestamp.
607                      *
608                      * Note that it doesn't appear that we need to convert 
609                      * timestamps, I guess that they were already correct,
610                      * so just push them through for rendering.
611                      *
612                      */
613                     if( sub_start < start )
614                     {
615                         sub = hb_fifo_get( subtitle->fifo_raw );
616                         sub->start = sub_start;
617                         sub->stop = sub_stop;
618                         hb_fifo_push( subtitle->fifo_out, sub );
619                     } else {
620                         sub = NULL;
621                         break;
622                     }
623                 }
624             }
625             
626             continue;
627         }
628
629         // For rendered subtitles (and, for backward compatibility, passthru VOBSUBs),
630         // delay pushing subtitle packets through the pipeline until the video catches up
631         if( subtitle->config.dest == RENDERSUB || subtitle->source == VOBSUB ) 
632         {
633             hb_buffer_t * sub2;
634             while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
635             {
636                 if( sub->size == 0 )
637                 {
638                     /*
639                      * EOF, pass it through immediately.
640                      */
641                     break;
642                 }
643
644                 hb_lock( pv->common->mutex );
645                 sub_start = sub->start - pv->common->video_pts_slip;
646                 hb_unlock( pv->common->mutex );
647                 duration = sub->stop - sub->start;
648                 sub_stop = sub_start + duration;
649
650                 /* If two subtitles overlap, make the first one stop
651                    when the second one starts */
652                 sub2 = hb_fifo_see2( subtitle->fifo_raw );
653                 if( sub2 && sub->stop > sub2->start )
654                 {
655                     sub->stop = sub2->start;
656                 }
657                 
658                 // hb_log("0x%x: video seq: %"PRId64" subtitle sequence: %"PRId64,
659                 //       sub, cur->sequence, sub->sequence);
660                 
661                 if( sub->sequence > cur->sequence )
662                 {
663                     /*
664                      * The video is behind where we are, so wait until
665                      * it catches up to the same reader point on the
666                      * DVD. Then our PTS should be in the same region
667                      * as the video.
668                      */
669                     sub = NULL;
670                     break;
671                 }
672                 
673                 if( sub_stop > start ) 
674                 {
675                     /*
676                      * The stop time is in the future, so fall through
677                      * and we'll deal with it in the next block of
678                      * code.
679                      */
680
681                     /*
682                      * There is a valid subtitle, is it time to display it?
683                      */
684                     if( sub_stop > sub_start)
685                     {
686                         /*
687                          * Normal subtitle which ends after it starts, 
688                          * check to see that the current video is between 
689                          * the start and end.
690                          */
691                         if( start > sub_start &&
692                             start < sub_stop )
693                         {
694                             /*
695                             * We should be playing this, so leave the
696                             * subtitle in place.
697                             *
698                             * fall through to display
699                             */
700                         }
701                         else
702                         {
703                             /*
704                              * Defer until the play point is within 
705                              * the subtitle
706                              */
707                             sub = NULL;
708                         }
709                     }
710                     else
711                     {
712                         /*
713                          * The end of the subtitle is less than the start, 
714                          * this is a sign of a PTS discontinuity.
715                          */
716                         if( sub_start > start )
717                         {
718                             /*
719                              * we haven't reached the start time yet, or
720                              * we have jumped backwards after having
721                              * already started this subtitle.
722                              */
723                             if( start < sub_stop )
724                             {
725                                 /*
726                                  * We have jumped backwards and so should
727                                  * continue displaying this subtitle.
728                                  *
729                                  * fall through to display.
730                                  */
731                             }
732                             else
733                             {
734                                 /*
735                                  * Defer until the play point is 
736                                  * within the subtitle
737                                  */
738                                 sub = NULL;
739                             }
740                         } else {
741                             /*
742                             * Play this subtitle as the start is 
743                             * greater than our video point.
744                             *
745                             * fall through to display/
746                             */
747                         }
748                     }
749                         break;
750                 }
751                 else
752                 {
753                     
754                     /*
755                      * The subtitle is older than this picture, trash it
756                      */
757                     sub = hb_fifo_get( subtitle->fifo_raw );
758                     hb_buffer_close( &sub );
759                 }
760             }
761             
762             /* If we have a subtitle for this picture, copy it */
763             if( sub )
764             {
765                 if( sub->size > 0 )
766                 {
767                     if( subtitle->config.dest == RENDERSUB )
768                     {
769                         // Only allow one subtitle to be showing at once; ignore others
770                         if ( cur->sub == NULL )
771                         {
772                             /*
773                              * Tack onto the video buffer for rendering
774                              */
775                             /* FIXME: we should avoid this memcpy */
776                             cur->sub = copy_subtitle( sub );
777                             cur->sub->start = sub_start;
778                             cur->sub->stop = sub_stop;
779                             
780                             // Leave the subtitle on the raw queue
781                             // (until it no longer needs to be displayed)
782                         }
783                     } else {
784                         /*
785                          * Pass-Through, pop it off of the raw queue, 
786                          */
787                         sub = hb_fifo_get( subtitle->fifo_raw );
788                         sub->start = sub_start;
789                         sub->stop = sub_stop;
790                         hb_fifo_push( subtitle->fifo_sync, sub );
791                     }
792                 } else {
793                     /*
794                     * EOF - consume for rendered, else pass through
795                     */
796                     if( subtitle->config.dest == RENDERSUB )
797                     {
798                         sub = hb_fifo_get( subtitle->fifo_raw );
799                         hb_buffer_close( &sub );
800                     } else {
801                         sub = hb_fifo_get( subtitle->fifo_raw );
802                         sub->start = sub_start;
803                         sub->stop = sub_stop;
804                         hb_fifo_push( subtitle->fifo_sync, sub );
805                     }
806                 }
807             }
808         }
809     } // end subtitles
810
811     /*
812      * Adjust the pts of the current frame so that it's contiguous
813      * with the previous frame. The start time of the current frame
814      * has to be the end time of the previous frame and the stop
815      * time has to be the start of the next frame.  We don't
816      * make any adjustments to the source timestamps other than removing
817      * the clock offsets (which also removes pts discontinuities).
818      * This means we automatically encode at the source's frame rate.
819      * MP2 uses an implicit duration (frames end when the next frame
820      * starts) but more advanced containers like MP4 use an explicit
821      * duration. Since we're looking ahead one frame we set the
822      * explicit stop time from the start time of the next frame.
823      */
824     *buf_out = cur;
825     sync->cur = cur = next;
826     cur->sub = NULL;
827     int64_t duration = next_start - start;
828     sync->pts_skip = 0;
829     if ( duration <= 0 )
830     {
831         hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
832                 duration, start, next_start );
833     }
834
835     (*buf_out)->start = sync->next_start;
836     sync->next_start += duration;
837     (*buf_out)->stop = sync->next_start;
838
839     if ( sync->chap_mark )
840     {
841         // we have a pending chapter mark from a recent drop - put it on this
842         // buffer (this may make it one frame late but we can't do any better).
843         (*buf_out)->new_chap = sync->chap_mark;
844         sync->chap_mark = 0;
845     }
846
847     /* Update UI */
848     UpdateState( w );
849
850     return HB_WORK_OK;
851 }
852
853 static hb_buffer_t * copy_subtitle( hb_buffer_t * src_list )
854 {
855     hb_buffer_t * dst_list = NULL;
856     
857     hb_buffer_t * src;
858     hb_buffer_t * dst;
859     hb_buffer_t ** dst_ptr = &dst_list;
860     for ( src = src_list, dst_ptr = &dst_list;
861           src;
862           src = src->next_subpicture, dst_ptr = &dst->next_subpicture )
863     {
864         (*dst_ptr)  = hb_buffer_init( src->size );
865         dst         = (*dst_ptr); 
866         dst->x      = src->x;
867         dst->y      = src->y;
868         dst->width  = src->width;
869         dst->height = src->height;
870         memcpy( dst->data, src->data, src->size );
871     }
872     
873     return dst_list;
874 }
875
876 // sync*Init does nothing because sync has a special initializer
877 // that takes care of initializing video and all audio tracks
878 int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
879 {
880     return 0;
881 }
882
883 hb_work_object_t hb_sync_video =
884 {
885     WORK_SYNC_VIDEO,
886     "Video Synchronization",
887     syncVideoInit,
888     syncVideoWork,
889     syncVideoClose
890 };
891
892 /***********************************************************************
893  * Close Audio
894  ***********************************************************************
895  *
896  **********************************************************************/
897 void syncAudioClose( hb_work_object_t * w )
898 {
899     hb_work_private_t * pv    = w->private_data;
900     hb_sync_audio_t   * sync  = &pv->type.audio;
901
902     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
903     {
904         free( sync->ac3_buf );
905     }
906     else
907     {
908         src_delete( sync->state );
909     }
910
911     hb_lock( pv->common->mutex );
912     if ( --pv->common->ref == 0 )
913     {
914         hb_unlock( pv->common->mutex );
915         hb_lock_close( &pv->common->mutex );
916         free( pv->common );
917     }
918     else
919     {
920         hb_unlock( pv->common->mutex );
921     }
922
923     free( pv );
924     w->private_data = NULL;
925 }
926
927 int syncAudioInit( hb_work_object_t * w, hb_job_t * job)
928 {
929     return 0;
930 }
931
932 /***********************************************************************
933  * SyncAudio
934  ***********************************************************************
935  *
936  **********************************************************************/
937 static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
938                        hb_buffer_t ** buf_out )
939 {
940     hb_work_private_t * pv = w->private_data;
941     hb_job_t        * job = pv->job;
942     hb_sync_audio_t * sync = &pv->type.audio;
943     hb_buffer_t     * buf;
944     int64_t start;
945
946     *buf_out = NULL;
947     buf = *buf_in;
948     *buf_in = NULL;
949     /* if the next buffer is an eof send it downstream */
950     if ( buf->size <= 0 )
951     {
952         hb_buffer_close( &buf );
953         *buf_out = hb_buffer_init( 0 );
954         return HB_WORK_DONE;
955     }
956
957     /* Wait till we can determine the initial pts of all streams */
958     if( pv->common->pts_offset == INT64_MIN )
959     {
960         pv->common->first_pts[sync->index+1] = buf->start;
961         hb_lock( pv->common->mutex );
962         while( pv->common->pts_offset == INT64_MIN )
963         {
964             // Full fifos will make us wait forever, so get the
965             // pts offset from the available streams if full
966             if (hb_fifo_is_full(w->fifo_in))
967             {
968                 getPtsOffset( w );
969                 hb_cond_broadcast( pv->common->next_frame );
970             }
971             else if ( checkPtsOffset( w ) )
972                 hb_cond_broadcast( pv->common->next_frame );
973             else
974                 hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
975         }
976         hb_unlock( pv->common->mutex );
977     }
978
979     /* Wait for start frame if doing point-to-point */
980     hb_lock( pv->common->mutex );
981     start = buf->start - pv->common->audio_pts_slip;
982     while ( !pv->common->start_found )
983     {
984         if ( pv->common->audio_pts_thresh < 0 )
985         {
986             // I would initialize this in hb_sync_init, but 
987             // job->pts_to_start can be modified by reader 
988             // after hb_sync_init is called.
989             pv->common->audio_pts_thresh = job->pts_to_start;
990         }
991         if ( start < pv->common->audio_pts_thresh )
992         {
993             hb_buffer_close( &buf );
994             hb_unlock( pv->common->mutex );
995             return HB_WORK_OK;
996         }
997         while ( !pv->common->start_found && 
998                 start >= pv->common->audio_pts_thresh )
999         {
1000             hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
1001         }
1002     }
1003     if ( start < 0 )
1004     {
1005         hb_buffer_close( &buf );
1006         hb_unlock( pv->common->mutex );
1007         return HB_WORK_OK;
1008     }
1009     hb_unlock( pv->common->mutex );
1010
1011     if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop )
1012     {
1013         hb_buffer_close( &buf );
1014         *buf_out = hb_buffer_init( 0 );
1015         return HB_WORK_DONE;
1016     }
1017
1018     if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
1019     {
1020         hb_buffer_close( &buf );
1021         *buf_out = hb_buffer_init( 0 );
1022         return HB_WORK_DONE;
1023     }
1024
1025     if ( start - sync->next_start < 0 )
1026     {
1027         // audio time went backwards.
1028         // If our output clock is more than a half frame ahead of the
1029         // input clock drop this frame to move closer to sync.
1030         // Otherwise drop frames until the input clock matches the output clock.
1031         if ( sync->first_drop || sync->next_start - start > 90*15 )
1032         {
1033             // Discard data that's in the past.
1034             if ( sync->first_drop == 0 )
1035             {
1036                 sync->first_drop = sync->next_start;
1037             }
1038             ++sync->drop_count;
1039             hb_buffer_close( &buf );
1040             return HB_WORK_OK;
1041         }
1042     }
1043     if ( sync->first_drop )
1044     {
1045         // we were dropping old data but input buf time is now current
1046         hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
1047                 "(next %"PRId64", current %"PRId64")", w->audio->id,
1048                 (int)( sync->next_start - sync->first_drop ) / 90,
1049                 sync->drop_count, sync->first_drop, (int64_t)sync->next_start );
1050         sync->first_drop = 0;
1051         sync->drop_count = 0;
1052     }
1053     if ( start - sync->next_start >= (90 * 70) )
1054     {
1055         if ( start - sync->next_start > (90000LL * 60) )
1056         {
1057             // there's a gap of more than a minute between the last
1058             // frame and this. assume we got a corrupted timestamp
1059             // and just drop the next buf.
1060             hb_log( "sync: %d minute time gap in audio %d - dropping buf"
1061                     "  start %"PRId64", next %"PRId64,
1062                     (int)((start - sync->next_start) / (90000*60)),
1063                     w->audio->id, start, (int64_t)sync->next_start );
1064             hb_buffer_close( &buf );
1065             return HB_WORK_OK;
1066         }
1067         /*
1068          * there's a gap of at least 70ms between the last
1069          * frame we processed & the next. Fill it with silence.
1070          * Or in the case of DCA, skip some frames from the
1071          * other streams.
1072          */
1073         if( w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1074         {
1075             hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
1076                     "  start %"PRId64", next %"PRId64,
1077                     (int)((start - sync->next_start) / 90),
1078                     w->audio->id, start, (int64_t)sync->next_start );
1079             hb_lock( pv->common->mutex );
1080             pv->common->audio_pts_slip += (start - sync->next_start);
1081             pv->common->video_pts_slip += (start - sync->next_start);
1082             hb_unlock( pv->common->mutex );
1083             *buf_out = buf;
1084             return HB_WORK_OK;
1085         }
1086         hb_log( "sync: adding %d ms of silence to audio %d"
1087                 "  start %"PRId64", next %"PRId64,
1088                 (int)((start - sync->next_start) / 90),
1089                 w->audio->id, start, (int64_t)sync->next_start );
1090         InsertSilence( w, start - sync->next_start );
1091     }
1092
1093     /*
1094      * When we get here we've taken care of all the dups and gaps in the
1095      * audio stream and are ready to inject the next input frame into
1096      * the output stream.
1097      */
1098     *buf_out = OutputAudioFrame( w->audio, buf, sync );
1099     return HB_WORK_OK;
1100 }
1101
1102 hb_work_object_t hb_sync_audio =
1103 {
1104     WORK_SYNC_AUDIO,
1105     "AudioSynchronization",
1106     syncAudioInit,
1107     syncAudioWork,
1108     syncAudioClose
1109 };
1110
1111 static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
1112 {
1113     hb_work_object_t  * w;
1114     hb_work_private_t * pv;
1115     hb_title_t        * title = job->title;
1116     hb_sync_audio_t   * sync;
1117
1118     pv = calloc( 1, sizeof( hb_work_private_t ) );
1119     sync = &pv->type.audio;
1120     sync->index = i;
1121     pv->job    = job;
1122     pv->common = common;
1123     pv->common->ref++;
1124     pv->common->pts_count++;
1125
1126     w = hb_get_work( WORK_SYNC_AUDIO );
1127     w->private_data = pv;
1128     w->audio = hb_list_item( title->list_audio, i );
1129     w->fifo_in = w->audio->priv.fifo_raw;
1130
1131     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1132         w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1133     {
1134         w->fifo_out = w->audio->priv.fifo_out;
1135     }
1136     else
1137     {
1138         w->fifo_out = w->audio->priv.fifo_sync;
1139     }
1140
1141     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1142     {
1143         /* Have a silent AC-3 frame ready in case we have to fill a
1144            gap */
1145         AVCodec        * codec;
1146         AVCodecContext * c;
1147         short          * zeros;
1148
1149         codec = avcodec_find_encoder( CODEC_ID_AC3 );
1150         c     = avcodec_alloc_context();
1151
1152         c->bit_rate    = w->audio->config.in.bitrate;
1153         c->sample_rate = w->audio->config.in.samplerate;
1154         c->channels    = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( w->audio->config.in.channel_layout );
1155
1156         if( hb_avcodec_open( c, codec ) < 0 )
1157         {
1158             hb_log( "sync: avcodec_open failed" );
1159             return;
1160         }
1161
1162         zeros          = calloc( AC3_SAMPLES_PER_FRAME *
1163                                  sizeof( short ) * c->channels, 1 );
1164         sync->ac3_size = w->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
1165                              w->audio->config.in.samplerate / 8;
1166         sync->ac3_buf  = malloc( sync->ac3_size );
1167
1168         if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
1169                                   zeros ) != sync->ac3_size )
1170         {
1171             hb_log( "sync: avcodec_encode_audio failed" );
1172         }
1173
1174         free( zeros );
1175         hb_avcodec_close( c );
1176         av_free( c );
1177     }
1178     else
1179     {
1180         /* Initialize libsamplerate */
1181         int error;
1182         sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, 
1183             HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1184                 w->audio->config.out.mixdown), &error );
1185         sync->data.end_of_input = 0;
1186     }
1187     hb_list_add( job->list_work, w );
1188 }
1189
1190 static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
1191                                        hb_sync_audio_t *sync )
1192 {
1193     int64_t start = (int64_t)sync->next_start;
1194     double duration = buf->stop - buf->start;
1195
1196     if( audio->config.in.samplerate == audio->config.out.samplerate ||
1197         audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1198         audio->config.out.codec == HB_ACODEC_DCA_PASS )
1199     {
1200         /*
1201          * If we don't have to do sample rate conversion or this audio is 
1202          * pass-thru just send the input buffer downstream after adjusting
1203          * its timestamps to make the output stream continuous.
1204          */
1205     }
1206     else
1207     {
1208         /* Not pass-thru - do sample rate conversion */
1209         int count_in, count_out;
1210         hb_buffer_t * buf_raw = buf;
1211         int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown) *
1212                             sizeof( float );
1213
1214         count_in  = buf_raw->size / channel_count;
1215         /*
1216          * When using stupid rates like 44.1 there will always be some
1217          * truncation error. E.g., a 1536 sample AC3 frame will turn into a
1218          * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2
1219          * the error will build up over time and eventually the audio will
1220          * substantially lag the video. libsamplerate will keep track of the
1221          * fractional sample & give it to us when appropriate if we give it
1222          * an extra sample of space in the output buffer.
1223          */
1224         count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1;
1225
1226         sync->data.input_frames = count_in;
1227         sync->data.output_frames = count_out;
1228         sync->data.src_ratio = (double)audio->config.out.samplerate /
1229                                (double)audio->config.in.samplerate;
1230
1231         buf = hb_buffer_init( count_out * channel_count );
1232         sync->data.data_in  = (float *) buf_raw->data;
1233         sync->data.data_out = (float *) buf->data;
1234         if( src_process( sync->state, &sync->data ) )
1235         {
1236             /* XXX If this happens, we're screwed */
1237             hb_log( "sync: audio %d src_process failed", audio->id );
1238         }
1239         hb_buffer_close( &buf_raw );
1240
1241         buf->size = sync->data.output_frames_gen * channel_count;
1242         duration = (double)( sync->data.output_frames_gen * 90000 ) /
1243                    audio->config.out.samplerate;
1244     }
1245     buf->frametype = HB_FRAME_AUDIO;
1246     buf->start = start;
1247     sync->next_start += duration;
1248     buf->stop  = (int64_t)sync->next_start;
1249     return buf;
1250 }
1251
1252 static void InsertSilence( hb_work_object_t * w, int64_t duration )
1253 {
1254     hb_work_private_t * pv = w->private_data;
1255     hb_sync_audio_t *sync = &pv->type.audio;
1256     hb_buffer_t     *buf;
1257     hb_fifo_t       *fifo;
1258
1259     // to keep pass-thru and regular audio in sync we generate silence in
1260     // AC3 frame-sized units. If the silence duration isn't an integer multiple
1261     // of the AC3 frame duration we will truncate or round up depending on
1262     // which minimizes the timing error.
1263     const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
1264                           w->audio->config.in.samplerate;
1265     int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
1266
1267     while ( --frame_count >= 0 )
1268     {
1269         if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1270         {
1271             buf        = hb_buffer_init( sync->ac3_size );
1272             buf->start = sync->next_start;
1273             buf->stop  = buf->start + frame_dur;
1274             memcpy( buf->data, sync->ac3_buf, buf->size );
1275             fifo = w->audio->priv.fifo_out;
1276         }
1277         else
1278         {
1279             buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
1280                                      HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1281                                          w->audio->config.out.mixdown) );
1282             buf->start = sync->next_start;
1283             buf->stop  = buf->start + frame_dur;
1284             memset( buf->data, 0, buf->size );
1285             fifo = w->audio->priv.fifo_sync;
1286         }
1287         buf = OutputAudioFrame( w->audio, buf, sync );
1288         hb_fifo_push( fifo, buf );
1289     }
1290 }
1291
1292 static void UpdateState( hb_work_object_t * w )
1293 {
1294     hb_work_private_t * pv = w->private_data;
1295     hb_sync_video_t   * sync = &pv->type.video;
1296     hb_state_t state;
1297
1298     if( !pv->common->count_frames )
1299     {
1300         sync->st_first = hb_get_date();
1301         pv->job->st_pause_date = -1;
1302         pv->job->st_paused = 0;
1303     }
1304     pv->common->count_frames++;
1305
1306     if( hb_get_date() > sync->st_dates[3] + 1000 )
1307     {
1308         memmove( &sync->st_dates[0], &sync->st_dates[1],
1309                  3 * sizeof( uint64_t ) );
1310         memmove( &sync->st_counts[0], &sync->st_counts[1],
1311                  3 * sizeof( uint64_t ) );
1312         sync->st_dates[3]  = hb_get_date();
1313         sync->st_counts[3] = pv->common->count_frames;
1314     }
1315
1316 #define p state.param.working
1317     state.state = HB_STATE_WORKING;
1318     p.progress  = (float) pv->common->count_frames / (float) sync->count_frames_max;
1319     if( p.progress > 1.0 )
1320     {
1321         p.progress = 1.0;
1322     }
1323     p.rate_cur   = 1000.0 *
1324         (float) ( sync->st_counts[3] - sync->st_counts[0] ) /
1325         (float) ( sync->st_dates[3] - sync->st_dates[0] );
1326     if( hb_get_date() > sync->st_first + 4000 )
1327     {
1328         int eta;
1329         p.rate_avg = 1000.0 * (float) sync->st_counts[3] /
1330             (float) ( sync->st_dates[3] - sync->st_first - pv->job->st_paused);
1331         eta = (float) ( sync->count_frames_max - sync->st_counts[3] ) /
1332             p.rate_avg;
1333         p.hours   = eta / 3600;
1334         p.minutes = ( eta % 3600 ) / 60;
1335         p.seconds = eta % 60;
1336     }
1337     else
1338     {
1339         p.rate_avg = 0.0;
1340         p.hours    = -1;
1341         p.minutes  = -1;
1342         p.seconds  = -1;
1343     }
1344 #undef p
1345
1346     hb_set_state( pv->job->h, &state );
1347 }
1348
1349 static void UpdateSearchState( hb_work_object_t * w, int64_t start )
1350 {
1351     hb_work_private_t * pv = w->private_data;
1352     hb_sync_video_t   * sync = &pv->type.video;
1353     hb_state_t state;
1354     uint64_t now;
1355     double avg;
1356
1357     now = hb_get_date();
1358     if( !pv->common->count_frames )
1359     {
1360         sync->st_first = now;
1361         pv->job->st_pause_date = -1;
1362         pv->job->st_paused = 0;
1363     }
1364     pv->common->count_frames++;
1365
1366 #define p state.param.working
1367     state.state = HB_STATE_SEARCHING;
1368     if ( pv->job->frame_to_start )
1369         p.progress  = (float) pv->common->count_frames / 
1370                       (float) pv->job->frame_to_start;
1371     else if ( pv->job->pts_to_start )
1372         p.progress  = (float) start / (float) pv->job->pts_to_start;
1373     else
1374         p.progress = 0;
1375     if( p.progress > 1.0 )
1376     {
1377         p.progress = 1.0;
1378     }
1379     if (now > sync->st_first)
1380     {
1381         int eta;
1382
1383         if ( pv->job->frame_to_start )
1384         {
1385             avg = 1000.0 * (double)pv->common->count_frames / (now - sync->st_first);
1386             eta = ( pv->job->frame_to_start - pv->common->count_frames ) / avg;
1387         }
1388         else if ( pv->job->pts_to_start )
1389         {
1390             avg = 1000.0 * (double)start / (now - sync->st_first);
1391             eta = ( pv->job->pts_to_start - start ) / avg;
1392         }
1393         p.hours   = eta / 3600;
1394         p.minutes = ( eta % 3600 ) / 60;
1395         p.seconds = eta % 60;
1396     }
1397     else
1398     {
1399         p.rate_avg = 0.0;
1400         p.hours    = -1;
1401         p.minutes  = -1;
1402         p.seconds  = -1;
1403     }
1404 #undef p
1405
1406     hb_set_state( pv->job->h, &state );
1407 }
1408
1409 static void getPtsOffset( hb_work_object_t * w )
1410 {
1411     hb_work_private_t * pv = w->private_data;
1412     int           i ;
1413     int64_t       first_pts = INT64_MAX;
1414
1415     for( i = 0; i < pv->common->pts_count; i++ )
1416     {
1417         if ( pv->common->first_pts[i] < first_pts )
1418             first_pts = pv->common->first_pts[i];
1419     }
1420     pv->common->video_pts_slip = pv->common->audio_pts_slip = pv->common->pts_offset = first_pts;
1421     return;
1422 }
1423
1424 static int checkPtsOffset( hb_work_object_t * w )
1425 {
1426     hb_work_private_t * pv = w->private_data;
1427     int           i ;
1428
1429     for( i = 0; i < pv->common->pts_count; i++ )
1430     {
1431         if ( pv->common->first_pts[i] == INT64_MAX )
1432             return 0;
1433     }
1434     getPtsOffset( w );
1435     return 1;
1436 }