diff options
author | van <[email protected]> | 2008-04-10 07:05:09 +0000 |
---|---|---|
committer | van <[email protected]> | 2008-04-10 07:05:09 +0000 |
commit | 169d6210069cfb6eb79d5efab82e0fd5c5a9baa0 (patch) | |
tree | ec097ddb111d1de142817f0cb40135094a5b8b47 | |
parent | 8652a800dcaf543fcedde794a80f608efe963ffb (diff) |
Fix avi containers which I unwittingly broke with r1341.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1400 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r-- | libhb/sync.c | 121 |
1 files changed, 95 insertions, 26 deletions
diff --git a/libhb/sync.c b/libhb/sync.c index d8b31ed93..9b2bcfd58 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -5,6 +5,7 @@ It may be used under the terms of the GNU General Public License. */ #include "hb.h" +#include <stdio.h> #include "samplerate.h" #include "ffmpeg/avcodec.h" @@ -49,6 +50,8 @@ struct hb_work_private_s int64_t next_pts; /* start time of next input frame */ int64_t first_drop; /* PTS of first 'went backwards' frame dropped */ int drop_count; /* count of 'time went backwards' drops */ + int drops; /* frames dropped to make a cbr video stream */ + int dups; /* frames duplicated to make a cbr video stream */ int video_sequence; int count_frames; int count_frames_max; @@ -132,10 +135,20 @@ void syncClose( hb_work_object_t * w ) hb_job_t * job = pv->job; hb_title_t * title = job->title; hb_audio_t * audio = NULL; - int i; - if( pv->cur ) hb_buffer_close( &pv->cur ); + if( pv->cur ) + { + hb_buffer_close( &pv->cur ); + } + + hb_log( "sync: got %d frames, %d expected", + pv->count_frames, pv->count_frames_max ); + + if (pv->drops || pv->dups ) + { + hb_log( "sync: %d frames dropped, %d duplicated", pv->drops, pv->dups ); + } for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { @@ -275,10 +288,6 @@ static int SyncVideo( hb_work_object_t * w ) !hb_fifo_size( job->fifo_mpeg2 ) && !hb_fifo_size( job->fifo_raw ) ) { - /* All video data has been processed already, we won't get - more */ - hb_log( "sync: got %d frames, %d expected", - pv->count_frames, pv->count_frames_max ); pv->done = 1; hb_buffer_t * buf_tmp; @@ -502,31 +511,91 @@ static int SyncVideo( hb_work_object_t * w ) } } - /* - * Adjust the pts of the current frame so that it's contiguous - * with the previous frame. The start time of the current frame - * has to be the end time of the previous frame and the stop - * time has to be the start of the next frame. We don't - * make any adjustments to the source timestamps other than removing - * the clock offsets (which also removes pts discontinuities). - * This means we automatically encode at the source's frame rate. - * MP2 uses an implicit duration (frames end when the next frame - * starts) but more advanced containers like MP4 use an explicit - * duration. Since we're looking ahead one frame we set the - * explicit stop time from the start time of the next frame. - */ - buf_tmp = cur; - pv->cur = cur = hb_fifo_get( job->fifo_raw ); - pv->next_pts = next->start; - int64_t duration = next->start - buf_tmp->start; - if ( duration <= 0 ) + int64_t duration; + if ( job->mux & HB_MUX_AVI ) { - hb_log( "sync: invalid video duration %lld, start %lld, next %lld", - duration, buf_tmp->start, next->start ); + /* + * The concept of variable bitrate video was a bit too advanced for + * Microsoft so AVI doesn't support it. Since almost all dvd + * video is VBR we have to convert it to constant bit rate to + * put it in an AVI container. So here we duplicate, drop and + * otherwise trash video frames to appease the gods of Redmond. + */ + + /* mpeg durations are exact when expressed in ticks of the + * 27MHz System clock but not in HB's 90KHz PTS clock. To avoid + * a truncation bias that will eventually cause the audio to desync + * we compute the duration of the next frame using 27MHz ticks + * then truncate it to 90KHz. */ + duration = ( (int64_t)(pv->count_frames + 1 ) * job->vrate_base ) / 300 - + pv->next_start; + + /* We don't want the input & output clocks to be exactly in phase + * otherwise small variations in the time will cause us to think + * we're a full frame off & there will be lots of drops and dups. + * We offset the input clock by half the duration so it's maximally + * out of phase with the output clock. */ + if( cur->start < pv->next_start - ( duration >> 1 ) ) + { + /* current frame too old - drop it */ + if ( cur->new_chap ) + { + pv->chap_mark = cur->new_chap; + } + hb_buffer_close( &cur ); + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + pv->next_pts = next->start; + ++pv->drops; + continue; + } + + if( next->start > pv->next_start + duration + ( duration >> 1 ) ) + { + /* next frame too far ahead - dup current frame */ + buf_tmp = hb_buffer_init( cur->size ); + hb_buffer_copy_settings( buf_tmp, cur ); + memcpy( buf_tmp->data, cur->data, cur->size ); + buf_tmp->sequence = cur->sequence; + ++pv->dups; + } + else + { + /* this frame in our time window & doesn't need to be duped */ + buf_tmp = cur; + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + pv->next_pts = next->start; + } } + else + { + /* + * Adjust the pts of the current frame so that it's contiguous + * with the previous frame. The start time of the current frame + * has to be the end time of the previous frame and the stop + * time has to be the start of the next frame. We don't + * make any adjustments to the source timestamps other than removing + * the clock offsets (which also removes pts discontinuities). + * This means we automatically encode at the source's frame rate. + * MP2 uses an implicit duration (frames end when the next frame + * starts) but more advanced containers like MP4 use an explicit + * duration. Since we're looking ahead one frame we set the + * explicit stop time from the start time of the next frame. + */ + buf_tmp = cur; + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + pv->next_pts = next->start; + duration = next->start - buf_tmp->start; + if ( duration <= 0 ) + { + hb_log( "sync: invalid video duration %lld, start %lld, next %lld", + duration, buf_tmp->start, next->start ); + } + } + buf_tmp->start = pv->next_start; pv->next_start += duration; buf_tmp->stop = pv->next_start; + if ( pv->chap_mark ) { // we have a pending chapter mark from a recent drop - put it on this |