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.fr/>.
5 It may be used under the terms of the GNU General Public License. */
10 #include "samplerate.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
23 int64_t next_start; /* start time of next output frame */
24 int64_t next_pts; /* start time of next input frame */
25 int64_t first_drop; /* PTS of first 'went backwards' frame dropped */
26 int drop_count; /* count of 'time went backwards' drops */
38 struct hb_work_private_s
41 int busy; // bitmask with one bit for each active input
42 // (bit 0 = video; 1 = audio 0, 2 = audio 1, ...
43 // appropriate bit is cleared when input gets
44 // an eof buf. syncWork returns done when all
47 hb_subtitle_t * subtitle;
49 int64_t next_start; /* start time of next output frame */
50 int64_t next_pts; /* start time of next input frame */
51 int64_t first_drop; /* PTS of first 'went backwards' frame dropped */
52 int drop_count; /* count of 'time went backwards' drops */
53 int drops; /* frames dropped to make a cbr video stream */
54 int dups; /* frames duplicated to make a cbr video stream */
58 int chap_mark; /* to propagate chapter mark across a drop */
59 hb_buffer_t * cur; /* The next picture to process */
62 hb_sync_audio_t sync_audio[8];
65 uint64_t st_counts[4];
70 /***********************************************************************
72 **********************************************************************/
73 static void InitAudio( hb_work_object_t * w, int i );
74 static void SyncVideo( hb_work_object_t * w );
75 static void SyncAudio( hb_work_object_t * w, int i );
76 static void InsertSilence( hb_work_object_t * w, int i, int64_t d );
77 static void UpdateState( hb_work_object_t * w );
79 /***********************************************************************
81 ***********************************************************************
82 * Initialize the work object
83 **********************************************************************/
84 int syncInit( hb_work_object_t * w, hb_job_t * job )
86 hb_title_t * title = job->title;
87 hb_chapter_t * chapter;
90 hb_work_private_t * pv;
92 pv = calloc( 1, sizeof( hb_work_private_t ) );
96 pv->pts_offset = INT64_MIN;
98 /* Calculate how many video frames we are expecting */
101 duration = job->pts_to_stop + 90000;
103 else if( job->frame_to_stop )
105 /* Set the duration to a rough estimate */
106 duration = ( job->frame_to_stop / ( job->vrate / job->vrate_base ) ) * 90000;
111 for( i = job->chapter_start; i <= job->chapter_end; i++ )
113 chapter = hb_list_item( title->list_chapter, i - 1 );
114 duration += chapter->duration;
117 /* 1 second safety so we're sure we won't miss anything */
119 pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
121 hb_log( "sync: expecting %d video frames", pv->count_frames_max );
124 /* Initialize libsamplerate for every audio track we have */
125 if ( ! job->indepth_scan )
127 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
129 pv->busy |= ( 1 << (i + 1) );
134 /* Get subtitle info, if any */
135 pv->subtitle = hb_list_item( title->list_subtitle, 0 );
140 /***********************************************************************
142 ***********************************************************************
144 **********************************************************************/
145 void syncClose( hb_work_object_t * w )
147 hb_work_private_t * pv = w->private_data;
148 hb_job_t * job = pv->job;
149 hb_title_t * title = job->title;
150 hb_audio_t * audio = NULL;
155 hb_buffer_close( &pv->cur );
158 hb_log( "sync: got %d frames, %d expected",
159 pv->count_frames, pv->count_frames_max );
161 if (pv->drops || pv->dups )
163 hb_log( "sync: %d frames dropped, %d duplicated", pv->drops, pv->dups );
166 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
168 audio = hb_list_item( title->list_audio, i );
169 if( audio->config.out.codec == HB_ACODEC_AC3 )
171 free( pv->sync_audio[i].ac3_buf );
175 src_delete( pv->sync_audio[i].state );
180 w->private_data = NULL;
183 /***********************************************************************
185 ***********************************************************************
186 * The root routine of this work abject
188 * The way this works is that we are syncing the audio to the PTS of
189 * the last video that we processed. That's why we skip the audio sync
190 * if we haven't got a valid PTS from the video yet.
192 **********************************************************************/
193 int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
194 hb_buffer_t ** unused2 )
196 hb_work_private_t * pv = w->private_data;
202 for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
204 if ( pv->busy & ( 1 << (i + 1) ) )
208 return ( pv->busy? HB_WORK_OK : HB_WORK_DONE );
211 hb_work_object_t hb_sync =
220 static void InitAudio( hb_work_object_t * w, int i )
222 hb_work_private_t * pv = w->private_data;
223 hb_job_t * job = pv->job;
224 hb_title_t * title = job->title;
225 hb_sync_audio_t * sync;
227 sync = &pv->sync_audio[i];
228 sync->audio = hb_list_item( title->list_audio, i );
230 if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
232 /* Have a silent AC-3 frame ready in case we have to fill a
238 codec = avcodec_find_encoder( CODEC_ID_AC3 );
239 c = avcodec_alloc_context();
241 c->bit_rate = sync->audio->config.in.bitrate;
242 c->sample_rate = sync->audio->config.in.samplerate;
243 c->channels = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( sync->audio->config.in.channel_layout );
245 if( hb_avcodec_open( c, codec ) < 0 )
247 hb_log( "sync: avcodec_open failed" );
251 zeros = calloc( AC3_SAMPLES_PER_FRAME *
252 sizeof( short ) * c->channels, 1 );
253 sync->ac3_size = sync->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
254 sync->audio->config.in.samplerate / 8;
255 sync->ac3_buf = malloc( sync->ac3_size );
257 if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
258 zeros ) != sync->ac3_size )
260 hb_log( "sync: avcodec_encode_audio failed" );
264 hb_avcodec_close( c );
269 /* Initialize libsamplerate */
271 sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->config.out.mixdown), &error );
272 sync->data.end_of_input = 0;
276 /***********************************************************************
278 ***********************************************************************
280 **********************************************************************/
281 static void SyncVideo( hb_work_object_t * w )
283 hb_work_private_t * pv = w->private_data;
284 hb_buffer_t * cur, * next, * sub = NULL;
285 hb_job_t * job = pv->job;
287 if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
289 /* We haven't even got a frame yet */
295 /* we got an end-of-stream. Feed it downstream & signal that we're done. */
296 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
301 /* At this point we have a frame to process. Let's check
302 1) if we will be able to push into the fifo ahead
303 2) if the next frame is there already, since we need it to
304 compute the duration of the current frame*/
305 while( !hb_fifo_is_full( job->fifo_sync ) &&
306 ( next = hb_fifo_see( job->fifo_raw ) ) )
308 hb_buffer_t * buf_tmp;
310 if( next->size == 0 )
312 /* we got an end-of-stream. Feed it downstream & signal that
313 * we're done. Note that this means we drop the final frame of
314 * video (we don't know its duration). On DVDs the final frame
315 * is often strange and dropping it seems to be a good idea. */
316 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
320 if( pv->pts_offset == INT64_MIN )
322 /* This is our first frame */
324 if ( cur->start != 0 )
327 * The first pts from a dvd should always be zero but
328 * can be non-zero with a transport or program stream since
329 * we're not guaranteed to start on an IDR frame. If we get
330 * a non-zero initial PTS extend its duration so it behaves
331 * as if it started at zero so that our audio timing will
334 hb_log( "sync: first pts is %lld", cur->start );
339 if( cur->new_chap ) {
340 hb_log("sync got new chapter %d", cur->new_chap );
344 * since the first frame is always 0 and the upstream reader code
345 * is taking care of adjusting for pts discontinuities, we just have
346 * to deal with the next frame's start being in the past. This can
347 * happen when the PTS is adjusted after data loss but video frame
348 * reordering causes some frames with the old clock to appear after
349 * the clock change. This creates frames that overlap in time which
350 * looks to us like time going backward. The downstream muxing code
351 * can deal with overlaps of up to a frame time but anything larger
352 * we handle by dropping frames here.
354 if ( (int64_t)( next->start - cur->start ) <= 0 )
356 if ( pv->first_drop == 0 )
358 pv->first_drop = next->start;
361 buf_tmp = hb_fifo_get( job->fifo_raw );
362 if ( buf_tmp->new_chap )
364 // don't drop a chapter mark when we drop the buffer
365 pv->chap_mark = buf_tmp->new_chap;
367 hb_buffer_close( &buf_tmp );
370 if ( pv->first_drop )
372 hb_log( "sync: video time didn't advance - dropped %d frames "
373 "(delta %d ms, current %lld, next %lld, dur %d)",
374 pv->drop_count, (int)( cur->start - pv->first_drop ) / 90,
375 cur->start, next->start, (int)( next->start - cur->start ) );
381 * Track the video sequence number localy so that we can sync the audio
382 * to it using the sequence number as well as the PTS.
384 pv->video_sequence = cur->sequence;
386 /* Look for a subtitle for this frame */
390 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
392 /* If two subtitles overlap, make the first one stop
393 when the second one starts */
394 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
395 if( sub2 && sub->stop > sub2->start )
396 sub->stop = sub2->start;
398 // hb_log("0x%x: video seq: %lld subtitle sequence: %lld",
399 // sub, cur->sequence, sub->sequence);
401 if( sub->sequence > cur->sequence )
404 * The video is behind where we are, so wait until
405 * it catches up to the same reader point on the
406 * DVD. Then our PTS should be in the same region
413 if( sub->stop > cur->start ) {
415 * The stop time is in the future, so fall through
416 * and we'll deal with it in the next block of
423 * The subtitle is older than this picture, trash it
425 sub = hb_fifo_get( pv->subtitle->fifo_raw );
426 hb_buffer_close( &sub );
430 * There is a valid subtitle, is it time to display it?
434 if( sub->stop > sub->start)
437 * Normal subtitle which ends after it starts, check to
438 * see that the current video is between the start and end.
440 if( cur->start > sub->start &&
441 cur->start < sub->stop )
444 * We should be playing this, so leave the
447 * fall through to display
449 if( ( sub->stop - sub->start ) < ( 3 * 90000 ) )
452 * Subtitle is on for less than three seconds, extend
453 * the time that it is displayed to make it easier
454 * to read. Make it 3 seconds or until the next
455 * subtitle is displayed.
457 * This is in response to Indochine which only
458 * displays subs for 1 second - too fast to read.
460 sub->stop = sub->start + ( 3 * 90000 );
462 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
464 if( sub2 && sub->stop > sub2->start )
466 sub->stop = sub2->start;
473 * Defer until the play point is within the subtitle
481 * The end of the subtitle is less than the start, this is a
482 * sign of a PTS discontinuity.
484 if( sub->start > cur->start )
487 * we haven't reached the start time yet, or
488 * we have jumped backwards after having
489 * already started this subtitle.
491 if( cur->start < sub->stop )
494 * We have jumped backwards and so should
495 * continue displaying this subtitle.
497 * fall through to display.
503 * Defer until the play point is within the subtitle
509 * Play this subtitle as the start is greater than our
512 * fall through to display/
520 if ( job->mux & HB_MUX_AVI || job->cfr )
523 * The concept of variable frame rate video was a bit too advanced
524 * for Microsoft so AVI doesn't support it. Since almost all dvd
525 * video is VFR we have to convert it to constant frame rate to
526 * put it in an AVI container. So here we duplicate, drop and
527 * otherwise trash video frames to appease the gods of Redmond.
530 /* mpeg durations are exact when expressed in ticks of the
531 * 27MHz System clock but not in HB's 90KHz PTS clock. To avoid
532 * a truncation bias that will eventually cause the audio to desync
533 * we compute the duration of the next frame using 27MHz ticks
534 * then truncate it to 90KHz. */
535 duration = ( (int64_t)(pv->count_frames + 1 ) * job->vrate_base ) / 300 -
538 /* We don't want the input & output clocks to be exactly in phase
539 * otherwise small variations in the time will cause us to think
540 * we're a full frame off & there will be lots of drops and dups.
541 * We offset the input clock by half the duration so it's maximally
542 * out of phase with the output clock. */
543 if( cur->start < pv->next_start - ( duration >> 1 ) )
545 /* current frame too old - drop it */
548 pv->chap_mark = cur->new_chap;
550 hb_buffer_close( &cur );
551 pv->cur = cur = hb_fifo_get( job->fifo_raw );
552 pv->next_pts = next->start;
557 if( next->start > pv->next_start + duration + ( duration >> 1 ) )
559 /* next frame too far ahead - dup current frame */
560 buf_tmp = hb_buffer_init( cur->size );
561 hb_buffer_copy_settings( buf_tmp, cur );
562 memcpy( buf_tmp->data, cur->data, cur->size );
563 buf_tmp->sequence = cur->sequence;
568 /* this frame in our time window & doesn't need to be duped */
570 pv->cur = cur = hb_fifo_get( job->fifo_raw );
571 pv->next_pts = next->start;
577 * Adjust the pts of the current frame so that it's contiguous
578 * with the previous frame. The start time of the current frame
579 * has to be the end time of the previous frame and the stop
580 * time has to be the start of the next frame. We don't
581 * make any adjustments to the source timestamps other than removing
582 * the clock offsets (which also removes pts discontinuities).
583 * This means we automatically encode at the source's frame rate.
584 * MP2 uses an implicit duration (frames end when the next frame
585 * starts) but more advanced containers like MP4 use an explicit
586 * duration. Since we're looking ahead one frame we set the
587 * explicit stop time from the start time of the next frame.
590 pv->cur = cur = hb_fifo_get( job->fifo_raw );
591 pv->next_pts = cur->start;
592 duration = cur->start - buf_tmp->start;
595 hb_log( "sync: invalid video duration %lld, start %lld, next %lld",
596 duration, buf_tmp->start, next->start );
600 buf_tmp->start = pv->next_start;
601 pv->next_start += duration;
602 buf_tmp->stop = pv->next_start;
606 // we have a pending chapter mark from a recent drop - put it on this
607 // buffer (this may make it one frame late but we can't do any better).
608 buf_tmp->new_chap = pv->chap_mark;
612 /* If we have a subtitle for this picture, copy it */
613 /* FIXME: we should avoid this memcpy */
616 buf_tmp->sub = hb_buffer_init( sub->size );
617 buf_tmp->sub->x = sub->x;
618 buf_tmp->sub->y = sub->y;
619 buf_tmp->sub->width = sub->width;
620 buf_tmp->sub->height = sub->height;
621 memcpy( buf_tmp->sub->data, sub->data, sub->size );
624 /* Push the frame to the renderer */
625 hb_fifo_push( job->fifo_sync, buf_tmp );
630 if( job->frame_to_stop && pv->count_frames > job->frame_to_stop )
632 // Drop an empty buffer into our output to ensure that things
633 // get flushed all the way out.
634 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
636 hb_log( "sync: reached %d frames, exiting early (%i busy)",
637 pv->count_frames, pv->busy );
641 /* Make sure we won't get more frames then expected */
642 if( pv->count_frames >= pv->count_frames_max * 2)
644 hb_log( "sync: got too many frames (%d), exiting early",
647 // Drop an empty buffer into our output to ensure that things
648 // get flushed all the way out.
649 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
656 static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf,
657 hb_sync_audio_t *sync, hb_fifo_t *fifo, int i )
659 int64_t start = sync->next_start;
660 int64_t duration = buf->stop - buf->start;
662 sync->next_pts += duration;
664 if( audio->config.in.samplerate == audio->config.out.samplerate ||
665 audio->config.out.codec == HB_ACODEC_AC3 ||
666 audio->config.out.codec == HB_ACODEC_DCA )
669 * If we don't have to do sample rate conversion or this audio is
670 * pass-thru just send the input buffer downstream after adjusting
671 * its timestamps to make the output stream continuous.
676 /* Not pass-thru - do sample rate conversion */
677 int count_in, count_out;
678 hb_buffer_t * buf_raw = buf;
679 int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown) *
682 count_in = buf_raw->size / channel_count;
684 * When using stupid rates like 44.1 there will always be some
685 * truncation error. E.g., a 1536 sample AC3 frame will turn into a
686 * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2
687 * the error will build up over time and eventually the audio will
688 * substantially lag the video. libsamplerate will keep track of the
689 * fractional sample & give it to us when appropriate if we give it
690 * an extra sample of space in the output buffer.
692 count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1;
694 sync->data.input_frames = count_in;
695 sync->data.output_frames = count_out;
696 sync->data.src_ratio = (double)audio->config.out.samplerate /
697 (double)audio->config.in.samplerate;
699 buf = hb_buffer_init( count_out * channel_count );
700 sync->data.data_in = (float *) buf_raw->data;
701 sync->data.data_out = (float *) buf->data;
702 if( src_process( sync->state, &sync->data ) )
704 /* XXX If this happens, we're screwed */
705 hb_log( "sync: audio %d src_process failed", i );
707 hb_buffer_close( &buf_raw );
709 buf->size = sync->data.output_frames_gen * channel_count;
710 duration = ( sync->data.output_frames_gen * 90000 ) /
711 audio->config.out.samplerate;
713 buf->frametype = HB_FRAME_AUDIO;
715 buf->stop = start + duration;
716 sync->next_start = start + duration;
717 hb_fifo_push( fifo, buf );
720 /***********************************************************************
722 ***********************************************************************
724 **********************************************************************/
725 static void SyncAudio( hb_work_object_t * w, int i )
727 hb_work_private_t * pv = w->private_data;
728 hb_job_t * job = pv->job;
729 hb_sync_audio_t * sync = &pv->sync_audio[i];
730 hb_audio_t * audio = sync->audio;
734 if( audio->config.out.codec == HB_ACODEC_AC3 )
736 fifo = audio->priv.fifo_out;
740 fifo = audio->priv.fifo_sync;
743 while( !hb_fifo_is_full( fifo ) && ( buf = hb_fifo_see( audio->priv.fifo_raw ) ) )
745 /* if the next buffer is an eof send it downstream */
746 if ( buf->size <= 0 )
748 buf = hb_fifo_get( audio->priv.fifo_raw );
749 hb_fifo_push( fifo, buf );
750 pv->busy &=~ (1 << (i + 1) );
753 if( job->frame_to_stop && pv->count_frames >= job->frame_to_stop )
755 hb_fifo_push( fifo, hb_buffer_init(0) );
756 pv->busy &=~ (1 << (i + 1) );
759 if ( (int64_t)( buf->start - sync->next_pts ) < 0 )
761 // audio time went backwards.
762 // If our output clock is more than a half frame ahead of the
763 // input clock drop this frame to move closer to sync.
764 // Otherwise drop frames until the input clock matches the output clock.
765 if ( sync->first_drop || sync->next_start - buf->start > 90*15 )
767 // Discard data that's in the past.
768 if ( sync->first_drop == 0 )
770 sync->first_drop = sync->next_pts;
773 buf = hb_fifo_get( audio->priv.fifo_raw );
774 hb_buffer_close( &buf );
777 sync->next_pts = buf->start;
779 if ( sync->first_drop )
781 // we were dropping old data but input buf time is now current
782 hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
783 "(next %lld, current %lld)", i,
784 (int)( sync->next_pts - sync->first_drop ) / 90,
785 sync->drop_count, sync->first_drop, sync->next_pts );
786 sync->first_drop = 0;
787 sync->drop_count = 0;
788 sync->next_pts = buf->start;
790 if ( buf->start - sync->next_pts >= (90 * 70) )
792 if ( buf->start - sync->next_pts > (90000LL * 60) )
794 // there's a gap of more than a minute between the last
795 // frame and this. assume we got a corrupted timestamp
796 // and just drop the next buf.
797 hb_log( "sync: %d minute time gap in audio %d - dropping buf"
798 " start %lld, next %lld",
799 (int)((buf->start - sync->next_pts) / (90000*60)),
800 i, buf->start, sync->next_pts );
801 buf = hb_fifo_get( audio->priv.fifo_raw );
802 hb_buffer_close( &buf );
806 * there's a gap of at least 70ms between the last
807 * frame we processed & the next. Fill it with silence.
809 hb_log( "sync: adding %d ms of silence to audio %d"
810 " start %lld, next %lld",
811 (int)((buf->start - sync->next_pts) / 90),
812 i, buf->start, sync->next_pts );
813 InsertSilence( w, i, buf->start - sync->next_pts );
818 * When we get here we've taken care of all the dups and gaps in the
819 * audio stream and are ready to inject the next input frame into
822 buf = hb_fifo_get( audio->priv.fifo_raw );
823 OutputAudioFrame( job, audio, buf, sync, fifo, i );
827 static void InsertSilence( hb_work_object_t * w, int i, int64_t duration )
829 hb_work_private_t * pv = w->private_data;
830 hb_job_t *job = pv->job;
831 hb_sync_audio_t *sync = &pv->sync_audio[i];
835 // to keep pass-thru and regular audio in sync we generate silence in
836 // AC3 frame-sized units. If the silence duration isn't an integer multiple
837 // of the AC3 frame duration we will truncate or round up depending on
838 // which minimizes the timing error.
839 const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
840 sync->audio->config.in.samplerate;
841 int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
843 while ( --frame_count >= 0 )
845 if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
847 buf = hb_buffer_init( sync->ac3_size );
848 buf->start = sync->next_pts;
849 buf->stop = buf->start + frame_dur;
850 memcpy( buf->data, sync->ac3_buf, buf->size );
851 fifo = sync->audio->priv.fifo_out;
855 buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
856 HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
857 sync->audio->config.out.mixdown) );
858 buf->start = sync->next_pts;
859 buf->stop = buf->start + frame_dur;
860 memset( buf->data, 0, buf->size );
861 fifo = sync->audio->priv.fifo_sync;
863 OutputAudioFrame( job, sync->audio, buf, sync, fifo, i );
867 static void UpdateState( hb_work_object_t * w )
869 hb_work_private_t * pv = w->private_data;
872 if( !pv->count_frames )
874 pv->st_first = hb_get_date();
878 if( hb_get_date() > pv->st_dates[3] + 1000 )
880 memmove( &pv->st_dates[0], &pv->st_dates[1],
881 3 * sizeof( uint64_t ) );
882 memmove( &pv->st_counts[0], &pv->st_counts[1],
883 3 * sizeof( uint64_t ) );
884 pv->st_dates[3] = hb_get_date();
885 pv->st_counts[3] = pv->count_frames;
888 #define p state.param.working
889 state.state = HB_STATE_WORKING;
890 p.progress = (float) pv->count_frames / (float) pv->count_frames_max;
891 if( p.progress > 1.0 )
895 p.rate_cur = 1000.0 *
896 (float) ( pv->st_counts[3] - pv->st_counts[0] ) /
897 (float) ( pv->st_dates[3] - pv->st_dates[0] );
898 if( hb_get_date() > pv->st_first + 4000 )
901 p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
902 (float) ( pv->st_dates[3] - pv->st_first );
903 eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
905 p.hours = eta / 3600;
906 p.minutes = ( eta % 3600 ) / 60;
907 p.seconds = eta % 60;
918 hb_set_state( pv->job->h, &state );