OSDN Git Service

cfc0c2a206d1599236a8426925d4c9f5324a1f75
[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 too early. Leave it in the fifo.
621                         sub = NULL;
622                         break;
623                     }
624                 }
625             }
626             
627             continue;
628         }
629
630         // For rendered subtitles (and, for backward compatibility, passthru VOBSUBs),
631         // delay pushing subtitle packets through the pipeline until the video catches up
632         if( subtitle->config.dest == RENDERSUB || subtitle->source == VOBSUB ) 
633         {
634             hb_buffer_t * sub2;
635             while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) )
636             {
637                 if( sub->size == 0 )
638                 {
639                     /*
640                      * EOF, pass it through immediately.
641                      */
642                     break;
643                 }
644
645                 hb_lock( pv->common->mutex );
646                 sub_start = sub->start - pv->common->video_pts_slip;
647                 hb_unlock( pv->common->mutex );
648                 duration = sub->stop - sub->start;
649                 sub_stop = sub_start + duration;
650
651                 /* If two subtitles overlap, make the first one stop
652                    when the second one starts */
653                 sub2 = hb_fifo_see2( subtitle->fifo_raw );
654                 if( sub2 && sub->stop > sub2->start )
655                 {
656                     sub->stop = sub2->start;
657                 }
658                 
659                 // hb_log("0x%x: video seq: %"PRId64" subtitle sequence: %"PRId64,
660                 //       sub, cur->sequence, sub->sequence);
661                 
662                 if( sub->sequence > cur->sequence )
663                 {
664                     /*
665                      * The video is behind where we are, so wait until
666                      * it catches up to the same reader point on the
667                      * DVD. Then our PTS should be in the same region
668                      * as the video.
669                      */
670                     sub = NULL;
671                     break;
672                 }
673                 
674                 if( sub_stop > start ) 
675                 {
676                     /*
677                      * The stop time is in the future, so fall through
678                      * and we'll deal with it in the next block of
679                      * code.
680                      */
681
682                     /*
683                      * There is a valid subtitle, is it time to display it?
684                      */
685                     if( sub_stop > sub_start)
686                     {
687                         /*
688                          * Normal subtitle which ends after it starts, 
689                          * check to see that the current video is between 
690                          * the start and end.
691                          */
692                         if( start > sub_start &&
693                             start < sub_stop )
694                         {
695                             /*
696                             * We should be playing this, so leave the
697                             * subtitle in place.
698                             *
699                             * fall through to display
700                             */
701                         }
702                         else
703                         {
704                             /*
705                              * Defer until the play point is within 
706                              * the subtitle
707                              */
708                             sub = NULL;
709                         }
710                     }
711                     else
712                     {
713                         /*
714                          * The end of the subtitle is less than the start, 
715                          * this is a sign of a PTS discontinuity.
716                          */
717                         if( sub_start > start )
718                         {
719                             /*
720                              * we haven't reached the start time yet, or
721                              * we have jumped backwards after having
722                              * already started this subtitle.
723                              */
724                             if( start < sub_stop )
725                             {
726                                 /*
727                                  * We have jumped backwards and so should
728                                  * continue displaying this subtitle.
729                                  *
730                                  * fall through to display.
731                                  */
732                             }
733                             else
734                             {
735                                 /*
736                                  * Defer until the play point is 
737                                  * within the subtitle
738                                  */
739                                 sub = NULL;
740                             }
741                         } else {
742                             /*
743                             * Play this subtitle as the start is 
744                             * greater than our video point.
745                             *
746                             * fall through to display/
747                             */
748                         }
749                     }
750                         break;
751                 }
752                 else
753                 {
754                     
755                     /*
756                      * The subtitle is older than this picture, trash it
757                      */
758                     sub = hb_fifo_get( subtitle->fifo_raw );
759                     hb_buffer_close( &sub );
760                 }
761             }
762             
763             /* If we have a subtitle for this picture, copy it */
764             if( sub )
765             {
766                 if( sub->size > 0 )
767                 {
768                     if( subtitle->config.dest == RENDERSUB )
769                     {
770                         // Only allow one subtitle to be showing at once; ignore others
771                         if ( cur->sub == NULL )
772                         {
773                             /*
774                              * Tack onto the video buffer for rendering
775                              */
776                             /* FIXME: we should avoid this memcpy */
777                             cur->sub = copy_subtitle( sub );
778                             cur->sub->start = sub_start;
779                             cur->sub->stop = sub_stop;
780                             
781                             // Leave the subtitle on the raw queue
782                             // (until it no longer needs to be displayed)
783                         }
784                     } else {
785                         /*
786                          * Pass-Through, pop it off of the raw queue, 
787                          */
788                         sub = hb_fifo_get( subtitle->fifo_raw );
789                         sub->start = sub_start;
790                         sub->stop = sub_stop;
791                         hb_fifo_push( subtitle->fifo_sync, sub );
792                     }
793                 } else {
794                     /*
795                     * EOF - consume for rendered, else pass through
796                     */
797                     if( subtitle->config.dest == RENDERSUB )
798                     {
799                         sub = hb_fifo_get( subtitle->fifo_raw );
800                         hb_buffer_close( &sub );
801                     } else {
802                         sub = hb_fifo_get( subtitle->fifo_raw );
803                         sub->start = sub_start;
804                         sub->stop = sub_stop;
805                         hb_fifo_push( subtitle->fifo_sync, sub );
806                     }
807                 }
808             }
809         }
810     } // end subtitles
811
812     /*
813      * Adjust the pts of the current frame so that it's contiguous
814      * with the previous frame. The start time of the current frame
815      * has to be the end time of the previous frame and the stop
816      * time has to be the start of the next frame.  We don't
817      * make any adjustments to the source timestamps other than removing
818      * the clock offsets (which also removes pts discontinuities).
819      * This means we automatically encode at the source's frame rate.
820      * MP2 uses an implicit duration (frames end when the next frame
821      * starts) but more advanced containers like MP4 use an explicit
822      * duration. Since we're looking ahead one frame we set the
823      * explicit stop time from the start time of the next frame.
824      */
825     *buf_out = cur;
826     sync->cur = cur = next;
827     cur->sub = NULL;
828     int64_t duration = next_start - start;
829     sync->pts_skip = 0;
830     if ( duration <= 0 )
831     {
832         hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"",
833                 duration, start, next_start );
834     }
835
836     (*buf_out)->start = sync->next_start;
837     sync->next_start += duration;
838     (*buf_out)->stop = sync->next_start;
839
840     if ( sync->chap_mark )
841     {
842         // we have a pending chapter mark from a recent drop - put it on this
843         // buffer (this may make it one frame late but we can't do any better).
844         (*buf_out)->new_chap = sync->chap_mark;
845         sync->chap_mark = 0;
846     }
847
848     /* Update UI */
849     UpdateState( w );
850
851     return HB_WORK_OK;
852 }
853
854 static hb_buffer_t * copy_subtitle( hb_buffer_t * src_list )
855 {
856     hb_buffer_t * dst_list = NULL;
857     
858     hb_buffer_t * src;
859     hb_buffer_t * dst;
860     hb_buffer_t ** dst_ptr = &dst_list;
861     for ( src = src_list, dst_ptr = &dst_list;
862           src;
863           src = src->next_subpicture, dst_ptr = &dst->next_subpicture )
864     {
865         (*dst_ptr)  = hb_buffer_init( src->size );
866         dst         = (*dst_ptr); 
867         dst->x      = src->x;
868         dst->y      = src->y;
869         dst->width  = src->width;
870         dst->height = src->height;
871         memcpy( dst->data, src->data, src->size );
872     }
873     
874     return dst_list;
875 }
876
877 // sync*Init does nothing because sync has a special initializer
878 // that takes care of initializing video and all audio tracks
879 int syncVideoInit( hb_work_object_t * w, hb_job_t * job)
880 {
881     return 0;
882 }
883
884 hb_work_object_t hb_sync_video =
885 {
886     WORK_SYNC_VIDEO,
887     "Video Synchronization",
888     syncVideoInit,
889     syncVideoWork,
890     syncVideoClose
891 };
892
893 /***********************************************************************
894  * Close Audio
895  ***********************************************************************
896  *
897  **********************************************************************/
898 void syncAudioClose( hb_work_object_t * w )
899 {
900     hb_work_private_t * pv    = w->private_data;
901     hb_sync_audio_t   * sync  = &pv->type.audio;
902
903     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
904     {
905         free( sync->ac3_buf );
906     }
907     else
908     {
909         src_delete( sync->state );
910     }
911
912     hb_lock( pv->common->mutex );
913     if ( --pv->common->ref == 0 )
914     {
915         hb_unlock( pv->common->mutex );
916         hb_lock_close( &pv->common->mutex );
917         free( pv->common );
918     }
919     else
920     {
921         hb_unlock( pv->common->mutex );
922     }
923
924     free( pv );
925     w->private_data = NULL;
926 }
927
928 int syncAudioInit( hb_work_object_t * w, hb_job_t * job)
929 {
930     return 0;
931 }
932
933 /***********************************************************************
934  * SyncAudio
935  ***********************************************************************
936  *
937  **********************************************************************/
938 static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
939                        hb_buffer_t ** buf_out )
940 {
941     hb_work_private_t * pv = w->private_data;
942     hb_job_t        * job = pv->job;
943     hb_sync_audio_t * sync = &pv->type.audio;
944     hb_buffer_t     * buf;
945     int64_t start;
946
947     *buf_out = NULL;
948     buf = *buf_in;
949     *buf_in = NULL;
950     /* if the next buffer is an eof send it downstream */
951     if ( buf->size <= 0 )
952     {
953         hb_buffer_close( &buf );
954         *buf_out = hb_buffer_init( 0 );
955         return HB_WORK_DONE;
956     }
957
958     /* Wait till we can determine the initial pts of all streams */
959     if( pv->common->pts_offset == INT64_MIN )
960     {
961         pv->common->first_pts[sync->index+1] = buf->start;
962         hb_lock( pv->common->mutex );
963         while( pv->common->pts_offset == INT64_MIN )
964         {
965             // Full fifos will make us wait forever, so get the
966             // pts offset from the available streams if full
967             if (hb_fifo_is_full(w->fifo_in))
968             {
969                 getPtsOffset( w );
970                 hb_cond_broadcast( pv->common->next_frame );
971             }
972             else if ( checkPtsOffset( w ) )
973                 hb_cond_broadcast( pv->common->next_frame );
974             else
975                 hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
976         }
977         hb_unlock( pv->common->mutex );
978     }
979
980     /* Wait for start frame if doing point-to-point */
981     hb_lock( pv->common->mutex );
982     start = buf->start - pv->common->audio_pts_slip;
983     while ( !pv->common->start_found )
984     {
985         if ( pv->common->audio_pts_thresh < 0 )
986         {
987             // I would initialize this in hb_sync_init, but 
988             // job->pts_to_start can be modified by reader 
989             // after hb_sync_init is called.
990             pv->common->audio_pts_thresh = job->pts_to_start;
991         }
992         if ( start < pv->common->audio_pts_thresh )
993         {
994             hb_buffer_close( &buf );
995             hb_unlock( pv->common->mutex );
996             return HB_WORK_OK;
997         }
998         while ( !pv->common->start_found && 
999                 start >= pv->common->audio_pts_thresh )
1000         {
1001             hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 );
1002         }
1003         start = buf->start - pv->common->audio_pts_slip;
1004     }
1005     if ( start < 0 )
1006     {
1007         hb_buffer_close( &buf );
1008         hb_unlock( pv->common->mutex );
1009         return HB_WORK_OK;
1010     }
1011     hb_unlock( pv->common->mutex );
1012
1013     if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop )
1014     {
1015         hb_buffer_close( &buf );
1016         *buf_out = hb_buffer_init( 0 );
1017         return HB_WORK_DONE;
1018     }
1019
1020     if( job->pts_to_stop && sync->next_start >= job->pts_to_stop )
1021     {
1022         hb_buffer_close( &buf );
1023         *buf_out = hb_buffer_init( 0 );
1024         return HB_WORK_DONE;
1025     }
1026
1027     if ( start - sync->next_start < 0 )
1028     {
1029         // audio time went backwards.
1030         // If our output clock is more than a half frame ahead of the
1031         // input clock drop this frame to move closer to sync.
1032         // Otherwise drop frames until the input clock matches the output clock.
1033         if ( sync->first_drop || sync->next_start - start > 90*15 )
1034         {
1035             // Discard data that's in the past.
1036             if ( sync->first_drop == 0 )
1037             {
1038                 sync->first_drop = sync->next_start;
1039             }
1040             ++sync->drop_count;
1041             hb_buffer_close( &buf );
1042             return HB_WORK_OK;
1043         }
1044     }
1045     if ( sync->first_drop )
1046     {
1047         // we were dropping old data but input buf time is now current
1048         hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
1049                 "(next %"PRId64", current %"PRId64")", w->audio->id,
1050                 (int)( sync->next_start - sync->first_drop ) / 90,
1051                 sync->drop_count, sync->first_drop, (int64_t)sync->next_start );
1052         sync->first_drop = 0;
1053         sync->drop_count = 0;
1054     }
1055     if ( start - sync->next_start >= (90 * 70) )
1056     {
1057         if ( start - sync->next_start > (90000LL * 60) )
1058         {
1059             // there's a gap of more than a minute between the last
1060             // frame and this. assume we got a corrupted timestamp
1061             // and just drop the next buf.
1062             hb_log( "sync: %d minute time gap in audio %d - dropping buf"
1063                     "  start %"PRId64", next %"PRId64,
1064                     (int)((start - sync->next_start) / (90000*60)),
1065                     w->audio->id, start, (int64_t)sync->next_start );
1066             hb_buffer_close( &buf );
1067             return HB_WORK_OK;
1068         }
1069         /*
1070          * there's a gap of at least 70ms between the last
1071          * frame we processed & the next. Fill it with silence.
1072          * Or in the case of DCA, skip some frames from the
1073          * other streams.
1074          */
1075         if( w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1076         {
1077             hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
1078                     "  start %"PRId64", next %"PRId64,
1079                     (int)((start - sync->next_start) / 90),
1080                     w->audio->id, start, (int64_t)sync->next_start );
1081             hb_lock( pv->common->mutex );
1082             pv->common->audio_pts_slip += (start - sync->next_start);
1083             pv->common->video_pts_slip += (start - sync->next_start);
1084             hb_unlock( pv->common->mutex );
1085             *buf_out = buf;
1086             return HB_WORK_OK;
1087         }
1088         hb_log( "sync: adding %d ms of silence to audio %d"
1089                 "  start %"PRId64", next %"PRId64,
1090                 (int)((start - sync->next_start) / 90),
1091                 w->audio->id, start, (int64_t)sync->next_start );
1092         InsertSilence( w, start - sync->next_start );
1093     }
1094
1095     /*
1096      * When we get here we've taken care of all the dups and gaps in the
1097      * audio stream and are ready to inject the next input frame into
1098      * the output stream.
1099      */
1100     *buf_out = OutputAudioFrame( w->audio, buf, sync );
1101     return HB_WORK_OK;
1102 }
1103
1104 hb_work_object_t hb_sync_audio =
1105 {
1106     WORK_SYNC_AUDIO,
1107     "AudioSynchronization",
1108     syncAudioInit,
1109     syncAudioWork,
1110     syncAudioClose
1111 };
1112
1113 static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i )
1114 {
1115     hb_work_object_t  * w;
1116     hb_work_private_t * pv;
1117     hb_title_t        * title = job->title;
1118     hb_sync_audio_t   * sync;
1119
1120     pv = calloc( 1, sizeof( hb_work_private_t ) );
1121     sync = &pv->type.audio;
1122     sync->index = i;
1123     pv->job    = job;
1124     pv->common = common;
1125     pv->common->ref++;
1126     pv->common->pts_count++;
1127
1128     w = hb_get_work( WORK_SYNC_AUDIO );
1129     w->private_data = pv;
1130     w->audio = hb_list_item( title->list_audio, i );
1131     w->fifo_in = w->audio->priv.fifo_raw;
1132
1133     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1134         w->audio->config.out.codec == HB_ACODEC_DCA_PASS )
1135     {
1136         w->fifo_out = w->audio->priv.fifo_out;
1137     }
1138     else
1139     {
1140         w->fifo_out = w->audio->priv.fifo_sync;
1141     }
1142
1143     if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1144     {
1145         /* Have a silent AC-3 frame ready in case we have to fill a
1146            gap */
1147         AVCodec        * codec;
1148         AVCodecContext * c;
1149         short          * zeros;
1150
1151         codec = avcodec_find_encoder( CODEC_ID_AC3 );
1152         c     = avcodec_alloc_context();
1153
1154         c->bit_rate    = w->audio->config.in.bitrate;
1155         c->sample_rate = w->audio->config.in.samplerate;
1156         c->channels    = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( w->audio->config.in.channel_layout );
1157
1158         if( hb_avcodec_open( c, codec ) < 0 )
1159         {
1160             hb_log( "sync: avcodec_open failed" );
1161             return;
1162         }
1163
1164         zeros          = calloc( AC3_SAMPLES_PER_FRAME *
1165                                  sizeof( short ) * c->channels, 1 );
1166         sync->ac3_size = w->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
1167                              w->audio->config.in.samplerate / 8;
1168         sync->ac3_buf  = malloc( sync->ac3_size );
1169
1170         if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
1171                                   zeros ) != sync->ac3_size )
1172         {
1173             hb_log( "sync: avcodec_encode_audio failed" );
1174         }
1175
1176         free( zeros );
1177         hb_avcodec_close( c );
1178         av_free( c );
1179     }
1180     else
1181     {
1182         /* Initialize libsamplerate */
1183         int error;
1184         sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, 
1185             HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1186                 w->audio->config.out.mixdown), &error );
1187         sync->data.end_of_input = 0;
1188     }
1189     hb_list_add( job->list_work, w );
1190 }
1191
1192 static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf,
1193                                        hb_sync_audio_t *sync )
1194 {
1195     int64_t start = (int64_t)sync->next_start;
1196     double duration = buf->stop - buf->start;
1197
1198     if( audio->config.in.samplerate == audio->config.out.samplerate ||
1199         audio->config.out.codec == HB_ACODEC_AC3_PASS ||
1200         audio->config.out.codec == HB_ACODEC_DCA_PASS )
1201     {
1202         /*
1203          * If we don't have to do sample rate conversion or this audio is 
1204          * pass-thru just send the input buffer downstream after adjusting
1205          * its timestamps to make the output stream continuous.
1206          */
1207     }
1208     else
1209     {
1210         /* Not pass-thru - do sample rate conversion */
1211         int count_in, count_out;
1212         hb_buffer_t * buf_raw = buf;
1213         int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown) *
1214                             sizeof( float );
1215
1216         count_in  = buf_raw->size / channel_count;
1217         /*
1218          * When using stupid rates like 44.1 there will always be some
1219          * truncation error. E.g., a 1536 sample AC3 frame will turn into a
1220          * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2
1221          * the error will build up over time and eventually the audio will
1222          * substantially lag the video. libsamplerate will keep track of the
1223          * fractional sample & give it to us when appropriate if we give it
1224          * an extra sample of space in the output buffer.
1225          */
1226         count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1;
1227
1228         sync->data.input_frames = count_in;
1229         sync->data.output_frames = count_out;
1230         sync->data.src_ratio = (double)audio->config.out.samplerate /
1231                                (double)audio->config.in.samplerate;
1232
1233         buf = hb_buffer_init( count_out * channel_count );
1234         sync->data.data_in  = (float *) buf_raw->data;
1235         sync->data.data_out = (float *) buf->data;
1236         if( src_process( sync->state, &sync->data ) )
1237         {
1238             /* XXX If this happens, we're screwed */
1239             hb_log( "sync: audio %d src_process failed", audio->id );
1240         }
1241         hb_buffer_close( &buf_raw );
1242
1243         buf->size = sync->data.output_frames_gen * channel_count;
1244         duration = (double)( sync->data.output_frames_gen * 90000 ) /
1245                    audio->config.out.samplerate;
1246     }
1247     buf->frametype = HB_FRAME_AUDIO;
1248     buf->start = start;
1249     sync->next_start += duration;
1250     buf->stop  = (int64_t)sync->next_start;
1251     return buf;
1252 }
1253
1254 static void InsertSilence( hb_work_object_t * w, int64_t duration )
1255 {
1256     hb_work_private_t * pv = w->private_data;
1257     hb_sync_audio_t *sync = &pv->type.audio;
1258     hb_buffer_t     *buf;
1259     hb_fifo_t       *fifo;
1260
1261     // to keep pass-thru and regular audio in sync we generate silence in
1262     // AC3 frame-sized units. If the silence duration isn't an integer multiple
1263     // of the AC3 frame duration we will truncate or round up depending on
1264     // which minimizes the timing error.
1265     const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
1266                           w->audio->config.in.samplerate;
1267     int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
1268
1269     while ( --frame_count >= 0 )
1270     {
1271         if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS )
1272         {
1273             buf        = hb_buffer_init( sync->ac3_size );
1274             buf->start = sync->next_start;
1275             buf->stop  = buf->start + frame_dur;
1276             memcpy( buf->data, sync->ac3_buf, buf->size );
1277             fifo = w->audio->priv.fifo_out;
1278         }
1279         else
1280         {
1281             buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
1282                                      HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
1283                                          w->audio->config.out.mixdown) );
1284             buf->start = sync->next_start;
1285             buf->stop  = buf->start + frame_dur;
1286             memset( buf->data, 0, buf->size );
1287             fifo = w->audio->priv.fifo_sync;
1288         }
1289         buf = OutputAudioFrame( w->audio, buf, sync );
1290         hb_fifo_push( fifo, buf );
1291     }
1292 }
1293
1294 static void UpdateState( hb_work_object_t * w )
1295 {
1296     hb_work_private_t * pv = w->private_data;
1297     hb_sync_video_t   * sync = &pv->type.video;
1298     hb_state_t state;
1299
1300     if( !pv->common->count_frames )
1301     {
1302         sync->st_first = hb_get_date();
1303         pv->job->st_pause_date = -1;
1304         pv->job->st_paused = 0;
1305     }
1306     pv->common->count_frames++;
1307
1308     if( hb_get_date() > sync->st_dates[3] + 1000 )
1309     {
1310         memmove( &sync->st_dates[0], &sync->st_dates[1],
1311                  3 * sizeof( uint64_t ) );
1312         memmove( &sync->st_counts[0], &sync->st_counts[1],
1313                  3 * sizeof( uint64_t ) );
1314         sync->st_dates[3]  = hb_get_date();
1315         sync->st_counts[3] = pv->common->count_frames;
1316     }
1317
1318 #define p state.param.working
1319     state.state = HB_STATE_WORKING;
1320     p.progress  = (float) pv->common->count_frames / (float) sync->count_frames_max;
1321     if( p.progress > 1.0 )
1322     {
1323         p.progress = 1.0;
1324     }
1325     p.rate_cur   = 1000.0 *
1326         (float) ( sync->st_counts[3] - sync->st_counts[0] ) /
1327         (float) ( sync->st_dates[3] - sync->st_dates[0] );
1328     if( hb_get_date() > sync->st_first + 4000 )
1329     {
1330         int eta;
1331         p.rate_avg = 1000.0 * (float) sync->st_counts[3] /
1332             (float) ( sync->st_dates[3] - sync->st_first - pv->job->st_paused);
1333         eta = (float) ( sync->count_frames_max - sync->st_counts[3] ) /
1334             p.rate_avg;
1335         p.hours   = eta / 3600;
1336         p.minutes = ( eta % 3600 ) / 60;
1337         p.seconds = eta % 60;
1338     }
1339     else
1340     {
1341         p.rate_avg = 0.0;
1342         p.hours    = -1;
1343         p.minutes  = -1;
1344         p.seconds  = -1;
1345     }
1346 #undef p
1347
1348     hb_set_state( pv->job->h, &state );
1349 }
1350
1351 static void UpdateSearchState( hb_work_object_t * w, int64_t start )
1352 {
1353     hb_work_private_t * pv = w->private_data;
1354     hb_sync_video_t   * sync = &pv->type.video;
1355     hb_state_t state;
1356     uint64_t now;
1357     double avg;
1358
1359     now = hb_get_date();
1360     if( !pv->common->count_frames )
1361     {
1362         sync->st_first = now;
1363         pv->job->st_pause_date = -1;
1364         pv->job->st_paused = 0;
1365     }
1366     pv->common->count_frames++;
1367
1368 #define p state.param.working
1369     state.state = HB_STATE_SEARCHING;
1370     if ( pv->job->frame_to_start )
1371         p.progress  = (float) pv->common->count_frames / 
1372                       (float) pv->job->frame_to_start;
1373     else if ( pv->job->pts_to_start )
1374         p.progress  = (float) start / (float) pv->job->pts_to_start;
1375     else
1376         p.progress = 0;
1377     if( p.progress > 1.0 )
1378     {
1379         p.progress = 1.0;
1380     }
1381     if (now > sync->st_first)
1382     {
1383         int eta;
1384
1385         if ( pv->job->frame_to_start )
1386         {
1387             avg = 1000.0 * (double)pv->common->count_frames / (now - sync->st_first);
1388             eta = ( pv->job->frame_to_start - pv->common->count_frames ) / avg;
1389         }
1390         else if ( pv->job->pts_to_start )
1391         {
1392             avg = 1000.0 * (double)start / (now - sync->st_first);
1393             eta = ( pv->job->pts_to_start - start ) / avg;
1394         }
1395         p.hours   = eta / 3600;
1396         p.minutes = ( eta % 3600 ) / 60;
1397         p.seconds = eta % 60;
1398     }
1399     else
1400     {
1401         p.rate_avg = 0.0;
1402         p.hours    = -1;
1403         p.minutes  = -1;
1404         p.seconds  = -1;
1405     }
1406 #undef p
1407
1408     hb_set_state( pv->job->h, &state );
1409 }
1410
1411 static void getPtsOffset( hb_work_object_t * w )
1412 {
1413     hb_work_private_t * pv = w->private_data;
1414     int           i ;
1415     int64_t       first_pts = INT64_MAX;
1416
1417     for( i = 0; i < pv->common->pts_count; i++ )
1418     {
1419         if ( pv->common->first_pts[i] < first_pts )
1420             first_pts = pv->common->first_pts[i];
1421     }
1422     pv->common->video_pts_slip = pv->common->audio_pts_slip = pv->common->pts_offset = first_pts;
1423     return;
1424 }
1425
1426 static int checkPtsOffset( hb_work_object_t * w )
1427 {
1428     hb_work_private_t * pv = w->private_data;
1429     int           i ;
1430
1431     for( i = 0; i < pv->common->pts_count; i++ )
1432     {
1433         if ( pv->common->first_pts[i] == INT64_MAX )
1434             return 0;
1435     }
1436     getPtsOffset( w );
1437     return 1;
1438 }