Index: ../hb-0.9.0/libhb/detelecine.c =================================================================== --- ../hb-0.9.0/libhb/detelecine.c (revision 1022) +++ ../hb-0.9.0/libhb/detelecine.c (working copy) @@ -975,10 +975,12 @@ } else { - goto output_frame; + goto discard_frame; } } + // hb_log("frame length: %i", frame->length); + /* Check to see if frame should be dropped */ if( frame->length < 2 ) { @@ -987,7 +989,7 @@ if (!frame) { - goto output_frame; + goto discard_frame; } if( frame->length < 2 ) { @@ -995,19 +997,19 @@ if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) ) { - goto output_frame; + goto discard_frame; } frame = pullup_get_frame( ctx ); if( !frame ) { - goto output_frame; + goto discard_frame; } if( frame->length < 2 ) { pullup_release_frame( frame ); - goto output_frame; + goto discard_frame; } } } @@ -1029,11 +1031,34 @@ memcpy( pv->pic_out.data[2], frame->buffer->planes[2], pv->width[2] * pv->height[2] * sizeof(uint8_t) ); + if (frame->length != 2) + { + /* Interlaced material always seems to have a length of 2 + coming out of pullup, whereas ivtc'd content doesn't. + So this is one potential way of knowing whether NTSC + video is interlaced or telecined. */ + //pullup_release_frame ( frame ); + //goto output_ivtc_frame; + } + pullup_release_frame( frame ); output_frame: *buf_out = pv->buf_out; return FILTER_OK; + +/* This and all discard_frame calls shown above are + the result of me restoring the functionality in + pullup that huevos_rancheros disabled because + HB couldn't handle it. */ +discard_frame: + *buf_out = pv->buf_out; + return FILTER_DROP; + +/*This lets render.c know when the fraeme length is varying. */ +output_ivtc_frame: + *buf_out = pv->buf_out; + return FILTER_OK_IVTC; + } - Index: ../hb-0.9.0/libhb/muxmp4.c =================================================================== --- ../hb-0.9.0/libhb/muxmp4.c (revision 1022) +++ ../hb-0.9.0/libhb/muxmp4.c (working copy) @@ -380,7 +380,7 @@ /* Because we use the audio samplerate as the timescale, we have to use potentially variable durations so the video doesn't go out of sync */ - duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur; + duration = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) ); m->sum_dur += duration; } else Index: ../hb-0.9.0/libhb/sync.c =================================================================== --- ../hb-0.9.0/libhb/sync.c (revision 1022) +++ ../hb-0.9.0/libhb/sync.c (working copy) @@ -52,6 +52,11 @@ uint64_t st_counts[4]; uint64_t st_dates[4]; uint64_t st_first; + + /* Throttle message flags */ + int trashing_audio; + int inserting_silence; + int way_out_of_sync; }; /*********************************************************************** @@ -85,6 +90,10 @@ pv->pts_offset_old = INT64_MIN; pv->count_frames = 0; + pv->trashing_audio = 0; + pv->inserting_silence = 0; + pv->way_out_of_sync = 0; + /* Calculate how many video frames we are expecting */ duration = 0; for( i = job->chapter_start; i <= job->chapter_end; i++ ) @@ -313,8 +322,8 @@ if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE || next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE ) { - hb_log( "Sync: Video PTS discontinuity (current buffer start=%lld, next buffer start=%lld), trash Video", - cur->start, next->start ); + //hb_log( "Sync: Video PTS discontinuity (current buffer start=%lld, next buffer start=%lld), trash Video", + // cur->start, next->start ); /* * Do we need to trash the subtitle, is it from the next->start period @@ -607,28 +616,39 @@ pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate; // hb_log("Video Sequence: %lld, Audio Sequence: %lld", pv->video_sequence, buf->sequence); + /* * Using the same logic as the Video have we crossed a VOB * boundary as detected by the expected PTS and the PTS of our * audio being out by more than the tolerance value. */ - if( ( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE || - buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE ) && - pv->pts_offset_old > INT64_MIN ) + if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE || + buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE ) { /* * Useful debug, but too verbose for normal use. - */ + * hb_log("Sync: Audio discontinuity (sequence: vid %lld aud %lld) (pts %lld < %lld < %lld)", pv->video_sequence, buf->sequence, pts_expected - PTS_DISCONTINUITY_TOLERANCE, buf->start, pts_expected + PTS_DISCONTINUITY_TOLERANCE ); + */ /* There has been a PTS discontinuity, and this frame might be from before the discontinuity*/ - pts_expected = pv->pts_offset_old + sync->count_frames * - 90000 / rate; + if( pv->pts_offset_old > INT64_MIN ) + { + pts_expected = pv->pts_offset_old + sync->count_frames * + 90000 / rate; + } + else + { + pv->pts_offset_old = pv->pts_offset; + pts_expected = pv->pts_offset_old + sync->count_frames * + 90000 / rate; + } + /* * Is the audio from a valid period given the previous * Video PTS. I.e. has there just been a video PTS @@ -651,7 +671,11 @@ * up diverging even more. We need to hold on to the * audio until the video catches up. */ - hb_log("Sync: Audio is way out of sync, attempt to reconverge from current video PTS"); + if( !pv->way_out_of_sync ) + { + hb_log("Sync: Audio is way out of sync, attempt to reconverge from current video PTS"); + pv->way_out_of_sync = 1; + } /* * It wasn't from the old place, so we must be from @@ -674,9 +698,13 @@ /* Tolerance: 100 ms */ if( buf->start < pts_expected - 9000 ) { - /* Audio is behind the Video, trash it, can't use it now. */ - hb_log( "Sync: Audio PTS (%lld) < Video PTS (%lld) by greater than 100ms, trashing audio to reconverge", - buf->start, pts_expected); + if( !pv->trashing_audio ) + { + /* Audio is behind the Video, trash it, can't use it now. */ + hb_log( "Sync: Audio PTS (%lld) < Video PTS (%lld) by greater than 100ms, trashing audio to reconverge", + buf->start, pts_expected); + pv->trashing_audio = 1; + } buf = hb_fifo_get( audio->fifo_raw ); hb_buffer_close( &buf ); continue; @@ -684,9 +712,28 @@ else if( buf->start > pts_expected + 9000 ) { /* Audio is ahead of the Video, insert silence until we catch up*/ - hb_log("Sync: Audio PTS (%lld) > Video PTS (%lld) by greater than 100ms insert silence until reconverged", buf->start, pts_expected); + if( !pv->inserting_silence ) + { + hb_log("Sync: Audio PTS (%lld) > Video PTS (%lld) by greater than 100ms insert silence until reconverged", buf->start, pts_expected); + pv->inserting_silence = 1; + } InsertSilence( w, i ); continue; + } + else + { + if( pv->trashing_audio || pv->inserting_silence ) + { + hb_log( "Sync: Audio back in Sync at PTS %lld", buf->start ); + pv->trashing_audio = 0; + pv->inserting_silence = 0; + } + if( pv->way_out_of_sync ) + { + hb_log( "Sync: Audio no longer way out of sync at PTS %lld", + buf->start ); + pv->way_out_of_sync = 0; + } } if( job->acodec & HB_ACODEC_AC3 ) @@ -741,6 +788,16 @@ hb_fifo_push( fifo, buf ); } + if( hb_fifo_is_full( fifo ) && + pv->way_out_of_sync ) + { + /* + * Trash the top audio packet to avoid dead lock as we reconverge. + */ + buf = hb_fifo_get( audio->fifo_raw ); + hb_buffer_close( &buf ); + } + if( NeedSilence( w, audio ) ) { InsertSilence( w, i ); @@ -818,8 +875,6 @@ buf->stop = buf->start + 90000 / 20; memset( buf->data, 0, buf->size ); - hb_log( "sync: adding 50 ms of silence for track %x", - sync->audio->id ); hb_fifo_push( sync->audio->fifo_sync, buf ); sync->count_frames += job->arate / 20; Index: ../hb-0.9.0/libhb/render.c =================================================================== --- ../hb-0.9.0/libhb/render.c (revision 1022) +++ ../hb-0.9.0/libhb/render.c (working copy) @@ -9,6 +9,14 @@ #include "ffmpeg/avcodec.h" #include "ffmpeg/swscale.h" +/* Used for keeping track of when frames are dropped by detelecine. */ +int ivtc_cadence[12]; +int frame_length[12]; +uint32_t dropped_frames = 0; +uint32_t extended_durations = 0; + +hb_buffer_t * temp = NULL; + struct hb_work_private_s { hb_job_t * job; @@ -184,6 +192,14 @@ /* Setup render buffer */ hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 ); + /* Rotate the cadence tracking. */ + int i = 0; + for(i=11; i > 0; i--) + { + ivtc_cadence[i] = ivtc_cadence[i-1]; + frame_length[i] = frame_length[i-1]; + } + /* Apply filters */ if( job->filters ) { @@ -217,6 +233,8 @@ if( result == FILTER_OK ) { buf_tmp_in = buf_tmp_out; + ivtc_cadence[0] = 0; + frame_length[0] = 0; } else if( result == FILTER_DELAY ) { @@ -225,13 +243,97 @@ } else if( result == FILTER_DROP ) { + /* A drop means detelecine's latched its teeth onto + something and has duped frames to discard. */ + ivtc_cadence[0] = 1; + frame_length[0] = 0; hb_fifo_get( pv->subtitle_queue ); buf_tmp_in = NULL; + dropped_frames++; + //hb_log("RENDER: dropped frames: %i", dropped_frames); +// buf_tmp_in = buf_tmp_out; break; } + else if( result == FILTER_OK_IVTC ) + { + /* Frame length isnt' 2 */ + buf_tmp_in = buf_tmp_out; + ivtc_cadence[0] = 0; + frame_length[0] = 1; + } } - } + + if ( (dropped_frames) && (dropped_frames * 4) > extended_durations && (buf_tmp_in != NULL) ) + { + // hb_log("dropped: %i >= extended / 4: %f", dropped_frames, (float)extended_durations / (float)4 ); + //in->stop = in->stop + 751; + in->stop = in->stop + (90090000 / 120000); + extended_durations++; + } + else if (in->flags & 16) + { + // in->stop = in->stop + 751; + } + /* Here's where the magic happens for VFR. The muxers sense VFR by frame durations. So here are + a few methods for detecting whether a frame should be displayed at 23.976 or 29.87 speed. */ + + /* Uncomment this section to set frame durations based + on the lengths that come out of detelecine.c */ +/* + if ( (frame_length[0] == 1) || (frame_length[1] == 1) || (frame_length[2] == 1) ) + { + if (job->vrate_base != 1126125) + { + in->stop = in->stop + 751; + } + } + else + { + if ( job->vrate_base == 1126125 ) + { + in->stop = in->stop - 751; + } + } +*/ + + /*This, more conservative method, watches for dropped frames in the filters above. When it + finds one, it extends the durations of the next 4 frames to make up for the lost time. It's + not perfect, of course. A buffering system is needed to do this accurately. We should be + stretching the duration of 2 frames before the dropped 1m and 2 after, not 4 after.. */ + +// if ( (ivtc_cadence[1] == 1) || (ivtc_cadence[2] == 1) || (ivtc_cadence[3] == 1) || (ivtc_cadence[4] == 1) /*|| (ivtc_cadence[5] == 1)*/ ) +// { + /* One of the last 4 frames was ditched by detelecine. + That's telecined stuff, and the frames need to be + displayed with a a 23.976 duration, so extend + their durations from ~3003 to ~3754 ticks. */ +// if (job->vrate_base != 1126125) +// { + /* Only extend ivtc frames if the FPS isn't set to 23.976 already. */ +// in->stop = in->stop + 751 ; +// extended_durations++; + //hb_log("RENDER: extended durations: %i / 4: %i", extended_durations, extended_durations / 4); +// } +// } +// else if ( in->flags & 16 ) +// { + /* The frame is progressive. If you want to blindly assume it really is + a 23.976 frame, extend its duration by uncommenting the line of code + below. Otherwise, have faith in detelecine.c. */ + //in->stop = in->stop + 751 ; +// } +// else +// { +// if ( (job->vrate_base == 1126125) && !(in->flags & 16) ) +// { + /* Shorten frame durations for non-ivtc video stuff when using 23.976 */ + // in->stop = in->stop - 751; +// } + +// } + } + /* Apply subtitles */ if( buf_tmp_in ) { @@ -268,6 +370,11 @@ buf_tmp_in = buf_render; } + /* Pass VFR NTSC IVTC frame durations on through the workflow. + I totally wish I could slip some cryptic abbreviations into + the last sentence. */ + buf_render->stop = in->stop; + /* Set output to render buffer */ (*buf_out) = buf_render; @@ -297,6 +404,14 @@ void renderClose( hb_work_object_t * w ) { + /* (90090000 / 120000) is the rational equivalent of 750.75. + That's the number of ticks by which a 23.976 frame is + longer than a 29.97 frame. God I hate fractions. + */ + hb_log("RENDER: dropped frames: %i (%i ticks)", dropped_frames, (dropped_frames * 3003) ); + hb_log("RENDER: extended frames: %i (%i ticks)", extended_durations, (extended_durations * (90090000 / 120000) ) ); + hb_log("RENDER: Lost time: %i ticks", (dropped_frames * 3003) - (extended_durations * (90090000 / 120000) ) ); + hb_work_private_t * pv = w->private_data; /* Cleanup subtitle queue */ Index: ../hb-0.9.0/libhb/common.h =================================================================== --- ../hb-0.9.0/libhb/common.h (revision 1022) +++ ../hb-0.9.0/libhb/common.h (working copy) @@ -528,6 +528,7 @@ #define FILTER_DELAY 1 #define FILTER_FAILED 2 #define FILTER_DROP 3 +#define FILTER_OK_IVTC 4 struct hb_filter_object_s {