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];
63 int64_t audio_passthru_slip;
66 uint64_t st_counts[4];
71 /***********************************************************************
73 **********************************************************************/
74 static void InitAudio( hb_work_object_t * w, int i );
75 static void SyncVideo( hb_work_object_t * w );
76 static void SyncAudio( hb_work_object_t * w, int i );
77 static void InsertSilence( hb_work_object_t * w, int i, int64_t d );
78 static void UpdateState( hb_work_object_t * w );
80 /***********************************************************************
82 ***********************************************************************
83 * Initialize the work object
84 **********************************************************************/
85 int syncInit( hb_work_object_t * w, hb_job_t * job )
87 hb_title_t * title = job->title;
88 hb_chapter_t * chapter;
91 hb_work_private_t * pv;
93 pv = calloc( 1, sizeof( hb_work_private_t ) );
97 pv->pts_offset = INT64_MIN;
99 /* Calculate how many video frames we are expecting */
100 if (job->pts_to_stop)
102 duration = job->pts_to_stop + 90000;
104 else if( job->frame_to_stop )
106 /* Set the duration to a rough estimate */
107 duration = ( job->frame_to_stop / ( job->vrate / job->vrate_base ) ) * 90000;
112 for( i = job->chapter_start; i <= job->chapter_end; i++ )
114 chapter = hb_list_item( title->list_chapter, i - 1 );
115 duration += chapter->duration;
118 /* 1 second safety so we're sure we won't miss anything */
120 pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
122 hb_log( "sync: expecting %d video frames", pv->count_frames_max );
125 /* Initialize libsamplerate for every audio track we have */
126 if ( ! job->indepth_scan )
128 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
130 pv->busy |= ( 1 << (i + 1) );
135 /* Get subtitle info, if any */
136 pv->subtitle = hb_list_item( title->list_subtitle, 0 );
141 /***********************************************************************
143 ***********************************************************************
145 **********************************************************************/
146 void syncClose( hb_work_object_t * w )
148 hb_work_private_t * pv = w->private_data;
149 hb_job_t * job = pv->job;
150 hb_title_t * title = job->title;
151 hb_audio_t * audio = NULL;
156 hb_buffer_close( &pv->cur );
159 hb_log( "sync: got %d frames, %d expected",
160 pv->count_frames, pv->count_frames_max );
162 if (pv->drops || pv->dups )
164 hb_log( "sync: %d frames dropped, %d duplicated", pv->drops, pv->dups );
167 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
169 audio = hb_list_item( title->list_audio, i );
170 if( audio->config.out.codec == HB_ACODEC_AC3 )
172 free( pv->sync_audio[i].ac3_buf );
176 src_delete( pv->sync_audio[i].state );
181 w->private_data = NULL;
184 /***********************************************************************
186 ***********************************************************************
187 * The root routine of this work abject
189 * The way this works is that we are syncing the audio to the PTS of
190 * the last video that we processed. That's why we skip the audio sync
191 * if we haven't got a valid PTS from the video yet.
193 **********************************************************************/
194 int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
195 hb_buffer_t ** unused2 )
197 hb_work_private_t * pv = w->private_data;
203 for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
205 if ( pv->busy & ( 1 << (i + 1) ) )
209 return ( pv->busy? HB_WORK_OK : HB_WORK_DONE );
212 hb_work_object_t hb_sync =
221 static void InitAudio( hb_work_object_t * w, int i )
223 hb_work_private_t * pv = w->private_data;
224 hb_job_t * job = pv->job;
225 hb_title_t * title = job->title;
226 hb_sync_audio_t * sync;
228 sync = &pv->sync_audio[i];
229 sync->audio = hb_list_item( title->list_audio, i );
231 if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
233 /* Have a silent AC-3 frame ready in case we have to fill a
239 codec = avcodec_find_encoder( CODEC_ID_AC3 );
240 c = avcodec_alloc_context();
242 c->bit_rate = sync->audio->config.in.bitrate;
243 c->sample_rate = sync->audio->config.in.samplerate;
244 c->channels = HB_INPUT_CH_LAYOUT_GET_DISCRETE_COUNT( sync->audio->config.in.channel_layout );
246 if( hb_avcodec_open( c, codec ) < 0 )
248 hb_log( "sync: avcodec_open failed" );
252 zeros = calloc( AC3_SAMPLES_PER_FRAME *
253 sizeof( short ) * c->channels, 1 );
254 sync->ac3_size = sync->audio->config.in.bitrate * AC3_SAMPLES_PER_FRAME /
255 sync->audio->config.in.samplerate / 8;
256 sync->ac3_buf = malloc( sync->ac3_size );
258 if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
259 zeros ) != sync->ac3_size )
261 hb_log( "sync: avcodec_encode_audio failed" );
265 hb_avcodec_close( c );
270 /* Initialize libsamplerate */
272 sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->config.out.mixdown), &error );
273 sync->data.end_of_input = 0;
277 /***********************************************************************
279 ***********************************************************************
281 **********************************************************************/
282 static void SyncVideo( hb_work_object_t * w )
284 hb_work_private_t * pv = w->private_data;
285 hb_buffer_t * cur, * next, * sub = NULL;
286 hb_job_t * job = pv->job;
288 if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
290 /* We haven't even got a frame yet */
296 /* we got an end-of-stream. Feed it downstream & signal that we're done. */
297 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
302 /* At this point we have a frame to process. Let's check
303 1) if we will be able to push into the fifo ahead
304 2) if the next frame is there already, since we need it to
305 compute the duration of the current frame*/
306 while( !hb_fifo_is_full( job->fifo_sync ) &&
307 ( next = hb_fifo_see( job->fifo_raw ) ) )
309 hb_buffer_t * buf_tmp;
311 if( next->size == 0 )
313 /* we got an end-of-stream. Feed it downstream & signal that
314 * we're done. Note that this means we drop the final frame of
315 * video (we don't know its duration). On DVDs the final frame
316 * is often strange and dropping it seems to be a good idea. */
317 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
321 if( pv->pts_offset == INT64_MIN )
323 /* This is our first frame */
325 if ( cur->start != 0 )
328 * The first pts from a dvd should always be zero but
329 * can be non-zero with a transport or program stream since
330 * we're not guaranteed to start on an IDR frame. If we get
331 * a non-zero initial PTS extend its duration so it behaves
332 * as if it started at zero so that our audio timing will
335 hb_log( "sync: first pts is %lld", cur->start );
340 if( cur->new_chap ) {
341 hb_log("sync got new chapter %d", cur->new_chap );
345 * since the first frame is always 0 and the upstream reader code
346 * is taking care of adjusting for pts discontinuities, we just have
347 * to deal with the next frame's start being in the past. This can
348 * happen when the PTS is adjusted after data loss but video frame
349 * reordering causes some frames with the old clock to appear after
350 * the clock change. This creates frames that overlap in time which
351 * looks to us like time going backward. The downstream muxing code
352 * can deal with overlaps of up to a frame time but anything larger
353 * we handle by dropping frames here.
355 if ( (int64_t)( next->start - cur->start ) <= 0 ||
356 (int64_t)( (cur->start - pv->audio_passthru_slip ) - pv->next_pts ) < 0 )
358 if ( pv->first_drop == 0 )
360 pv->first_drop = next->start;
363 buf_tmp = hb_fifo_get( job->fifo_raw );
364 if ( buf_tmp->new_chap )
366 // don't drop a chapter mark when we drop the buffer
367 pv->chap_mark = buf_tmp->new_chap;
369 hb_buffer_close( &buf_tmp );
372 if ( pv->first_drop )
374 hb_log( "sync: video time didn't advance - dropped %d frames "
375 "(delta %d ms, current %lld, next %lld, dur %d)",
376 pv->drop_count, (int)( cur->start - pv->first_drop ) / 90,
377 cur->start, next->start, (int)( next->start - cur->start ) );
383 * Track the video sequence number localy so that we can sync the audio
384 * to it using the sequence number as well as the PTS.
386 pv->video_sequence = cur->sequence;
388 /* Look for a subtitle for this frame */
392 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
394 /* If two subtitles overlap, make the first one stop
395 when the second one starts */
396 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
397 if( sub2 && sub->stop > sub2->start )
398 sub->stop = sub2->start;
400 // hb_log("0x%x: video seq: %lld subtitle sequence: %lld",
401 // sub, cur->sequence, sub->sequence);
403 if( sub->sequence > cur->sequence )
406 * The video is behind where we are, so wait until
407 * it catches up to the same reader point on the
408 * DVD. Then our PTS should be in the same region
415 if( sub->stop > cur->start ) {
417 * The stop time is in the future, so fall through
418 * and we'll deal with it in the next block of
425 * The subtitle is older than this picture, trash it
427 sub = hb_fifo_get( pv->subtitle->fifo_raw );
428 hb_buffer_close( &sub );
432 * There is a valid subtitle, is it time to display it?
436 if( sub->stop > sub->start)
439 * Normal subtitle which ends after it starts, check to
440 * see that the current video is between the start and end.
442 if( cur->start > sub->start &&
443 cur->start < sub->stop )
446 * We should be playing this, so leave the
449 * fall through to display
451 if( ( sub->stop - sub->start ) < ( 3 * 90000 ) )
454 * Subtitle is on for less than three seconds, extend
455 * the time that it is displayed to make it easier
456 * to read. Make it 3 seconds or until the next
457 * subtitle is displayed.
459 * This is in response to Indochine which only
460 * displays subs for 1 second - too fast to read.
462 sub->stop = sub->start + ( 3 * 90000 );
464 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
466 if( sub2 && sub->stop > sub2->start )
468 sub->stop = sub2->start;
475 * Defer until the play point is within the subtitle
483 * The end of the subtitle is less than the start, this is a
484 * sign of a PTS discontinuity.
486 if( sub->start > cur->start )
489 * we haven't reached the start time yet, or
490 * we have jumped backwards after having
491 * already started this subtitle.
493 if( cur->start < sub->stop )
496 * We have jumped backwards and so should
497 * continue displaying this subtitle.
499 * fall through to display.
505 * Defer until the play point is within the subtitle
511 * Play this subtitle as the start is greater than our
514 * fall through to display/
522 if ( job->mux & HB_MUX_AVI || job->cfr )
525 * The concept of variable frame rate video was a bit too advanced
526 * for Microsoft so AVI doesn't support it. Since almost all dvd
527 * video is VFR we have to convert it to constant frame rate to
528 * put it in an AVI container. So here we duplicate, drop and
529 * otherwise trash video frames to appease the gods of Redmond.
532 /* mpeg durations are exact when expressed in ticks of the
533 * 27MHz System clock but not in HB's 90KHz PTS clock. To avoid
534 * a truncation bias that will eventually cause the audio to desync
535 * we compute the duration of the next frame using 27MHz ticks
536 * then truncate it to 90KHz. */
537 duration = ( (int64_t)(pv->count_frames + 1 ) * job->vrate_base ) / 300 -
540 /* We don't want the input & output clocks to be exactly in phase
541 * otherwise small variations in the time will cause us to think
542 * we're a full frame off & there will be lots of drops and dups.
543 * We offset the input clock by half the duration so it's maximally
544 * out of phase with the output clock. */
545 if( cur->start < pv->next_start - ( duration >> 1 ) )
547 /* current frame too old - drop it */
550 pv->chap_mark = cur->new_chap;
552 hb_buffer_close( &cur );
553 pv->cur = cur = hb_fifo_get( job->fifo_raw );
554 pv->next_pts = next->start;
559 if( next->start > pv->next_start + duration + ( duration >> 1 ) )
561 /* next frame too far ahead - dup current frame */
562 buf_tmp = hb_buffer_init( cur->size );
563 hb_buffer_copy_settings( buf_tmp, cur );
564 memcpy( buf_tmp->data, cur->data, cur->size );
565 buf_tmp->sequence = cur->sequence;
570 /* this frame in our time window & doesn't need to be duped */
572 pv->cur = cur = hb_fifo_get( job->fifo_raw );
573 pv->next_pts = next->start;
579 * Adjust the pts of the current frame so that it's contiguous
580 * with the previous frame. The start time of the current frame
581 * has to be the end time of the previous frame and the stop
582 * time has to be the start of the next frame. We don't
583 * make any adjustments to the source timestamps other than removing
584 * the clock offsets (which also removes pts discontinuities).
585 * This means we automatically encode at the source's frame rate.
586 * MP2 uses an implicit duration (frames end when the next frame
587 * starts) but more advanced containers like MP4 use an explicit
588 * duration. Since we're looking ahead one frame we set the
589 * explicit stop time from the start time of the next frame.
592 pv->cur = cur = hb_fifo_get( job->fifo_raw );
593 pv->next_pts = cur->start;
594 duration = cur->start - buf_tmp->start;
597 hb_log( "sync: invalid video duration %lld, start %lld, next %lld",
598 duration, buf_tmp->start, next->start );
602 buf_tmp->start = pv->next_start;
603 pv->next_start += duration;
604 buf_tmp->stop = pv->next_start;
608 // we have a pending chapter mark from a recent drop - put it on this
609 // buffer (this may make it one frame late but we can't do any better).
610 buf_tmp->new_chap = pv->chap_mark;
614 /* If we have a subtitle for this picture, copy it */
615 /* FIXME: we should avoid this memcpy */
618 buf_tmp->sub = hb_buffer_init( sub->size );
619 buf_tmp->sub->x = sub->x;
620 buf_tmp->sub->y = sub->y;
621 buf_tmp->sub->width = sub->width;
622 buf_tmp->sub->height = sub->height;
623 memcpy( buf_tmp->sub->data, sub->data, sub->size );
626 /* Push the frame to the renderer */
627 hb_fifo_push( job->fifo_sync, buf_tmp );
632 if( job->frame_to_stop && pv->count_frames > job->frame_to_stop )
634 // Drop an empty buffer into our output to ensure that things
635 // get flushed all the way out.
636 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
638 hb_log( "sync: reached %d frames, exiting early (%i busy)",
639 pv->count_frames, pv->busy );
643 /* Make sure we won't get more frames then expected */
644 if( pv->count_frames >= pv->count_frames_max * 2)
646 hb_log( "sync: got too many frames (%d), exiting early",
649 // Drop an empty buffer into our output to ensure that things
650 // get flushed all the way out.
651 hb_fifo_push( job->fifo_sync, hb_buffer_init( 0 ) );
658 static void OutputAudioFrame( hb_job_t *job, hb_audio_t *audio, hb_buffer_t *buf,
659 hb_sync_audio_t *sync, hb_fifo_t *fifo, int i )
661 int64_t start = sync->next_start;
662 int64_t duration = buf->stop - buf->start;
664 sync->next_pts += duration;
666 if( audio->config.in.samplerate == audio->config.out.samplerate ||
667 audio->config.out.codec == HB_ACODEC_AC3 ||
668 audio->config.out.codec == HB_ACODEC_DCA )
671 * If we don't have to do sample rate conversion or this audio is
672 * pass-thru just send the input buffer downstream after adjusting
673 * its timestamps to make the output stream continuous.
678 /* Not pass-thru - do sample rate conversion */
679 int count_in, count_out;
680 hb_buffer_t * buf_raw = buf;
681 int channel_count = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown) *
684 count_in = buf_raw->size / channel_count;
686 * When using stupid rates like 44.1 there will always be some
687 * truncation error. E.g., a 1536 sample AC3 frame will turn into a
688 * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2
689 * the error will build up over time and eventually the audio will
690 * substantially lag the video. libsamplerate will keep track of the
691 * fractional sample & give it to us when appropriate if we give it
692 * an extra sample of space in the output buffer.
694 count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1;
696 sync->data.input_frames = count_in;
697 sync->data.output_frames = count_out;
698 sync->data.src_ratio = (double)audio->config.out.samplerate /
699 (double)audio->config.in.samplerate;
701 buf = hb_buffer_init( count_out * channel_count );
702 sync->data.data_in = (float *) buf_raw->data;
703 sync->data.data_out = (float *) buf->data;
704 if( src_process( sync->state, &sync->data ) )
706 /* XXX If this happens, we're screwed */
707 hb_log( "sync: audio %d src_process failed", i );
709 hb_buffer_close( &buf_raw );
711 buf->size = sync->data.output_frames_gen * channel_count;
712 duration = ( sync->data.output_frames_gen * 90000 ) /
713 audio->config.out.samplerate;
715 buf->frametype = HB_FRAME_AUDIO;
717 buf->stop = start + duration;
718 sync->next_start = start + duration;
719 hb_fifo_push( fifo, buf );
722 /***********************************************************************
724 ***********************************************************************
726 **********************************************************************/
727 static void SyncAudio( hb_work_object_t * w, int i )
729 hb_work_private_t * pv = w->private_data;
730 hb_job_t * job = pv->job;
731 hb_sync_audio_t * sync = &pv->sync_audio[i];
732 hb_audio_t * audio = sync->audio;
737 if( audio->config.out.codec == HB_ACODEC_AC3 ||
738 audio->config.out.codec == HB_ACODEC_DCA )
740 fifo = audio->priv.fifo_out;
744 fifo = audio->priv.fifo_sync;
747 while( !hb_fifo_is_full( fifo ) && ( buf = hb_fifo_see( audio->priv.fifo_raw ) ) )
749 start = buf->start - pv->audio_passthru_slip;
750 /* if the next buffer is an eof send it downstream */
751 if ( buf->size <= 0 )
753 buf = hb_fifo_get( audio->priv.fifo_raw );
754 hb_fifo_push( fifo, buf );
755 pv->busy &=~ (1 << (i + 1) );
758 if( job->frame_to_stop && pv->count_frames >= job->frame_to_stop )
760 hb_fifo_push( fifo, hb_buffer_init(0) );
761 pv->busy &=~ (1 << (i + 1) );
764 if ( (int64_t)( start - sync->next_pts ) < 0 )
766 // audio time went backwards.
767 // If our output clock is more than a half frame ahead of the
768 // input clock drop this frame to move closer to sync.
769 // Otherwise drop frames until the input clock matches the output clock.
770 if ( sync->first_drop || sync->next_start - start > 90*15 )
772 // Discard data that's in the past.
773 if ( sync->first_drop == 0 )
775 sync->first_drop = sync->next_pts;
778 buf = hb_fifo_get( audio->priv.fifo_raw );
779 hb_buffer_close( &buf );
782 sync->next_pts = start;
784 if ( sync->first_drop )
786 // we were dropping old data but input buf time is now current
787 hb_log( "sync: audio %d time went backwards %d ms, dropped %d frames "
788 "(next %lld, current %lld)", i,
789 (int)( sync->next_pts - sync->first_drop ) / 90,
790 sync->drop_count, sync->first_drop, sync->next_pts );
791 sync->first_drop = 0;
792 sync->drop_count = 0;
793 sync->next_pts = start;
795 if ( start - sync->next_pts >= (90 * 70) )
797 if ( start - sync->next_pts > (90000LL * 60) )
799 // there's a gap of more than a minute between the last
800 // frame and this. assume we got a corrupted timestamp
801 // and just drop the next buf.
802 hb_log( "sync: %d minute time gap in audio %d - dropping buf"
803 " start %lld, next %lld",
804 (int)((start - sync->next_pts) / (90000*60)),
805 i, start, sync->next_pts );
806 buf = hb_fifo_get( audio->priv.fifo_raw );
807 hb_buffer_close( &buf );
811 * there's a gap of at least 70ms between the last
812 * frame we processed & the next. Fill it with silence.
813 * Or in the case of DCA, skip some frames from the
816 if( sync->audio->config.out.codec == HB_ACODEC_DCA )
818 hb_log( "sync: audio gap %d ms. Skipping frames. Audio %d"
819 " start %lld, next %lld",
820 (int)((start - sync->next_pts) / 90),
821 i, start, sync->next_pts );
822 pv->audio_passthru_slip += (start - sync->next_pts);
825 hb_log( "sync: adding %d ms of silence to audio %d"
826 " start %lld, next %lld",
827 (int)((start - sync->next_pts) / 90),
828 i, start, sync->next_pts );
829 InsertSilence( w, i, start - sync->next_pts );
834 * When we get here we've taken care of all the dups and gaps in the
835 * audio stream and are ready to inject the next input frame into
838 buf = hb_fifo_get( audio->priv.fifo_raw );
839 OutputAudioFrame( job, audio, buf, sync, fifo, i );
843 static void InsertSilence( hb_work_object_t * w, int i, int64_t duration )
845 hb_work_private_t * pv = w->private_data;
846 hb_job_t *job = pv->job;
847 hb_sync_audio_t *sync = &pv->sync_audio[i];
851 // to keep pass-thru and regular audio in sync we generate silence in
852 // AC3 frame-sized units. If the silence duration isn't an integer multiple
853 // of the AC3 frame duration we will truncate or round up depending on
854 // which minimizes the timing error.
855 const int frame_dur = ( 90000 * AC3_SAMPLES_PER_FRAME ) /
856 sync->audio->config.in.samplerate;
857 int frame_count = ( duration + (frame_dur >> 1) ) / frame_dur;
859 while ( --frame_count >= 0 )
861 if( sync->audio->config.out.codec == HB_ACODEC_AC3 )
863 buf = hb_buffer_init( sync->ac3_size );
864 buf->start = sync->next_pts;
865 buf->stop = buf->start + frame_dur;
866 memcpy( buf->data, sync->ac3_buf, buf->size );
867 fifo = sync->audio->priv.fifo_out;
871 buf = hb_buffer_init( AC3_SAMPLES_PER_FRAME * sizeof( float ) *
872 HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(
873 sync->audio->config.out.mixdown) );
874 buf->start = sync->next_pts;
875 buf->stop = buf->start + frame_dur;
876 memset( buf->data, 0, buf->size );
877 fifo = sync->audio->priv.fifo_sync;
879 OutputAudioFrame( job, sync->audio, buf, sync, fifo, i );
883 static void UpdateState( hb_work_object_t * w )
885 hb_work_private_t * pv = w->private_data;
888 if( !pv->count_frames )
890 pv->st_first = hb_get_date();
894 if( hb_get_date() > pv->st_dates[3] + 1000 )
896 memmove( &pv->st_dates[0], &pv->st_dates[1],
897 3 * sizeof( uint64_t ) );
898 memmove( &pv->st_counts[0], &pv->st_counts[1],
899 3 * sizeof( uint64_t ) );
900 pv->st_dates[3] = hb_get_date();
901 pv->st_counts[3] = pv->count_frames;
904 #define p state.param.working
905 state.state = HB_STATE_WORKING;
906 p.progress = (float) pv->count_frames / (float) pv->count_frames_max;
907 if( p.progress > 1.0 )
911 p.rate_cur = 1000.0 *
912 (float) ( pv->st_counts[3] - pv->st_counts[0] ) /
913 (float) ( pv->st_dates[3] - pv->st_dates[0] );
914 if( hb_get_date() > pv->st_first + 4000 )
917 p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
918 (float) ( pv->st_dates[3] - pv->st_first );
919 eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
921 p.hours = eta / 3600;
922 p.minutes = ( eta % 3600 ) / 60;
923 p.seconds = eta % 60;
934 hb_set_state( pv->job->h, &state );