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;
45 int64_t count_frames_max;
46 int64_t video_sequence;
47 hb_buffer_t * cur; /* The next picture to process */
50 hb_sync_audio_t sync_audio[8];
56 uint64_t st_counts[4];
60 /* Throttle message flags */
62 int inserting_silence;
66 /***********************************************************************
68 **********************************************************************/
69 static void InitAudio( hb_work_object_t * w, int i );
70 static int SyncVideo( hb_work_object_t * w );
71 static void SyncAudio( hb_work_object_t * w, int i );
72 static int NeedSilence( hb_work_object_t * w, hb_audio_t * );
73 static void InsertSilence( hb_work_object_t * w, int i );
74 static void UpdateState( hb_work_object_t * w );
76 /***********************************************************************
78 ***********************************************************************
79 * Initialize the work object
80 **********************************************************************/
81 int syncInit( hb_work_object_t * w, hb_job_t * job )
83 hb_title_t * title = job->title;
84 hb_chapter_t * chapter;
87 hb_work_private_t * pv;
89 pv = calloc( 1, sizeof( hb_work_private_t ) );
93 pv->pts_offset = INT64_MIN;
94 pv->pts_offset_old = INT64_MIN;
97 pv->discontinuity = 0;
99 pv->trashing_audio = 0;
100 pv->inserting_silence = 0;
101 pv->way_out_of_sync = 0;
103 /* Calculate how many video frames we are expecting */
105 for( i = job->chapter_start; i <= job->chapter_end; i++ )
107 chapter = hb_list_item( title->list_chapter, i - 1 );
108 duration += chapter->duration;
111 /* 1 second safety so we're sure we won't miss anything */
112 pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000;
114 hb_log( "sync: expecting %lld video frames", pv->count_frames_max );
116 /* Initialize libsamplerate for every audio track we have */
117 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
122 /* Get subtitle info, if any */
123 pv->subtitle = hb_list_item( title->list_subtitle, 0 );
125 pv->video_sequence = 0;
130 /***********************************************************************
132 ***********************************************************************
134 **********************************************************************/
135 void syncClose( hb_work_object_t * w )
137 hb_work_private_t * pv = w->private_data;
138 hb_job_t * job = pv->job;
139 hb_title_t * title = job->title;
143 if( pv->cur ) hb_buffer_close( &pv->cur );
145 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
147 if( job->acodec & HB_ACODEC_AC3 )
149 free( pv->sync_audio[i].ac3_buf );
153 src_delete( pv->sync_audio[i].state );
158 w->private_data = NULL;
161 /***********************************************************************
163 ***********************************************************************
164 * The root routine of this work abject
166 * The way this works is that we are syncing the audio to the PTS of
167 * the last video that we processed. That's why we skip the audio sync
168 * if we haven't got a valid PTS from the video yet.
170 **********************************************************************/
171 int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1,
172 hb_buffer_t ** unused2 )
174 hb_work_private_t * pv = w->private_data;
177 /* If we ever got a video frame, handle audio now */
178 if( pv->pts_offset != INT64_MIN )
180 for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ )
187 return SyncVideo( w );
190 hb_work_object_t hb_sync =
199 static void InitAudio( hb_work_object_t * w, int i )
201 hb_work_private_t * pv = w->private_data;
202 hb_job_t * job = pv->job;
203 hb_title_t * title = job->title;
204 hb_sync_audio_t * sync;
206 sync = &pv->sync_audio[i];
207 sync->audio = hb_list_item( title->list_audio, i );
209 if( job->acodec & HB_ACODEC_AC3 )
211 /* Have a silent AC-3 frame ready in case we have to fill a
217 codec = avcodec_find_encoder( CODEC_ID_AC3 );
218 c = avcodec_alloc_context();
220 c->bit_rate = sync->audio->bitrate;
221 c->sample_rate = sync->audio->rate;
224 if( avcodec_open( c, codec ) < 0 )
226 hb_log( "sync: avcodec_open failed" );
230 zeros = calloc( AC3_SAMPLES_PER_FRAME *
231 sizeof( short ) * c->channels, 1 );
232 sync->ac3_size = sync->audio->bitrate * AC3_SAMPLES_PER_FRAME /
233 sync->audio->rate / 8;
234 sync->ac3_buf = malloc( sync->ac3_size );
236 if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size,
237 zeros ) != sync->ac3_size )
239 hb_log( "sync: avcodec_encode_audio failed" );
248 /* Initialize libsamplerate */
250 sync->state = src_new( SRC_LINEAR, HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown), &error );
251 sync->data.end_of_input = 0;
257 #define PTS_DISCONTINUITY_TOLERANCE 90000
259 /***********************************************************************
261 ***********************************************************************
263 **********************************************************************/
264 static int SyncVideo( hb_work_object_t * w )
266 hb_work_private_t * pv = w->private_data;
267 hb_buffer_t * cur, * next, * sub = NULL;
268 hb_job_t * job = pv->job;
269 int64_t pts_expected;
277 if( hb_thread_has_exited( job->reader ) &&
278 !hb_fifo_size( job->fifo_mpeg2 ) &&
279 !hb_fifo_size( job->fifo_raw ) )
281 /* All video data has been processed already, we won't get
283 hb_log( "sync: got %lld frames, %lld expected",
284 pv->count_frames, pv->count_frames_max );
287 hb_buffer_t * buf_tmp;
289 // Drop an empty buffer into our output to ensure that things
290 // get flushed all the way out.
291 buf_tmp = hb_buffer_init(0); // Empty end buffer
292 hb_fifo_push( job->fifo_sync, buf_tmp );
297 if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) )
299 /* We haven't even got a frame yet */
304 /* At this point we have a frame to process. Let's check
305 1) if we will be able to push into the fifo ahead
306 2) if the next frame is there already, since we need it to
307 know whether we'll have to repeat the current frame or not */
308 while( !hb_fifo_is_full( job->fifo_sync ) &&
309 ( next = hb_fifo_see( job->fifo_raw ) ) )
311 hb_buffer_t * buf_tmp;
313 if( pv->pts_offset == INT64_MIN )
315 /* This is our first frame */
316 hb_log( "sync: first pts is %lld", cur->start );
317 pv->pts_offset = cur->start;
321 * Track the video sequence number localy so that we can sync the audio
322 * to it using the sequence number as well as the PTS.
324 pv->video_sequence = cur->sequence;
326 /* Check for PTS jumps over 0.5 second */
327 if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE ||
328 next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE )
330 hb_log( "Sync: Video PTS discontinuity %s (current buffer start=%lld, next buffer start=%lld)",
331 pv->discontinuity ? "second" : "first", cur->start, next->start );
334 * Do we need to trash the subtitle, is it from the next->start period
335 * or is it from our old position. If the latter then trash it.
339 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
341 if( ( sub->start > ( cur->start - PTS_DISCONTINUITY_TOLERANCE ) ) &&
342 ( sub->start < ( cur->start + PTS_DISCONTINUITY_TOLERANCE ) ) )
345 * The subtitle is from our current time region which we are
346 * jumping from. So trash it as we are about to jump backwards
347 * or forwards and don't want it blocking the subtitle fifo.
349 hb_log("Trashing subtitle 0x%x due to PTS discontinuity", sub);
350 sub = hb_fifo_get( pv->subtitle->fifo_raw );
351 hb_buffer_close( &sub );
358 /* Trash current picture */
359 /* Also, make sure we don't trash a chapter break */
360 chap_break = cur->new_chap;
361 hb_buffer_close( &cur );
362 pv->cur = cur = hb_fifo_get( job->fifo_raw );
363 cur->new_chap |= chap_break; // Don't stomp existing chapter breaks
365 /* Calculate new offset */
366 pv->pts_offset_old = pv->pts_offset;
369 pv->pts_offset = cur->start - pv->next_start;
371 pv->pts_offset = cur->start -
372 pv->count_frames * pv->job->vrate_base / 300;
375 if( !pv->discontinuity )
377 pv->discontinuity = 1;
380 pv->video_sequence = cur->sequence;
384 /* Look for a subtitle for this frame */
388 while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) )
390 /* If two subtitles overlap, make the first one stop
391 when the second one starts */
392 sub2 = hb_fifo_see2( pv->subtitle->fifo_raw );
393 if( sub2 && sub->stop > sub2->start )
394 sub->stop = sub2->start;
396 // hb_log("0x%x: video seq: %lld subtitle sequence: %lld",
397 // sub, cur->sequence, sub->sequence);
399 if( sub->sequence > cur->sequence )
402 * The video is behind where we are, so wait until
403 * it catches up to the same reader point on the
404 * DVD. Then our PTS should be in the same region
411 if( sub->stop > cur->start ) {
413 * The stop time is in the future, so fall through
414 * and we'll deal with it in the next block of
422 * The stop time is in the past. But is it due to
423 * it having been played already, or has the PTS
426 if( ( cur->start - sub->stop ) > PTS_DISCONTINUITY_TOLERANCE ) {
428 * There is a lot of time between our current
429 * video and where this subtitle is ending,
430 * assume that we are about to reset the PTS
431 * and do not throw away this subtitle.
438 * The subtitle is older than this picture, trash it
440 sub = hb_fifo_get( pv->subtitle->fifo_raw );
441 hb_buffer_close( &sub );
445 * There is a valid subtitle, is it time to display it?
449 if( sub->stop > sub->start)
452 * Normal subtitle which ends after it starts, check to
453 * see that the current video is between the start and end.
455 if( cur->start > sub->start &&
456 cur->start < sub->stop )
459 * We should be playing this, so leave the
462 * fall through to display
468 * Defer until the play point is within the subtitle
476 * The end of the subtitle is less than the start, this is a
477 * sign of a PTS discontinuity.
479 if( sub->start > cur->start )
482 * we haven't reached the start time yet, or
483 * we have jumped backwards after having
484 * already started this subtitle.
486 if( cur->start < sub->stop )
489 * We have jumped backwards and so should
490 * continue displaying this subtitle.
492 * fall through to display.
498 * Defer until the play point is within the subtitle
504 * Play this subtitle as the start is greater than our
507 * fall through to display/
517 * adjust the pts of the current frame so that it's contiguous
518 * with the previous frame. pts_offset tracks the time difference
519 * between the pts values in the input content (which start at some
520 * random time) and our timestamps (which start at zero). We don't
521 * make any adjustments to the source timestamps other than removing
522 * the clock offsets (which also removes pts discontinuities).
523 * This means we automatically encode at the source's frame rate.
524 * MP2 uses an implicit duration (frames end when the next frame
525 * starts) but more advanced containers like MP4 use an explicit
526 * duration. Since we're looking ahead one frame we set the
527 * explicit stop time from the start time of the next frame.
530 pv->cur = cur = hb_fifo_get( job->fifo_raw );
531 buf_tmp->start = pv->next_start;
532 pv->next_start = next->start - pv->pts_offset;
533 buf_tmp->stop = pv->next_start;
537 /* The PTS of the frame we are expecting now */
538 pts_expected = pv->pts_offset +
539 pv->count_frames * pv->job->vrate_base / 300;
541 //hb_log("Video expecting PTS %lld, current frame: %lld, next frame: %lld, cf: %lld",
542 // pts_expected, cur->start, next->start, pv->count_frames * pv->job->vrate_base / 300 );
544 if( cur->start < pts_expected - pv->job->vrate_base / 300 / 2 &&
545 next->start < pts_expected + pv->job->vrate_base / 300 / 2 )
547 /* The current frame is too old but the next one matches,
549 /* Also, make sure we don't trash a chapter break */
550 chap_break = cur->new_chap;
551 hb_buffer_close( &cur );
552 pv->cur = cur = hb_fifo_get( job->fifo_raw );
553 cur->new_chap |= chap_break; // Make sure we don't stomp the existing one.
558 if( next->start > pts_expected + 3 * pv->job->vrate_base / 300 / 2 )
560 /* We'll need the current frame more than one time. Make a
561 copy of it and keep it */
562 buf_tmp = hb_buffer_init( cur->size );
563 memcpy( buf_tmp->data, cur->data, cur->size );
564 buf_tmp->sequence = cur->sequence;
568 /* The frame has the expected date and won't have to be
569 duplicated, just put it through */
571 pv->cur = cur = hb_fifo_get( job->fifo_raw );
574 /* Replace those MPEG-2 dates with our dates */
575 buf_tmp->start = (uint64_t) pv->count_frames *
576 pv->job->vrate_base / 300;
577 buf_tmp->stop = (uint64_t) ( pv->count_frames + 1 ) *
578 pv->job->vrate_base / 300;
581 /* If we have a subtitle for this picture, copy it */
582 /* FIXME: we should avoid this memcpy */
585 buf_tmp->sub = hb_buffer_init( sub->size );
586 buf_tmp->sub->x = sub->x;
587 buf_tmp->sub->y = sub->y;
588 buf_tmp->sub->width = sub->width;
589 buf_tmp->sub->height = sub->height;
590 memcpy( buf_tmp->sub->data, sub->data, sub->size );
593 /* Push the frame to the renderer */
594 hb_fifo_push( job->fifo_sync, buf_tmp );
599 /* Make sure we won't get more frames then expected */
600 if( pv->count_frames >= pv->count_frames_max * 2)
602 hb_log( "sync: got too many frames (%lld), exiting early", pv->count_frames );
605 // Drop an empty buffer into our output to ensure that things
606 // get flushed all the way out.
607 buf_tmp = hb_buffer_init(0); // Empty end buffer
608 hb_fifo_push( job->fifo_sync, buf_tmp );
617 /***********************************************************************
619 ***********************************************************************
621 **********************************************************************/
622 static void SyncAudio( hb_work_object_t * w, int i )
624 hb_work_private_t * pv = w->private_data;
628 hb_sync_audio_t * sync;
633 int64_t pts_expected;
637 sync = &pv->sync_audio[i];
640 if( job->acodec & HB_ACODEC_AC3 )
642 fifo = audio->fifo_out;
647 fifo = audio->fifo_sync;
651 while( !hb_fifo_is_full( fifo ) &&
652 ( buf = hb_fifo_see( audio->fifo_raw ) ) )
654 /* The PTS of the samples we are expecting now */
655 pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
657 // hb_log("Video Sequence: %lld, Audio Sequence: %lld", pv->video_sequence, buf->sequence);
660 * Using the same logic as the Video have we crossed a VOB
661 * boundary as detected by the expected PTS and the PTS of our
662 * audio being out by more than the tolerance value.
664 if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
665 buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
667 /* There has been a PTS discontinuity, and this frame might
668 be from before the discontinuity*/
670 if( pv->discontinuity )
673 * There is an outstanding discontinuity, so use the offset from
674 * that discontinuity.
676 pts_expected = pv->pts_offset_old + sync->count_frames *
682 * No outstanding discontinuity, so the audio must be leading the
683 * video (or the PTS values are really stuffed). So lets mark this
684 * as a discontinuity ourselves for the audio to use until
685 * the video also crosses the discontinuity.
687 * pts_offset is used when we are in the same time space as the video
688 * pts_offset_old when in a discontinuity.
690 * Therefore set the pts_offset_old given the new pts_offset for this
693 pv->discontinuity = 1;
694 pv->pts_offset_old = buf->start - sync->count_frames *
696 pts_expected = pv->pts_offset_old + sync->count_frames *
699 hb_log("Sync: Audio discontinuity (sequence: vid %lld aud %lld) (pts %lld < %lld < %lld)",
700 pv->video_sequence, buf->sequence,
701 pts_expected - PTS_DISCONTINUITY_TOLERANCE, buf->start,
702 pts_expected + PTS_DISCONTINUITY_TOLERANCE );
706 * Is the audio from a valid period given the previous
707 * Video PTS. I.e. has there just been a video PTS
708 * discontinuity and this audio belongs to the vdeo from
711 if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE ||
712 buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE )
715 * It's outside of our tolerance for where the video
716 * is now, and it's outside of the tolerance for
717 * where we have been in the case of a VOB change.
718 * Try and reconverge regardless. so continue on to
719 * our convergence code below which will kick in as
720 * it will be more than 100ms out.
722 * Note that trashing the Audio could make things
723 * worse if the Audio is in front because we will end
724 * up diverging even more. We need to hold on to the
725 * audio until the video catches up.
727 if( !pv->way_out_of_sync )
729 hb_log("Sync: Audio is way out of sync, attempt to reconverge from current video PTS");
730 pv->way_out_of_sync = 1;
734 * It wasn't from the old place, so we must be from
735 * the new, but just too far out. So attempt to
736 * reconverge by resetting the point we want to be to
737 * where we are currently wanting to be.
739 pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate;
740 start = pts_expected - pv->pts_offset;
742 /* Use the older offset */
743 start = pts_expected - pv->pts_offset_old;
748 start = pts_expected - pv->pts_offset;
750 if( pv->discontinuity )
753 * The Audio is tracking the Video again using the normal pts_offset, so the
754 * discontinuity is over.
756 hb_log( "Sync: Audio joined Video after discontinuity at PTS %lld", buf->start );
757 pv->discontinuity = 0;
761 /* Tolerance: 100 ms */
762 if( buf->start < pts_expected - 9000 )
764 if( !pv->trashing_audio )
766 /* Audio is behind the Video, trash it, can't use it now. */
767 hb_log( "Sync: Audio PTS (%lld) < Video PTS (%lld) by greater than 100ms, trashing audio to reconverge",
768 buf->start, pts_expected);
769 pv->trashing_audio = 1;
771 buf = hb_fifo_get( audio->fifo_raw );
772 hb_buffer_close( &buf );
775 else if( buf->start > pts_expected + 9000 )
777 /* Audio is ahead of the Video, insert silence until we catch up*/
778 if( !pv->inserting_silence )
780 hb_log("Sync: Audio PTS (%lld) > Video PTS (%lld) by greater than 100ms insert silence until reconverged", buf->start, pts_expected);
781 pv->inserting_silence = 1;
783 InsertSilence( w, i );
788 if( pv->trashing_audio || pv->inserting_silence )
790 hb_log( "Sync: Audio back in Sync at PTS %lld", buf->start );
791 pv->trashing_audio = 0;
792 pv->inserting_silence = 0;
794 if( pv->way_out_of_sync )
796 hb_log( "Sync: Audio no longer way out of sync at PTS %lld",
798 pv->way_out_of_sync = 0;
802 if( job->acodec & HB_ACODEC_AC3 )
804 buf = hb_fifo_get( audio->fifo_raw );
806 buf->stop = start + 90000 * AC3_SAMPLES_PER_FRAME / rate;
808 sync->count_frames += AC3_SAMPLES_PER_FRAME;
812 hb_buffer_t * buf_raw = hb_fifo_get( audio->fifo_raw );
814 int count_in, count_out;
816 count_in = buf_raw->size / HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) / sizeof( float );
817 count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000;
818 if( buf->start < pts_expected - 1500 )
820 else if( buf->start > pts_expected + 1500 )
823 sync->data.data_in = (float *) buf_raw->data;
824 sync->data.input_frames = count_in;
825 sync->data.output_frames = count_out;
827 sync->data.src_ratio = (double) sync->data.output_frames /
828 (double) sync->data.input_frames;
830 buf = hb_buffer_init( sync->data.output_frames * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) *
832 sync->data.data_out = (float *) buf->data;
833 if( src_process( sync->state, &sync->data ) )
835 /* XXX If this happens, we're screwed */
836 hb_log( "sync: src_process failed" );
838 hb_buffer_close( &buf_raw );
840 buf->size = sync->data.output_frames_gen * HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown) * sizeof( float );
842 /* Set dates for resampled data */
844 buf->stop = start + sync->data.output_frames_gen *
847 sync->count_frames += sync->data.output_frames_gen;
850 buf->frametype = HB_FRAME_AUDIO;
851 hb_fifo_push( fifo, buf );
854 if( hb_fifo_is_full( fifo ) &&
855 pv->way_out_of_sync )
858 * Trash the top audio packet to avoid dead lock as we reconverge.
860 if ( (buf = hb_fifo_get( audio->fifo_raw ) ) != NULL)
861 hb_buffer_close( &buf );
864 if( NeedSilence( w, audio ) )
866 InsertSilence( w, i );
870 static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio )
872 hb_work_private_t * pv = w->private_data;
873 hb_job_t * job = pv->job;
875 if( hb_fifo_size( audio->fifo_in ) ||
876 hb_fifo_size( audio->fifo_raw ) ||
877 hb_fifo_size( audio->fifo_sync ) ||
878 hb_fifo_size( audio->fifo_out ) )
880 /* We have some audio, we are fine */
884 /* No audio left in fifos */
886 if( hb_thread_has_exited( job->reader ) )
888 /* We might miss some audio to complete encoding and muxing
890 hb_log("Reader has exited early, inserting silence.");
894 if( hb_fifo_is_full( job->fifo_mpeg2 ) &&
895 hb_fifo_is_full( job->fifo_raw ) &&
896 hb_fifo_is_full( job->fifo_sync ) &&
897 hb_fifo_is_full( job->fifo_render ) &&
898 hb_fifo_is_full( job->fifo_mpeg4 ) )
900 /* Too much video and no audio, oh-oh */
901 hb_log("Still got some video - and nothing in the audio fifo, insert silence");
908 static void InsertSilence( hb_work_object_t * w, int i )
910 hb_work_private_t * pv = w->private_data;
912 hb_sync_audio_t * sync;
916 sync = &pv->sync_audio[i];
918 if( job->acodec & HB_ACODEC_AC3 )
920 buf = hb_buffer_init( sync->ac3_size );
921 buf->start = sync->count_frames * 90000 / sync->audio->rate;
922 buf->stop = buf->start + 90000 * AC3_SAMPLES_PER_FRAME /
924 memcpy( buf->data, sync->ac3_buf, buf->size );
926 hb_log( "sync: adding a silent AC-3 frame for track %x",
928 hb_fifo_push( sync->audio->fifo_out, buf );
930 sync->count_frames += AC3_SAMPLES_PER_FRAME;
935 buf = hb_buffer_init( HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(sync->audio->amixdown) * job->arate / 20 *
937 buf->start = sync->count_frames * 90000 / job->arate;
938 buf->stop = buf->start + 90000 / 20;
939 memset( buf->data, 0, buf->size );
941 hb_fifo_push( sync->audio->fifo_sync, buf );
943 sync->count_frames += job->arate / 20;
947 static void UpdateState( hb_work_object_t * w )
949 hb_work_private_t * pv = w->private_data;
952 if( !pv->count_frames )
954 pv->st_first = hb_get_date();
958 if( hb_get_date() > pv->st_dates[3] + 1000 )
960 memmove( &pv->st_dates[0], &pv->st_dates[1],
961 3 * sizeof( uint64_t ) );
962 memmove( &pv->st_counts[0], &pv->st_counts[1],
963 3 * sizeof( uint64_t ) );
964 pv->st_dates[3] = hb_get_date();
965 pv->st_counts[3] = pv->count_frames;
968 #define p state.param.working
969 state.state = HB_STATE_WORKING;
970 p.progress = (float) pv->count_frames / (float) pv->count_frames_max;
971 if( p.progress > 1.0 )
975 p.rate_cur = 1000.0 *
976 (float) ( pv->st_counts[3] - pv->st_counts[0] ) /
977 (float) ( pv->st_dates[3] - pv->st_dates[0] );
978 if( hb_get_date() > pv->st_first + 4000 )
981 p.rate_avg = 1000.0 * (float) pv->st_counts[3] /
982 (float) ( pv->st_dates[3] - pv->st_first );
983 eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) /
985 p.hours = eta / 3600;
986 p.minutes = ( eta % 3600 ) / 60;
987 p.seconds = eta % 60;
998 hb_set_state( pv->job->h, &state );