Index: libhb/encx264.c =================================================================== --- libhb/encx264.c (revision 1244) +++ libhb/encx264.c (working copy) @@ -25,6 +25,24 @@ #define DTS_BUFFER_SIZE 32 +/* + * The frame info struct remembers information about each frame across calls + * to x264_encoder_encode. Since frames are uniquely identified by their + * timestamp, we use some bits of the timestamp as an index. The LSB is + * chosen so that two successive frames will have different values in the + * bits over any plausible range of frame rates. (Starting with bit 9 allows + * any frame rate slower than 175fps.) The MSB determines the size of the array. + * It is chosen so that two frames can't use the same slot during the + * encoder's max frame delay (set by the standard as 16 frames) and so + * that, up to some minimum frame rate, frames are guaranteed to map to + * different slots. (An MSB of 16 which is 2^(16-9+1) = 256 slots guarantees + * no collisions down to a rate of 1.4 fps). + */ +#define FRAME_INFO_MAX2 (9) // 2^9 = 512; 90000/512 = 175 frames/sec +#define FRAME_INFO_MIN2 (16) // 2^16 = 65536; 90000/65536 = 1.4 frames/sec +#define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) +#define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) + struct hb_work_private_s { hb_job_t * job; @@ -32,15 +50,15 @@ x264_picture_t pic_in; uint8_t *x264_allocated_pic; - // Internal queue of DTS start/stop values. - int64_t dts_start[DTS_BUFFER_SIZE]; - int64_t dts_stop[DTS_BUFFER_SIZE]; + int64_t dts_next; // DTS start time value for next output frame + int64_t last_stop; // Debugging - stop time of previous input frame int64_t init_delay; - - int64_t dts_write_index; - int64_t dts_read_index; int64_t next_chap; + struct { + int64_t duration; + } frame_info[FRAME_INFO_SIZE]; + char filename[1024]; }; @@ -247,8 +265,7 @@ pv->x264_allocated_pic = pv->pic_in.img.plane[0]; - pv->dts_write_index = 0; - pv->dts_read_index = 0; + pv->dts_next = -1; pv->next_chap = 0; if (job->areBframes) @@ -268,7 +285,7 @@ will use the duration of frames running at 23.976fps instead.. */ if (job->vfr) { - pv->init_delay = 3754; + pv->init_delay = 7506; } /* The delay is 2 frames for regular b-frames, 3 for b-pyramid. @@ -296,6 +313,22 @@ /* TODO */ } +/* + * see comments in definition of 'frame_info' in pv struct for description + * of what these routines are doing. + */ +static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in ) +{ + int i = (in->start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; + pv->frame_info[i].duration = in->stop - in->start; +} + +static int64_t get_frame_duration( hb_work_private_t * pv, int64_t pts ) +{ + int i = (pts >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; + return pv->frame_info[i].duration; +} + int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { @@ -330,6 +363,16 @@ job->height / 4; } + if( pv->dts_next == -1 ) + { + /* we don't have a start time yet so use the first frame's + * start. All other frame times will be determined by the + * sum of the prior output frame durations in *DTS* order + * (not by the order they arrive here). This timing change is + * essential for VFR with b-frames, but a complete nop * otherwise. + */ + pv->dts_next = in->start; + } if( in->new_chap && job->chapter_markers ) { /* chapters have to start with an IDR frame so request that this @@ -351,11 +394,22 @@ } pv->pic_in.i_qpplus1 = 0; - // Remember current PTS value, use as DTS later - pv->dts_start[pv->dts_write_index & (DTS_BUFFER_SIZE-1)] = in->start; - pv->dts_stop[pv->dts_write_index & (DTS_BUFFER_SIZE-1)] = in->stop; - pv->dts_write_index++; + /* XXX this is temporary debugging code to check that the upstream + * modules (render & sync) have generated a continuous, self-consistent + * frame stream with the current frame's start time equal to the + * previous frame's stop time. + */ + if( pv->last_stop != in->start ) + { + hb_log("encx264 input continuity err: last stop %lld start %lld", + pv->last_stop, in->start); + } + pv->last_stop = in->stop; + // Remember info about this frame that we need to pass across + // the x264_encoder_encode call (since it reorders frames). + save_frame_info( pv, in ); + /* Feed the input DTS to x264 so it can figure out proper output PTS */ pv->pic_in.i_pts = in->start; @@ -386,17 +440,15 @@ /* Should be way too large */ buf = hb_buffer_init( 3 * job->width * job->height / 2 ); buf->size = 0; - buf->start = in->start; - buf->stop = in->stop; buf->frametype = 0; - int64_t dts_start, dts_stop; - /* Get next DTS value to use */ - dts_start = pv->dts_start[pv->dts_read_index & (DTS_BUFFER_SIZE-1)]; - dts_stop = pv->dts_stop[pv->dts_read_index & (DTS_BUFFER_SIZE-1)]; - pv->dts_read_index++; + int64_t dts_start = pv->dts_next; + /* compute the stop time based on the original frame's duration */ + int64_t dts_stop = dts_start + get_frame_duration( pv, pic_out.i_pts ); + pv->dts_next = dts_stop; + for( i = 0; i < i_nal; i++ ) { int size, data; @@ -469,11 +521,7 @@ /* Store the output presentation time stamp from x264 for use by muxmp4 in off-setting - b-frames with the CTTS atom. - For now, just add 1000000 to the offset so that the - value is pretty much guaranteed to be positive. The - muxing code will minimize the renderOffset at the end. */ - + b-frames with the CTTS atom. */ buf->renderOffset = pic_out.i_pts - dts_start + pv->init_delay; /* Send out the next dts values */ Index: libhb/render.c =================================================================== --- libhb/render.c (revision 1244) +++ libhb/render.c (working copy) @@ -20,11 +20,13 @@ hb_buffer_t * buf_scale; hb_fifo_t * subtitle_queue; hb_fifo_t * delay_queue; - int frames_to_extend; int dropped_frames; int extended_frames; uint64_t last_start[4]; uint64_t last_stop[4]; + uint64_t lost_time[4]; + uint64_t total_lost_time; + uint64_t total_gained_time; }; int renderInit( hb_work_object_t *, hb_job_t * ); @@ -280,9 +282,22 @@ { if( job->vfr ) { - pv->frames_to_extend += 4; + /* We need to compensate for the time lost by dropping this frame. + Spread its duration out in quarters, because usually dropped frames + maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones. + Store these in the lost_time array, which has 4 slots in it. + Because not every frame duration divides evenly by 4, and we can't lose the + remainder, we have to go through an awkward process to preserve it in the 4th array index. */ + uint64_t temp_duration = buf_tmp_out->stop - buf_tmp_out->start; + pv->lost_time[0] += (temp_duration / 4); + pv->lost_time[1] += (temp_duration / 4); + pv->lost_time[2] += (temp_duration / 4); + pv->lost_time[3] += ( temp_duration - (temp_duration / 4) - (temp_duration / 4) - (temp_duration / 4) ); + + pv->total_lost_time += temp_duration; pv->dropped_frames++; - hb_fifo_get( pv->subtitle_queue ); + + hb_fifo_get( pv->subtitle_queue ); buf_tmp_in = NULL; } else @@ -304,8 +319,11 @@ pv->last_start[i] = pv->last_start[i-1]; pv->last_stop[i] = pv->last_stop[i-1]; } - pv->last_start[0] = in->start; - pv->last_stop[0] = in->stop; + + /* In order to make sure we have continuous time stamps, store + the current frame's duration as starting when the last one stopped. */ + pv->last_start[0] = pv->last_stop[1]; + pv->last_stop[0] = pv->last_start[0] + (in->stop - in->start); } /* Apply subtitles */ @@ -385,13 +403,17 @@ if( *buf_out ) { - if( pv->frames_to_extend ) + /* The current frame exists. That means it hasn't been dropped by a filter. + Make it accessible as ivtc_buffer so we can edit its duration if needed. */ + ivtc_buffer = *buf_out; + + if( pv->lost_time[3] > 0 ) { /* - * A frame's been dropped by VFR detelecine. + * A frame's been dropped earlier by VFR detelecine. * Gotta make up the lost time. This will also - * slow down the video to 23.976fps. - * The dropped frame ran for 3003 ticks, so + * slow down the video. + * The dropped frame's has to be accounted for, so * divvy it up amongst the 4 frames left behind. * This is what the delay_queue is for; * telecined sequences start 2 frames before @@ -399,29 +421,53 @@ * ones you need a 2 frame delay between * reading input and writing output. */ - ivtc_buffer = *buf_out; + + /* We want to extend the outputted frame's duration by the value + stored in the 4th slot of the lost_time array. Because we need + to adjust all the values in the array so they're contiguous, + extend the duration inside the array first, before applying + it to the current frame buffer. */ + pv->last_stop[3] += pv->lost_time[3]; - /* The 4th cached frame will be the to use. */ - ivtc_buffer->start = pv->last_start[3]; - ivtc_buffer->stop = pv->last_stop[3]; - - if (pv->frames_to_extend % 4) - ivtc_buffer->stop += 751; - else - ivtc_buffer->stop += 750; + /* Log how much time has been added back in to the video. */ + pv->total_gained_time += pv->lost_time[3]; - /* Set the 3rd cached frame to start when this one stops, - and to stop 3003 ticks later -- a normal 29.97fps - length frame. If it needs to be extended as well to - make up lost time, it'll be handled on the next - loop through the renderer. */ - int temp_duration = pv->last_stop[2] - pv->last_start[2]; - pv->last_start[2] = ivtc_buffer->stop; - pv->last_stop[2] = ivtc_buffer->stop + temp_duration; + /* We've pulled the 4th value from the lost_time array + and added it to the last_stop array's 4th slot. Now, rotate the + lost_time array so the 4th slot now holds the 3rd's value, and + so on down the line, and set the 0 index to a value of 0. */ + int i; + for( i=2; i >= 0; i--) + { + pv->lost_time[i+1] = pv->lost_time[i]; + } + pv->lost_time[0] = 0; - pv->frames_to_extend--; + /* Log how many frames have had their durations extended. */ pv->extended_frames++; } + + /* We can't use the given time stamps. Previous frames + might already have been extended, throwing off the + raw values fed to render.c. Instead, their + stop and start times are stored in arrays. + The 4th cached frame will be the to use. + If it needed its duration extended to make up + lost time, it will have happened above. */ + ivtc_buffer->start = pv->last_start[3]; + ivtc_buffer->stop = pv->last_stop[3]; + + /* Set the 3rd cached frame to start when this one stops, + and so on down the line. If any of them need to be + extended as well to make up lost time, it'll be handled + on the next loop through the renderer. */ + int i; + for (i = 2; i >= 0; i--) + { + int temp_duration = pv->last_stop[i] - pv->last_start[i]; + pv->last_start[i] = pv->last_stop[i+1]; + pv->last_stop[i] = pv->last_start[i] + temp_duration; + } } @@ -431,11 +477,14 @@ void renderClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; - - hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) ); - hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) ); - hb_log("render: Lost time: %i frames (%i ticks)", (pv->dropped_frames * 4) - (pv->extended_frames), (pv->dropped_frames * 3003) - ( ( pv->extended_frames / 4 ) * 3003 ) ); + hb_log("render: lost time: %lld (%i frames)", pv->total_lost_time, pv->dropped_frames); + hb_log("render: gained time: %lld (%i frames) (%lld not accounted for)", pv->total_gained_time, pv->extended_frames, pv->total_lost_time - pv->total_gained_time); + if( pv->dropped_frames ) + { + hb_log("render: average dropped frame duration: %lld", (pv->total_lost_time / pv->dropped_frames) ); + } + /* Cleanup subtitle queue */ if( pv->subtitle_queue ) { @@ -476,12 +525,17 @@ /* Setup FIFO queue for subtitle cache */ pv->subtitle_queue = hb_fifo_init( 8 ); pv->delay_queue = hb_fifo_init( 8 ); - pv->frames_to_extend = 0; pv->dropped_frames = 0; pv->extended_frames = 0; pv->last_start[0] = 0; pv->last_stop[0] = 0; - + pv->total_lost_time = 0; + pv->total_gained_time = 0; + pv->lost_time[0] = 0; + pv->lost_time[1] = 0; + pv->lost_time[2] = 0; + pv->lost_time[3] = 0; + /* Setup filters */ /* TODO: Move to work.c? */ if( job->filters ) Index: contrib/patch-x264-vbv-2pass.patch =================================================================== --- contrib/patch-x264-vbv-2pass.patch (revision 1244) +++ contrib/patch-x264-vbv-2pass.patch (working copy) @@ -1,6 +1,6 @@ Index: encoder/ratecontrol.c =================================================================== ---- encoder/ratecontrol.c (revision 680) +--- encoder/ratecontrol.c (revision 736) +++ encoder/ratecontrol.c (working copy) @@ -43,6 +43,7 @@ int p_tex_bits; @@ -12,14 +12,14 @@ int i_count; @@ -1149,7 +1150,7 @@ return; - + rct->buffer_fill_final += rct->buffer_rate - bits; - if( rct->buffer_fill_final < 0 && !rct->b_2pass ) + if( rct->buffer_fill_final < 0 ) x264_log( h, X264_LOG_WARNING, "VBV underflow (%.0f bits)\n", rct->buffer_fill_final ); rct->buffer_fill_final = x264_clip3f( rct->buffer_fill_final, 0, rct->buffer_size ); } -@@ -1325,6 +1326,28 @@ +@@ -1325,6 +1326,22 @@ double w = x264_clip3f( time*100, 0.0, 1.0 ); q *= pow( (double)total_bits / rcc->expected_bits_sum, w ); } @@ -37,21 +37,15 @@ + expected_size = qscale2bits(&rce, q); + expected_vbv = rcc->buffer_fill + rcc->buffer_rate - expected_size; + } -+/* x264_log( h, X264_LOG_INFO, -+ "frame %d rcc expected vbv = %d encoding expected vbv = %d\n", -+ (int)(h->fenc->i_frame), -+ (int)(rce.expected_vbv), -+ (int)(expected_vbv));*/ -+ + } + } q = x264_clip3f( q, lmin, lmax ); } else /* 1pass ABR */ -@@ -1455,6 +1478,137 @@ +@@ -1455,6 +1472,137 @@ /* the rest of the variables are either constant or thread-local */ } - + +FILE *fh_vbv; + +static int find_underflow( x264_t *h, double *fills, int *t0, int *t1, int over ) @@ -84,15 +78,17 @@ + return start>=0 && end>=0; +} + -+static void fix_underflow( x264_t *h, int t0, int t1, double adjustment ) ++static void fix_underflow( x264_t *h, int t0, int t1, double adjustment, double qscale_min, double qscale_max) +{ + x264_ratecontrol_t *rcc = h->rc; ++ double qscale_new; + int i; + if(t0 > 0) + t0++; -+// printf("interval [%d,%d] %.4f\n", t0, t1, adjustment); -+ for(i=t0; i<=t1; i++) -+ rcc->entry[i].new_qscale *= adjustment; ++ for(i=t0; i<=t1; i++) { ++ qscale_new = rcc->entry[i].new_qscale *= adjustment; ++ rcc->entry[i].new_qscale = x264_clip3f(qscale_new, qscale_min, qscale_max); ++ } +} + +static double count_expected_bits( x264_t *h ) @@ -136,9 +132,10 @@ + double all_available_bits = h->param.rc.i_bitrate * 1000. * rcc->num_entries / rcc->fps; + double expected_bits, prev_bits, adjustment; + int i, t0, t1, space; ++ double qscale_min = qp2qscale(h->param.rc.i_qp_min); ++ double qscale_max = qp2qscale(h->param.rc.i_qp_max); + + fills++; -+// fh_vbv = fopen("vbv.log", "w"); + + //adjust overall stream size + do { @@ -149,7 +146,7 @@ + //fix underflows + while(find_underflow(h, fills, &t0, &t1, 0)) + { -+ fix_underflow(h, t0, t1, 1.001); ++ fix_underflow(h, t0, t1, 1.001, qscale_min, qscale_max); + space = 1; + } + @@ -160,7 +157,7 @@ + //fix overflows + while(find_underflow(h, fills, &t0, &t1, 1)) + { -+ fix_underflow(h, t0, t1, adjustment); ++ fix_underflow(h, t0, t1, adjustment, qscale_min, qscale_max); + t0 = t1; + space = 1; + } @@ -171,30 +168,27 @@ + fills[-1] = rcc->buffer_size * (1. - h->param.rc.f_vbv_buffer_init); + t0 = 0; + while(find_underflow(h, fills, &t0, &t1, 0)) -+ fix_underflow(h, t0, t1, 1.001); ++ fix_underflow(h, t0, t1, 1.001, qscale_min, qscale_max); + -+// debug_dump_vbv(h); -+ + //store expected vbv filling values for tracking when encoding + for(i=0; inum_entries; i++) + rcc->entry[i].expected_vbv = rcc->buffer_size - fills[i]; + -+// fclose(fh_vbv); + x264_free(fills-1); +} + static int init_pass2( x264_t *h ) { x264_ratecontrol_t *rcc = h->rc; -@@ -1543,7 +1697,6 @@ +@@ -1543,7 +1691,6 @@ rcc->last_non_b_pict_type = -1; rcc->last_accum_p_norm = 1; rcc->accum_p_norm = 0; - rcc->buffer_fill = rcc->buffer_size * h->param.rc.f_vbv_buffer_init; - + /* find qscale */ for(i=0; inum_entries; i++){ -@@ -1580,15 +1733,9 @@ +@@ -1580,18 +1727,11 @@ /* find expected bits */ for(i=0; inum_entries; i++){ ratecontrol_entry_t *rce = &rcc->entry[i]; @@ -209,12 +203,15 @@ - rcc->buffer_fill = rcc->buffer_fill_final; + expected_bits += qscale2bits(rce, rce->new_qscale); } - - //printf("expected:%llu available:%llu factor:%lf avgQ:%lf\n", (uint64_t)expected_bits, all_available_bits, rate_factor); -@@ -1599,6 +1746,10 @@ + +-//printf("expected:%llu available:%llu factor:%lf avgQ:%lf\n", (uint64_t)expected_bits, all_available_bits, rate_factor); + if(expected_bits > all_available_bits) rate_factor -= step; + } + +@@ -1599,6 +1739,10 @@ if(filter_size > 1) x264_free(blurred_qscale); - + + if(rcc->b_vbv) + vbv_pass2(h); + expected_bits = count_expected_bits(h); @@ -222,17 +219,17 @@ if(fabs(expected_bits/all_available_bits - 1.0) > 0.01) { double avgq = 0; -@@ -1606,7 +1757,8 @@ +@@ -1606,7 +1750,8 @@ avgq += rcc->entry[i].new_qscale; avgq = qscale2qp(avgq / rcc->num_entries); - + - x264_log(h, X264_LOG_WARNING, "Error: 2pass curve failed to converge\n"); + if ((expected_bits > all_available_bits) || (!rcc->b_vbv)) + x264_log(h, X264_LOG_WARNING, "Error: 2pass curve failed to converge\n"); x264_log(h, X264_LOG_WARNING, "target: %.2f kbit/s, expected: %.2f kbit/s, avg QP: %.4f\n", (float)h->param.rc.i_bitrate, expected_bits * rcc->fps / (rcc->num_entries * 1000.), -@@ -1625,7 +1777,7 @@ +@@ -1625,7 +1770,7 @@ else x264_log(h, X264_LOG_WARNING, "try increasing target bitrate\n"); } @@ -240,9 +237,10 @@ + else if(!(rcc->b_2pass && rcc->b_vbv)) x264_log(h, X264_LOG_WARNING, "internal error\n"); } - -@@ -1633,3 +1785,4 @@ + +@@ -1633,3 +1778,4 @@ } + - + + Index: contrib/Jamfile =================================================================== --- contrib/Jamfile (revision 1244) +++ contrib/Jamfile (working copy) @@ -359,9 +359,11 @@ { LIBX264_PATCH += " $(PATCH) -p1 < ../patch-x264-solaris.patch && " ; } -# AQ is temporarily disabled LIBX264_PATCH += "$(PATCH) -p0 < ../patch-x264-aq.patch && " ; +# VBV 2 Pass v3 (must be applied *before* patch-x264-vaq48.patch) Uncomment to use both + LIBX264_PATCH += "$(PATCH) -p0 < ../patch-x264-vbv-2pass.patch && " ; +# VAQ .48 Patch (Dark_Shikari) + LIBX264_PATCH += "$(PATCH) -p0 < ../patch-x264-vaq48.patch && " ; LIBX264_PATCH += "$(PATCH) -p0 < ../patch-x264-idr.patch && " ; - LIBX264_PATCH += "$(PATCH) -p0 < ../patch-x264-vbv-2pass.patch && " ; Depends $(<) : $(>) ; Depends lib : $(<) ; } Index: contrib/version_x264.txt =================================================================== --- contrib/version_x264.txt (revision 1244) +++ contrib/version_x264.txt (working copy) @@ -1 +1 @@ -http://download.m0k.org/handbrake/contrib/x264-r720.tar.gz +http://download.m0k.org/handbrake/contrib/x264-r736.tar.gz