OSDN Git Service

fix problem with delayed audio when using point-to-point
[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         start = buf->start - pv->common->audio_pts_slip;
1003     }
1004     if ( start < 0 )
1005     {
1006         hb_buffer_close( &buf );
1007         hb_unlock( pv->common->mutex );
1008         return HB_WORK_OK;
1009     }
1010     hb_unlock( pv->common->mutex );
1011
1012     if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop )
1013     {
1014         hb_buffer_close( &buf );
1015         *buf_out = hb_buffer_init( 0 );
1016         return HB_WORK_DONE;
1017     }
1018
1019     if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
1020     {
1021         hb_buffer_close( &buf );
1022         *buf_out = hb_buffer_init( 0 );
1023         return HB_WORK_DONE;
1024     }
1025
1026     if ( start - sync->next_start < 0 )
1027     {
1028         // audio time went backwards.
1029         // If our output clock is more than a half frame ahead of the
1030         // input clock drop this frame to move closer to sync.
1031         // Otherwise drop frames until the input clock matches the output clock.
1032         if ( sync->first_drop || sync->next_start - start > 90*15 )
1033         {
1034             // Discard data that's in the past.
1035             if ( sync->first_drop == 0 )
1036             {
1037                 sync->first_drop = sync->next_start;
1038             }
1039             ++sync->drop_count;
1040             hb_buffer_close( &buf );
1041             return HB_WORK_OK;
1042         }
1043     }
1044     if ( sync->first_drop )
1045     {
1046         // we were dropping old data but input buf time is now current
1047         hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
1048                 "(next %"PRId64", current %"PRId64")", w->audio->id,
1049                 (int)( sync->next_start - sync->first_drop ) / 90,
1050                 sync->drop_count, sync->first_drop, (int64_t)sync->next_start );
1051         sync->first_drop = 0;
1052         sync->drop_count = 0;
1053     }
1054     if ( start - sync->next_start >= (90 * 70) )
1055     {
1056         if ( start - sync->next_start > (90000LL * 60) )
1057         {
1058             // there's a gap of more than a minute between the last
1059             // frame and this. assume we got a corrupted timestamp
1060             // and just drop the next buf.
1061             hb_log( "sync: %d minute time gap in audio %d - dropping buf"
1062                     "  start %"PRId64", next %"PRId64,
1063                     (int)((start - sync->next_start) / (90000*60)),
1064                     w->audio->id, start, (int64_t)sync->next_start );
1065             hb_buffer_close( &buf );
1066             return HB_WORK_OK;
1067         }
1068         /*
1069          * there's a gap of at least 70ms between the last
1070          * frame we processed & the next. Fill it with silence.
1071          * Or in the case of DCA, skip some frames from the
1072          * other streams.
1073          */
1074         if( w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1075         {
1076             hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
1077                     "  start %"PRId64", next %"PRId64,
1078                     (int)((start - sync->next_start) / 90),
1079                     w->audio->id, start, (int64_t)sync->next_start );
1080             hb_lock( pv->common->mutex );
1081             pv->common->audio_pts_slip += (start - sync->next_start);
1082             pv->common->video_pts_slip += (start - sync->next_start);
1083             hb_unlock( pv->common->mutex );
1084             *buf_out = buf;
1085             return HB_WORK_OK;
1086         }
1087         hb_log( "sync: adding %d ms of silence to audio %d"
1088                 "  start %"PRId64", next %"PRId64,
1089                 (int)((start - sync->next_start) / 90),
1090                 w->audio->id, start, (int64_t)sync->next_start );
1091         InsertSilence( w, start - sync->next_start );
1092     }
1093
1094     /*
1095      * When we get here we've taken care of all the dups and gaps in the
1096      * audio stream and are ready to inject the next input frame into
1097      * the output stream.
1098      */
1099     *buf_out = OutputAudioFrame( w->audio, buf, sync );
1100     return HB_WORK_OK;
1101 }
1102
1103 hb_work_object_t hb_sync_audio =
1104 {
1105     WORK_SYNC_AUDIO,
1106     "AudioSynchronization",
1107     syncAudioInit,
1108     syncAudioWork,
1109     syncAudioClose
1110 };
1111
1112 static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
1113 {
1114     hb_work_object_t  * w;
1115     hb_work_private_t * pv;
1116     hb_title_t        * title = job->title;
1117     hb_sync_audio_t   * sync;
1118
1119     pv = calloc( 1, sizeof( hb_work_private_t ) );
1120     sync = &pv->type.audio;
1121     sync->index = i;
1122     pv->job    = job;
1123     pv->common = common;
1124     pv->common->ref++;
1125     pv->common->pts_count++;
1126
1127     w = hb_get_work( WORK_SYNC_AUDIO );
1128     w->private_data = pv;
1129     w->audio = hb_list_item( title->list_audio, i );
1130     w->fifo_in = w->audio->priv.fifo_raw;
1131
1132     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1133         w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1134     {
1135         w->fifo_out = w->audio->priv.fifo_out;
1136     }
1137     else
1138     {
1139         w->fifo_out = w->audio->priv.fifo_sync;
1140     }
1141
1142     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1143     {
1144         /* Have a silent AC-3 frame ready in case we have to fill a
1145            gap */
1146         AVCodec        * codec;
1147         AVCodecContext * c;
1148         short          * zeros;
1149
1150         codec = avcodec_find_encoder( CODEC_ID_AC3 );
1151         c     = avcodec_alloc_context();
1152
1153         c->bit_rate    = w->audio->config.in.bitrate;
1154         c->sample_rate = w->audio->config.in.samplerate;
1155         c->channels    = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( w->audio->config.in.channel_layout );
1156
1157         if( hb_avcodec_open( c, codec ) < 0 )
1158         {
1159             hb_log( "sync: avcodec_open failed" );
1160             return;
1161         }
1162
1163         zeros          = calloc( AC3_SAMPLES_PER_FRAME *
1164                                  sizeof( short ) * c->channels, 1 );
1165         sync->ac3_size = w->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
1166                              w->audio->config.in.samplerate / 8;
1167         sync->ac3_buf  = malloc( sync->ac3_size );
1168
1169         if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
1170                                   zeros ) != sync->ac3_size )
1171         {
1172             hb_log( "sync: avcodec_encode_audio failed" );
1173         }
1174
1175         free( zeros );
1176         hb_avcodec_close( c );
1177         av_free( c );
1178     }
1179     else
1180     {
1181         /* Initialize libsamplerate */
1182         int error;
1183         sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, 
1184             HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1185                 w->audio->config.out.mixdown), &error );
1186         sync->data.end_of_input = 0;
1187     }
1188     hb_list_add( job->list_work, w );
1189 }
1190
1191 static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
1192                                        hb_sync_audio_t *sync )
1193 {
1194     int64_t start = (int64_t)sync->next_start;
1195     double duration = buf->stop - buf->start;
1196
1197     if( audio->config.in.samplerate == audio->config.out.samplerate ||
1198         audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1199         audio->config.out.codec == HB_ACODEC_DCA_PASS )
1200     {
1201         /*
1202          * If we don't have to do sample rate conversion or this audio is 
1203          * pass-thru just send the input buffer downstream after adjusting
1204          * its timestamps to make the output stream continuous.
1205          */
1206     }
1207     else
1208     {
1209         /* Not pass-thru - do sample rate conversion */
1210         int count_in, count_out;
1211         hb_buffer_t * buf_raw = buf;
1212         int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown) *
1213                             sizeof( float );
1214
1215         count_in  = buf_raw->size / channel_count;
1216         /*
1217          * When using stupid rates like 44.1 there will always be some
1218          * truncation error. E.g., a 1536 sample AC3 frame will turn into a
1219          * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2
1220          * the error will build up over time and eventually the audio will
1221          * substantially lag the video. libsamplerate will keep track of the
1222          * fractional sample & give it to us when appropriate if we give it
1223          * an extra sample of space in the output buffer.
1224          */
1225         count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1;
1226
1227         sync->data.input_frames = count_in;
1228         sync->data.output_frames = count_out;
1229         sync->data.src_ratio = (double)audio->config.out.samplerate /
1230                                (double)audio->config.in.samplerate;
1231
1232         buf = hb_buffer_init( count_out * channel_count );
1233         sync->data.data_in  = (float *) buf_raw->data;
1234         sync->data.data_out = (float *) buf->data;
1235         if( src_process( sync->state, &sync->data ) )
1236         {
1237             /* XXX If this happens, we're screwed */
1238             hb_log( "sync: audio %d src_process failed", audio->id );
1239         }
1240         hb_buffer_close( &buf_raw );
1241
1242         buf->size = sync->data.output_frames_gen * channel_count;
1243         duration = (double)( sync->data.output_frames_gen * 90000 ) /
1244                    audio->config.out.samplerate;
1245     }
1246     buf->frametype = HB_FRAME_AUDIO;
1247     buf->start = start;
1248     sync->next_start += duration;
1249     buf->stop  = (int64_t)sync->next_start;
1250     return buf;
1251 }
1252
1253 static void InsertSilence( hb_work_object_t * w, int64_t duration )
1254 {
1255     hb_work_private_t * pv = w->private_data;
1256     hb_sync_audio_t *sync = &pv->type.audio;
1257     hb_buffer_t     *buf;
1258     hb_fifo_t       *fifo;
1259
1260     // to keep pass-thru and regular audio in sync we generate silence in
1261     // AC3 frame-sized units. If the silence duration isn't an integer multiple
1262     // of the AC3 frame duration we will truncate or round up depending on
1263     // which minimizes the timing error.
1264     const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
1265                           w->audio->config.in.samplerate;
1266     int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
1267
1268     while ( --frame_count >= 0 )
1269     {
1270         if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1271         {
1272             buf        = hb_buffer_init( sync->ac3_size );
1273             buf->start = sync->next_start;
1274             buf->stop  = buf->start + frame_dur;
1275             memcpy( buf->data, sync->ac3_buf, buf->size );
1276             fifo = w->audio->priv.fifo_out;
1277         }
1278         else
1279         {
1280             buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
1281                                      HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1282                                          w->audio->config.out.mixdown) );
1283             buf->start = sync->next_start;
1284             buf->stop  = buf->start + frame_dur;
1285             memset( buf->data, 0, buf->size );
1286             fifo = w->audio->priv.fifo_sync;
1287         }
1288         buf = OutputAudioFrame( w->audio, buf, sync );
1289         hb_fifo_push( fifo, buf );
1290     }
1291 }
1292
1293 static void UpdateState( hb_work_object_t * w )
1294 {
1295     hb_work_private_t * pv = w->private_data;
1296     hb_sync_video_t   * sync = &pv->type.video;
1297     hb_state_t state;
1298
1299     if( !pv->common->count_frames )
1300     {
1301         sync->st_first = hb_get_date();
1302         pv->job->st_pause_date = -1;
1303         pv->job->st_paused = 0;
1304     }
1305     pv->common->count_frames++;
1306
1307     if( hb_get_date() > sync->st_dates[3] + 1000 )
1308     {
1309         memmove( &sync->st_dates[0], &sync->st_dates[1],
1310                  3 * sizeof( uint64_t ) );
1311         memmove( &sync->st_counts[0], &sync->st_counts[1],
1312                  3 * sizeof( uint64_t ) );
1313         sync->st_dates[3]  = hb_get_date();
1314         sync->st_counts[3] = pv->common->count_frames;
1315     }
1316
1317 #define p state.param.working
1318     state.state = HB_STATE_WORKING;
1319     p.progress  = (float) pv->common->count_frames / (float) sync->count_frames_max;
1320     if( p.progress > 1.0 )
1321     {
1322         p.progress = 1.0;
1323     }
1324     p.rate_cur   = 1000.0 *
1325         (float) ( sync->st_counts[3] - sync->st_counts[0] ) /
1326         (float) ( sync->st_dates[3] - sync->st_dates[0] );
1327     if( hb_get_date() > sync->st_first + 4000 )
1328     {
1329         int eta;
1330         p.rate_avg = 1000.0 * (float) sync->st_counts[3] /
1331             (float) ( sync->st_dates[3] - sync->st_first - pv->job->st_paused);
1332         eta = (float) ( sync->count_frames_max - sync->st_counts[3] ) /
1333             p.rate_avg;
1334         p.hours   = eta / 3600;
1335         p.minutes = ( eta % 3600 ) / 60;
1336         p.seconds = eta % 60;
1337     }
1338     else
1339     {
1340         p.rate_avg = 0.0;
1341         p.hours    = -1;
1342         p.minutes  = -1;
1343         p.seconds  = -1;
1344     }
1345 #undef p
1346
1347     hb_set_state( pv->job->h, &state );
1348 }
1349
1350 static void UpdateSearchState( hb_work_object_t * w, int64_t start )
1351 {
1352     hb_work_private_t * pv = w->private_data;
1353     hb_sync_video_t   * sync = &pv->type.video;
1354     hb_state_t state;
1355     uint64_t now;
1356     double avg;
1357
1358     now = hb_get_date();
1359     if( !pv->common->count_frames )
1360     {
1361         sync->st_first = now;
1362         pv->job->st_pause_date = -1;
1363         pv->job->st_paused = 0;
1364     }
1365     pv->common->count_frames++;
1366
1367 #define p state.param.working
1368     state.state = HB_STATE_SEARCHING;
1369     if ( pv->job->frame_to_start )
1370         p.progress  = (float) pv->common->count_frames / 
1371                       (float) pv->job->frame_to_start;
1372     else if ( pv->job->pts_to_start )
1373         p.progress  = (float) start / (float) pv->job->pts_to_start;
1374     else
1375         p.progress = 0;
1376     if( p.progress > 1.0 )
1377     {
1378         p.progress = 1.0;
1379     }
1380     if (now > sync->st_first)
1381     {
1382         int eta;
1383
1384         if ( pv->job->frame_to_start )
1385         {
1386             avg = 1000.0 * (double)pv->common->count_frames / (now - sync->st_first);
1387             eta = ( pv->job->frame_to_start - pv->common->count_frames ) / avg;
1388         }
1389         else if ( pv->job->pts_to_start )
1390         {
1391             avg = 1000.0 * (double)start / (now - sync->st_first);
1392             eta = ( pv->job->pts_to_start - start ) / avg;
1393         }
1394         p.hours   = eta / 3600;
1395         p.minutes = ( eta % 3600 ) / 60;
1396         p.seconds = eta % 60;
1397     }
1398     else
1399     {
1400         p.rate_avg = 0.0;
1401         p.hours    = -1;
1402         p.minutes  = -1;
1403         p.seconds  = -1;
1404     }
1405 #undef p
1406
1407     hb_set_state( pv->job->h, &state );
1408 }
1409
1410 static void getPtsOffset( hb_work_object_t * w )
1411 {
1412     hb_work_private_t * pv = w->private_data;
1413     int           i ;
1414     int64_t       first_pts = INT64_MAX;
1415
1416     for( i = 0; i < pv->common->pts_count; i++ )
1417     {
1418         if ( pv->common->first_pts[i] < first_pts )
1419             first_pts = pv->common->first_pts[i];
1420     }
1421     pv->common->video_pts_slip = pv->common->audio_pts_slip = pv->common->pts_offset = first_pts;
1422     return;
1423 }
1424
1425 static int checkPtsOffset( hb_work_object_t * w )
1426 {
1427     hb_work_private_t * pv = w->private_data;
1428     int           i ;
1429
1430     for( i = 0; i < pv->common->pts_count; i++ )
1431     {
1432         if ( pv->common->first_pts[i] == INT64_MAX )
1433             return 0;
1434     }
1435     getPtsOffset( w );
1436     return 1;
1437 }