1 /* $Id: sync.c,v 1.38 2005/04/14 21:57:58 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
9 #include "samplerate.h"
10 #include "ffmpeg/avcodec.h"
13 #undef INT64_MIN /* Because it isn't defined correctly in Zeta */
15 #define INT64_MIN (-9223372036854775807LL-1)
17 #define AC3_SAMPLES_PER_FRAME 1536
34 struct hb_work_private_s
40 hb_subtitle_t * subtitle;
42 int64_t pts_offset_old;
44 int64_t count_frames_max;
45 int64_t video_sequence;
46 hb_buffer_t * cur; /* The next picture to process */
49 hb_sync_audio_t sync_audio[8];
55 uint64_t st_counts[4];
59 /* Throttle message flags */
61 int inserting_silence;
65 /***********************************************************************
67 **********************************************************************/
68 static void InitAudio( hb_work_object_t * w, int i );
69 static int SyncVideo( hb_work_object_t * w );
70 static void SyncAudio( hb_work_object_t * w, int i );
71 static int NeedSilence( hb_work_object_t * w, hb_audio_t * );
72 static void InsertSilence( hb_work_object_t * w, int i );
73 static void UpdateState( hb_work_object_t * w );
75 /***********************************************************************
77 ***********************************************************************
78 * Initialize the work object
79 **********************************************************************/
80 int syncInit( hb_work_object_t * w, hb_job_t * job )
82 hb_title_t * title = job->title;
83 hb_chapter_t * chapter;
86 hb_work_private_t * pv;
88 pv = calloc( 1, sizeof( hb_work_private_t ) );
92 pv->pts_offset = INT64_MIN;
93 pv->pts_offset_old = INT64_MIN;
96 pv->discontinuity = 0;
98 pv->trashing_audio = 0;
99 pv->inserting_silence = 0;
100 pv->way_out_of_sync = 0;
102 /* Calculate how many video frames we are expecting */
104 for( i = job->chapter_start; i <= job->chapter_end; i++ )
106 chapter = hb_list_item( title->list_chapter, i - 1 );
107 duration += chapter->duration;
110 /* 1 second safety so we're sure we won't miss anything */
111 pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
113 hb_log( "sync: expecting %lld video frames", pv->count_frames_max );
115 /* Initialize libsamplerate for every audio track we have */
116 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
121 /* Get subtitle info, if any */
122 pv->subtitle = hb_list_item( title->list_subtitle, 0 );
124 pv->video_sequence = 0;
129 /***********************************************************************
131 ***********************************************************************
133 **********************************************************************/
134 void syncClose( hb_work_object_t * w )
136 hb_work_private_t * pv = w->private_data;
137 hb_job_t * job = pv->job;
138 hb_title_t * title = job->title;
142 if( pv->cur ) hb_buffer_close( &pv->cur );
144 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
146 if( job->acodec & HB_ACODEC_AC3 )
148 free( pv->sync_audio[i].ac3_buf );
152 src_delete( pv->sync_audio[i].state );
157 w->private_data = NULL;
160 /***********************************************************************
162 ***********************************************************************
163 * The root routine of this work abject
165 * The way this works is that we are syncing the audio to the PTS of
166 * the last video that we processed. That's why we skip the audio sync
167 * if we haven't got a valid PTS from the video yet.
169 **********************************************************************/
170 int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
171 hb_buffer_t ** unused2 )
173 hb_work_private_t * pv = w->private_data;
176 /* If we ever got a video frame, handle audio now */
177 if( pv->pts_offset != INT64_MIN )
179 for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
186 return SyncVideo( w );
189 hb_work_object_t hb_sync =
198 static void InitAudio( hb_work_object_t * w, int i )
200 hb_work_private_t * pv = w->private_data;
201 hb_job_t * job = pv->job;
202 hb_title_t * title = job->title;
203 hb_sync_audio_t * sync;
205 sync = &pv->sync_audio[i];
206 sync->audio = hb_list_item( title->list_audio, i );
208 if( job->acodec & HB_ACODEC_AC3 )
210 /* Have a silent AC-3 frame ready in case we have to fill a
216 codec = avcodec_find_encoder( CODEC_ID_AC3 );
217 c = avcodec_alloc_context();
219 c->bit_rate = sync->audio->bitrate;
220 c->sample_rate = sync->audio->rate;
223 if( avcodec_open( c, codec ) < 0 )
225 hb_log( "sync: avcodec_open failed" );
229 zeros = calloc( AC3_SAMPLES_PER_FRAME *
230 sizeof( short ) * c->channels, 1 );
231 sync->ac3_size = sync->audio->bitrate * AC3_SAMPLES_PER_FRAME /
232 sync->audio->rate / 8;
233 sync->ac3_buf = malloc( sync->ac3_size );
235 if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
236 zeros ) != sync->ac3_size )
238 hb_log( "sync: avcodec_encode_audio failed" );
247 /* Initialize libsamplerate */
249 sync->state = src_new( SRC_LINEAR, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown), &error );
250 sync->data.end_of_input = 0;
256 #define PTS_DISCONTINUITY_TOLERANCE 90000
258 /***********************************************************************
260 ***********************************************************************
262 **********************************************************************/
263 static int SyncVideo( hb_work_object_t * w )
265 hb_work_private_t * pv = w->private_data;
266 hb_buffer_t * cur, * next, * sub = NULL;
267 hb_job_t * job = pv->job;
268 int64_t pts_expected;
276 if( hb_thread_has_exited( job->reader ) &&
277 !hb_fifo_size( job->fifo_mpeg2 ) &&
278 !hb_fifo_size( job->fifo_raw ) )
280 /* All video data has been processed already, we won't get
282 hb_log( "sync: got %lld frames, %lld expected",
283 pv->count_frames, pv->count_frames_max );
286 hb_buffer_t * buf_tmp;
288 // Drop an empty buffer into our output to ensure that things
289 // get flushed all the way out.
290 buf_tmp = hb_buffer_init(0); // Empty end buffer
291 hb_fifo_push( job->fifo_sync, buf_tmp );
296 if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
298 /* We haven't even got a frame yet */
303 /* At this point we have a frame to process. Let's check
304 1) if we will be able to push into the fifo ahead
305 2) if the next frame is there already, since we need it to
306 know whether we'll have to repeat the current frame or not */
307 while( !hb_fifo_is_full( job->fifo_sync ) &&
308 ( next = hb_fifo_see( job->fifo_raw ) ) )
310 hb_buffer_t * buf_tmp;
312 if( pv->pts_offset == INT64_MIN )
314 /* This is our first frame */
315 hb_log( "sync: first pts is %lld", cur->start );
316 pv->pts_offset = cur->start;
320 * Track the video sequence number localy so that we can sync the audio
321 * to it using the sequence number as well as the PTS.
323 pv->video_sequence = cur->sequence;
325 /* Check for PTS jumps over 0.5 second */
326 if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE ||
327 next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE )
329 hb_log( "Sync: Video PTS discontinuity %s (current buffer start=%lld, next buffer start=%lld)",
330 pv->discontinuity ? "second" : "first", cur->start, next->start );
333 * Do we need to trash the subtitle, is it from the next->start period
334 * or is it from our old position. If the latter then trash it.
338 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
340 if( ( sub->start > ( cur->start - PTS_DISCONTINUITY_TOLERANCE ) ) &&
341 ( sub->start < ( cur->start + PTS_DISCONTINUITY_TOLERANCE ) ) )
344 * The subtitle is from our current time region which we are
345 * jumping from. So trash it as we are about to jump backwards
346 * or forwards and don't want it blocking the subtitle fifo.
348 hb_log("Trashing subtitle 0x%x due to PTS discontinuity", sub);
349 sub = hb_fifo_get( pv->subtitle->fifo_raw );
350 hb_buffer_close( &sub );
357 /* Trash current picture */
358 /* Also, make sure we don't trash a chapter break */
359 chap_break = cur->new_chap;
360 hb_buffer_close( &cur );
361 pv->cur = cur = hb_fifo_get( job->fifo_raw );
362 cur->new_chap |= chap_break; // Don't stomp existing chapter breaks
364 /* Calculate new offset */
365 pv->pts_offset_old = pv->pts_offset;
366 pv->pts_offset = cur->start -
367 pv->count_frames * pv->job->vrate_base / 300;
369 if( !pv->discontinuity )
371 pv->discontinuity = 1;
374 pv->video_sequence = cur->sequence;
378 /* Look for a subtitle for this frame */
382 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
384 /* If two subtitles overlap, make the first one stop
385 when the second one starts */
386 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
387 if( sub2 && sub->stop > sub2->start )
388 sub->stop = sub2->start;
390 // hb_log("0x%x: video seq: %lld subtitle sequence: %lld",
391 // sub, cur->sequence, sub->sequence);
393 if( sub->sequence > cur->sequence )
396 * The video is behind where we are, so wait until
397 * it catches up to the same reader point on the
398 * DVD. Then our PTS should be in the same region
405 if( sub->stop > cur->start ) {
407 * The stop time is in the future, so fall through
408 * and we'll deal with it in the next block of
416 * The stop time is in the past. But is it due to
417 * it having been played already, or has the PTS
420 if( ( cur->start - sub->stop ) > PTS_DISCONTINUITY_TOLERANCE ) {
422 * There is a lot of time between our current
423 * video and where this subtitle is ending,
424 * assume that we are about to reset the PTS
425 * and do not throw away this subtitle.
432 * The subtitle is older than this picture, trash it
434 sub = hb_fifo_get( pv->subtitle->fifo_raw );
435 hb_buffer_close( &sub );
439 * There is a valid subtitle, is it time to display it?
443 if( sub->stop > sub->start)
446 * Normal subtitle which ends after it starts, check to
447 * see that the current video is between the start and end.
449 if( cur->start > sub->start &&
450 cur->start < sub->stop )
453 * We should be playing this, so leave the
456 * fall through to display
462 * Defer until the play point is within the subtitle
470 * The end of the subtitle is less than the start, this is a
471 * sign of a PTS discontinuity.
473 if( sub->start > cur->start )
476 * we haven't reached the start time yet, or
477 * we have jumped backwards after having
478 * already started this subtitle.
480 if( cur->start < sub->stop )
483 * We have jumped backwards and so should
484 * continue displaying this subtitle.
486 * fall through to display.
492 * Defer until the play point is within the subtitle
498 * Play this subtitle as the start is greater than our
501 * fall through to display/
508 /* The PTS of the frame we are expecting now */
509 pts_expected = pv->pts_offset +
510 pv->count_frames * pv->job->vrate_base / 300;
512 //hb_log("Video expecting PTS %lld, current frame: %lld, next frame: %lld, cf: %lld",
513 // pts_expected, cur->start, next->start, pv->count_frames * pv->job->vrate_base / 300 );
515 if( cur->start < pts_expected - pv->job->vrate_base / 300 / 2 &&
516 next->start < pts_expected + pv->job->vrate_base / 300 / 2 )
518 /* The current frame is too old but the next one matches,
520 /* Also, make sure we don't trash a chapter break */
521 chap_break = cur->new_chap;
522 hb_buffer_close( &cur );
523 pv->cur = cur = hb_fifo_get( job->fifo_raw );
524 cur->new_chap |= chap_break; // Make sure we don't stomp the existing one.
529 if( next->start > pts_expected + 3 * pv->job->vrate_base / 300 / 2 )
531 /* We'll need the current frame more than one time. Make a
532 copy of it and keep it */
533 buf_tmp = hb_buffer_init( cur->size );
534 memcpy( buf_tmp->data, cur->data, cur->size );
535 buf_tmp->sequence = cur->sequence;
539 /* The frame has the expected date and won't have to be
540 duplicated, just put it through */
542 pv->cur = cur = hb_fifo_get( job->fifo_raw );
545 /* Replace those MPEG-2 dates with our dates */
546 buf_tmp->start = (uint64_t) pv->count_frames *
547 pv->job->vrate_base / 300;
548 buf_tmp->stop = (uint64_t) ( pv->count_frames + 1 ) *
549 pv->job->vrate_base / 300;
551 /* If we have a subtitle for this picture, copy it */
552 /* FIXME: we should avoid this memcpy */
555 buf_tmp->sub = hb_buffer_init( sub->size );
556 buf_tmp->sub->x = sub->x;
557 buf_tmp->sub->y = sub->y;
558 buf_tmp->sub->width = sub->width;
559 buf_tmp->sub->height = sub->height;
560 memcpy( buf_tmp->sub->data, sub->data, sub->size );
563 /* Push the frame to the renderer */
564 hb_fifo_push( job->fifo_sync, buf_tmp );
569 /* Make sure we won't get more frames then expected */
570 if( pv->count_frames >= pv->count_frames_max )
572 hb_log( "sync: got %lld frames", pv->count_frames );
575 // Drop an empty buffer into our output to ensure that things
576 // get flushed all the way out.
577 buf_tmp = hb_buffer_init(0); // Empty end buffer
578 hb_fifo_push( job->fifo_sync, buf_tmp );
587 /***********************************************************************
589 ***********************************************************************
591 **********************************************************************/
592 static void SyncAudio( hb_work_object_t * w, int i )
594 hb_work_private_t * pv = w->private_data;
598 hb_sync_audio_t * sync;
603 int64_t pts_expected;
607 sync = &pv->sync_audio[i];
610 if( job->acodec & HB_ACODEC_AC3 )
612 fifo = audio->fifo_out;
617 fifo = audio->fifo_sync;
621 while( !hb_fifo_is_full( fifo ) &&
622 ( buf = hb_fifo_see( audio->fifo_raw ) ) )
624 /* The PTS of the samples we are expecting now */
625 pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
627 // hb_log("Video Sequence: %lld, Audio Sequence: %lld", pv->video_sequence, buf->sequence);
630 * Using the same logic as the Video have we crossed a VOB
631 * boundary as detected by the expected PTS and the PTS of our
632 * audio being out by more than the tolerance value.
634 if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
635 buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
637 /* There has been a PTS discontinuity, and this frame might
638 be from before the discontinuity*/
640 if( pv->discontinuity )
643 * There is an outstanding discontinuity, so use the offset from
644 * that discontinuity.
646 pts_expected = pv->pts_offset_old + sync->count_frames *
652 * No outstanding discontinuity, so the audio must be leading the
653 * video (or the PTS values are really stuffed). So lets mark this
654 * as a discontinuity ourselves for the audio to use until
655 * the video also crosses the discontinuity.
657 * pts_offset is used when we are in the same time space as the video
658 * pts_offset_old when in a discontinuity.
660 * Therefore set the pts_offset_old given the new pts_offset for this
663 pv->discontinuity = 1;
664 pv->pts_offset_old = buf->start - sync->count_frames *
666 pts_expected = pv->pts_offset_old + sync->count_frames *
669 hb_log("Sync: Audio discontinuity (sequence: vid %lld aud %lld) (pts %lld < %lld < %lld)",
670 pv->video_sequence, buf->sequence,
671 pts_expected - PTS_DISCONTINUITY_TOLERANCE, buf->start,
672 pts_expected + PTS_DISCONTINUITY_TOLERANCE );
676 * Is the audio from a valid period given the previous
677 * Video PTS. I.e. has there just been a video PTS
678 * discontinuity and this audio belongs to the vdeo from
681 if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
682 buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
685 * It's outside of our tolerance for where the video
686 * is now, and it's outside of the tolerance for
687 * where we have been in the case of a VOB change.
688 * Try and reconverge regardless. so continue on to
689 * our convergence code below which will kick in as
690 * it will be more than 100ms out.
692 * Note that trashing the Audio could make things
693 * worse if the Audio is in front because we will end
694 * up diverging even more. We need to hold on to the
695 * audio until the video catches up.
697 if( !pv->way_out_of_sync )
699 hb_log("Sync: Audio is way out of sync, attempt to reconverge from current video PTS");
700 pv->way_out_of_sync = 1;
704 * It wasn't from the old place, so we must be from
705 * the new, but just too far out. So attempt to
706 * reconverge by resetting the point we want to be to
707 * where we are currently wanting to be.
709 pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
710 start = pts_expected - pv->pts_offset;
712 /* Use the older offset */
713 start = pts_expected - pv->pts_offset_old;
718 start = pts_expected - pv->pts_offset;
720 if( pv->discontinuity )
723 * The Audio is tracking the Video again using the normal pts_offset, so the
724 * discontinuity is over.
726 hb_log( "Sync: Audio joined Video after discontinuity at PTS %lld", buf->start );
727 pv->discontinuity = 0;
731 /* Tolerance: 100 ms */
732 if( buf->start < pts_expected - 9000 )
734 if( !pv->trashing_audio )
736 /* Audio is behind the Video, trash it, can't use it now. */
737 hb_log( "Sync: Audio PTS (%lld) < Video PTS (%lld) by greater than 100ms, trashing audio to reconverge",
738 buf->start, pts_expected);
739 pv->trashing_audio = 1;
741 buf = hb_fifo_get( audio->fifo_raw );
742 hb_buffer_close( &buf );
745 else if( buf->start > pts_expected + 9000 )
747 /* Audio is ahead of the Video, insert silence until we catch up*/
748 if( !pv->inserting_silence )
750 hb_log("Sync: Audio PTS (%lld) > Video PTS (%lld) by greater than 100ms insert silence until reconverged", buf->start, pts_expected);
751 pv->inserting_silence = 1;
753 InsertSilence( w, i );
758 if( pv->trashing_audio || pv->inserting_silence )
760 hb_log( "Sync: Audio back in Sync at PTS %lld", buf->start );
761 pv->trashing_audio = 0;
762 pv->inserting_silence = 0;
764 if( pv->way_out_of_sync )
766 hb_log( "Sync: Audio no longer way out of sync at PTS %lld",
768 pv->way_out_of_sync = 0;
772 if( job->acodec & HB_ACODEC_AC3 )
774 buf = hb_fifo_get( audio->fifo_raw );
776 buf->stop = start + 90000 * AC3_SAMPLES_PER_FRAME / rate;
778 sync->count_frames += AC3_SAMPLES_PER_FRAME;
782 hb_buffer_t * buf_raw = hb_fifo_get( audio->fifo_raw );
784 int count_in, count_out;
786 count_in = buf_raw->size / HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) / sizeof( float );
787 count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000;
788 if( buf->start < pts_expected - 1500 )
790 else if( buf->start > pts_expected + 1500 )
793 sync->data.data_in = (float *) buf_raw->data;
794 sync->data.input_frames = count_in;
795 sync->data.output_frames = count_out;
797 sync->data.src_ratio = (double) sync->data.output_frames /
798 (double) sync->data.input_frames;
800 buf = hb_buffer_init( sync->data.output_frames * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) *
802 sync->data.data_out = (float *) buf->data;
803 if( src_process( sync->state, &sync->data ) )
805 /* XXX If this happens, we're screwed */
806 hb_log( "sync: src_process failed" );
808 hb_buffer_close( &buf_raw );
810 buf->size = sync->data.output_frames_gen * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) * sizeof( float );
812 /* Set dates for resampled data */
814 buf->stop = start + sync->data.output_frames_gen *
817 sync->count_frames += sync->data.output_frames_gen;
820 buf->frametype = HB_FRAME_AUDIO;
821 hb_fifo_push( fifo, buf );
824 if( hb_fifo_is_full( fifo ) &&
825 pv->way_out_of_sync )
828 * Trash the top audio packet to avoid dead lock as we reconverge.
830 buf = hb_fifo_get( audio->fifo_raw );
831 hb_buffer_close( &buf );
834 if( NeedSilence( w, audio ) )
836 InsertSilence( w, i );
840 static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
842 hb_work_private_t * pv = w->private_data;
843 hb_job_t * job = pv->job;
845 if( hb_fifo_size( audio->fifo_in ) ||
846 hb_fifo_size( audio->fifo_raw ) ||
847 hb_fifo_size( audio->fifo_sync ) ||
848 hb_fifo_size( audio->fifo_out ) )
850 /* We have some audio, we are fine */
854 /* No audio left in fifos */
856 if( hb_thread_has_exited( job->reader ) )
858 /* We might miss some audio to complete encoding and muxing
860 hb_log("Reader has exited early, inserting silence.");
864 if( hb_fifo_is_full( job->fifo_mpeg2 ) &&
865 hb_fifo_is_full( job->fifo_raw ) &&
866 hb_fifo_is_full( job->fifo_sync ) &&
867 hb_fifo_is_full( job->fifo_render ) &&
868 hb_fifo_is_full( job->fifo_mpeg4 ) )
870 /* Too much video and no audio, oh-oh */
871 hb_log("Still got some video - and nothing in the audio fifo, insert silence");
878 static void InsertSilence( hb_work_object_t * w, int i )
880 hb_work_private_t * pv = w->private_data;
882 hb_sync_audio_t * sync;
886 sync = &pv->sync_audio[i];
888 if( job->acodec & HB_ACODEC_AC3 )
890 buf = hb_buffer_init( sync->ac3_size );
891 buf->start = sync->count_frames * 90000 / sync->audio->rate;
892 buf->stop = buf->start + 90000 * AC3_SAMPLES_PER_FRAME /
894 memcpy( buf->data, sync->ac3_buf, buf->size );
896 hb_log( "sync: adding a silent AC-3 frame for track %x",
898 hb_fifo_push( sync->audio->fifo_out, buf );
900 sync->count_frames += AC3_SAMPLES_PER_FRAME;
905 buf = hb_buffer_init( HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown) * job->arate / 20 *
907 buf->start = sync->count_frames * 90000 / job->arate;
908 buf->stop = buf->start + 90000 / 20;
909 memset( buf->data, 0, buf->size );
911 hb_fifo_push( sync->audio->fifo_sync, buf );
913 sync->count_frames += job->arate / 20;
917 static void UpdateState( hb_work_object_t * w )
919 hb_work_private_t * pv = w->private_data;
922 if( !pv->count_frames )
924 pv->st_first = hb_get_date();
928 if( hb_get_date() > pv->st_dates[3] + 1000 )
930 memmove( &pv->st_dates[0], &pv->st_dates[1],
931 3 * sizeof( uint64_t ) );
932 memmove( &pv->st_counts[0], &pv->st_counts[1],
933 3 * sizeof( uint64_t ) );
934 pv->st_dates[3] = hb_get_date();
935 pv->st_counts[3] = pv->count_frames;
938 #define p state.param.working
939 state.state = HB_STATE_WORKING;
940 p.progress = (float) pv->count_frames / (float) pv->count_frames_max;
941 if( p.progress > 1.0 )
945 p.rate_cur = 1000.0 *
946 (float) ( pv->st_counts[3] - pv->st_counts[0] ) /
947 (float) ( pv->st_dates[3] - pv->st_dates[0] );
948 if( hb_get_date() > pv->st_first + 4000 )
951 p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
952 (float) ( pv->st_dates[3] - pv->st_first );
953 eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
955 p.hours = eta / 3600;
956 p.minutes = ( eta % 3600 ) / 60;
957 p.seconds = eta % 60;
968 hb_set_state( pv->job->h, &state );