--- trunk/vfw/src/2pass.c 2002/04/06 06:21:29 105 +++ trunk/vfw/src/2pass.c 2002/09/23 06:10:43 527 @@ -23,6 +23,8 @@ * * History: * + * 17.04.2002 changed 1st pass quant to always be 2 (2pass_update()) + * 07.04.2002 added max bitrate constraint, overflow controls (foxer) * 31.03.2002 inital version; * *************************************************************************/ @@ -45,6 +47,7 @@ double total1 = 0.0; double total2 = 0.0; + double dbytes, dbytes2; if (codec->config.hinted_me) { @@ -159,8 +162,9 @@ { if (twopass->nns1.quant & NNSTATS_KEYFRAME) { - i_boost_total = twopass->nns2.bytes * codec->config.keyframe_boost / 100; + i_boost_total += twopass->nns2.bytes * codec->config.keyframe_boost / 100; i_total += twopass->nns2.bytes; + twopass->keyframe_locations[i_frames] = frames; ++i_frames; } @@ -172,6 +176,7 @@ ++frames; } + twopass->keyframe_locations[i_frames] = frames; twopass->movie_curve = ((double)(total_ext + i_boost_total) / total_ext); twopass->average_frame = ((double)(total_ext - i_total) / (frames - credits_frames - i_frames) / twopass->movie_curve); @@ -248,10 +253,16 @@ } } + if (frames == 0) + { + twopass->minpsize = (twopass->nns1.kblk + 88) / 8; + twopass->minisize = ((twopass->nns1.kblk * 22) + 240) / 8; + } + if (!codec_is_in_credits(&codec->config, frames) && !(twopass->nns1.quant & NNSTATS_KEYFRAME)) { - double dbytes = twopass->nns2.bytes / twopass->movie_curve; + dbytes = twopass->nns2.bytes / twopass->movie_curve; total1 += dbytes; if (codec->config.use_alt_curve) @@ -259,21 +270,21 @@ if (dbytes > twopass->average_frame) { if (dbytes >= twopass->alt_curve_high) - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev); + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev); else { switch(codec->config.alt_curve_type) { case 2: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))); break; case 1: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff); break; case 0: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)))); } } @@ -281,21 +292,21 @@ else { if (dbytes <= twopass->alt_curve_low) - total2 += dbytes; + dbytes2 = dbytes; else { switch(codec->config.alt_curve_type) { case 2: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))); break; case 1: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff); break; case 0: - total2 += dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev * (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)))); } } @@ -305,15 +316,20 @@ { if (dbytes > twopass->average_frame) { - total2 += ((double)dbytes + (twopass->average_frame - dbytes) * + dbytes2 = ((double)dbytes + (twopass->average_frame - dbytes) * codec->config.curve_compression_high / 100.0); } else { - total2 += ((double)dbytes + (twopass->average_frame - dbytes) * + dbytes2 = ((double)dbytes + (twopass->average_frame - dbytes) * codec->config.curve_compression_low / 100.0); } } + + if (dbytes2 < twopass->minpsize) + dbytes2 = twopass->minpsize; + + total2 += dbytes2; } ++frames; @@ -369,6 +385,7 @@ { i_total += twopass->nns1.bytes + twopass->nns1.bytes * codec->config.keyframe_boost / 100; total += twopass->nns1.bytes * codec->config.keyframe_boost / 100; + twopass->keyframe_locations[i_frames] = frames; ++i_frames; } @@ -376,6 +393,7 @@ ++frames; } + twopass->keyframe_locations[i_frames] = frames; // compensate for avi frame overhead desired -= frames * 24; @@ -505,10 +523,16 @@ } } + if (frames == 0) + { + twopass->minpsize = (twopass->nns1.kblk + 88) / 8; + twopass->minisize = ((twopass->nns1.kblk * 22) + 240) / 8; + } + if (!codec_is_in_credits(&codec->config, frames) && !(twopass->nns1.quant & NNSTATS_KEYFRAME)) { - double dbytes = twopass->nns1.bytes / twopass->movie_curve; + dbytes = twopass->nns1.bytes / twopass->movie_curve; total1 += dbytes; if (codec->config.use_alt_curve) @@ -516,21 +540,21 @@ if (dbytes > twopass->average_frame) { if (dbytes >= twopass->alt_curve_high) - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev); + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev); else { switch(codec->config.alt_curve_type) { case 2: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff))); break; case 1: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (dbytes - twopass->average_frame) / twopass->alt_curve_high_diff); break; case 0: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_high_diff)))); } } @@ -538,21 +562,21 @@ else { if (dbytes <= twopass->alt_curve_low) - total2 += dbytes; + dbytes2 = dbytes; else { switch(codec->config.alt_curve_type) { case 2: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * sin(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff))); break; case 1: - total2 += dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual - twopass->alt_curve_qual_dev * (dbytes - twopass->average_frame) / twopass->alt_curve_low_diff); break; case 0: - total2 += dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev * + dbytes2 = dbytes * (twopass->alt_curve_mid_qual + twopass->alt_curve_qual_dev * (1.0 - cos(DEG2RAD * ((dbytes - twopass->average_frame) * 90.0 / twopass->alt_curve_low_diff)))); } } @@ -562,15 +586,20 @@ { if (dbytes > twopass->average_frame) { - total2 += ((double)dbytes + (twopass->average_frame - dbytes) * + dbytes2 = ((double)dbytes + (twopass->average_frame - dbytes) * codec->config.curve_compression_high / 100.0); } else { - total2 += ((double)dbytes + (twopass->average_frame - dbytes) * + dbytes2 = ((double)dbytes + (twopass->average_frame - dbytes) * codec->config.curve_compression_low / 100.0); } } + + if (dbytes2 < twopass->minpsize) + dbytes2 = twopass->minpsize; + + total2 += dbytes2; } ++frames; @@ -680,6 +709,9 @@ } twopass->overflow = 0; + twopass->KFoverflow = 0; + twopass->KFoverflow_partial = 0; + twopass->KF_idx = 1; break; } @@ -700,6 +732,8 @@ int bytes1, bytes2; int overflow; int credits_pos; + int capped_to_max_framesize = 0; + int KFdistance, KF_min_size; if (codec->framenum == 0) { @@ -783,7 +817,27 @@ } else // DLG_MODE_2PASS_2_EXT { - bytes2 = twopass->nns2.bytes; + if (codec->config.credits_mode == CREDITS_MODE_QUANT) + { + if (codec->config.credits_quant_i != codec->config.credits_quant_p) + { + frame->quant = frame->intra ? + codec->config.credits_quant_i : + codec->config.credits_quant_p; + } + else + { + frame->quant = codec->config.credits_quant_p; + frame->intra = -1; + } + + twopass->bytes1 = bytes1; + twopass->bytes2 = bytes1; + twopass->desired_bytes2 = bytes1; + return ICERR_OK; + } + else + bytes2 = twopass->nns2.bytes; } } else // Foxer: apply curve compression outside credits @@ -904,24 +958,59 @@ bytes2 += ((int)dbytes); } - // cap bytes2 to first pass size, lowers number of quant=1 frames - if (bytes2 > bytes1) + if (frame->intra) { - curve_comp_error += bytes2 - bytes1; - bytes2 = bytes1; + if (bytes2 < twopass->minisize) + { + curve_comp_error -= twopass->minisize - bytes2; + bytes2 = twopass->minisize; + } } - else if (bytes2 < 1) + else { - curve_comp_error += --bytes2; - bytes2 = 1; + // cap bytes2 to first pass size, lowers number of quant=1 frames + if (bytes2 > bytes1) + { + curve_comp_error += bytes2 - bytes1; + bytes2 = bytes1; + } + else if (bytes2 < twopass->minpsize) + bytes2 = twopass->minpsize; } } twopass->desired_bytes2 = bytes2; + // if this keyframe is too close to the next, + // reduce it's byte allotment + if (frame->intra && !credits_pos) + { + KFdistance = codec->twopass.keyframe_locations[codec->twopass.KF_idx] - + codec->twopass.keyframe_locations[codec->twopass.KF_idx - 1]; + + if (KFdistance < codec->config.kftreshold) + { + KFdistance = KFdistance - codec->config.min_key_interval; + + if (KFdistance >= 0) + { + KF_min_size = bytes2 * (100 - codec->config.kfreduction) / 100; + if (KF_min_size < 1) + KF_min_size = 1; + + bytes2 = KF_min_size + (bytes2 - KF_min_size) * KFdistance / + (codec->config.kftreshold - codec->config.min_key_interval); + + if (bytes2 < 1) + bytes2 = 1; + } + } + } + // Foxer: scale overflow in relation to average size, so smaller frames don't get // too much/little bitrate - overflow = (int)((double)overflow * bytes2 / twopass->average_frame); + overflow = (int)((double)overflow * bytes2 / twopass->average_frame * + (bytes1 - bytes2) / bytes1); // Foxer: reign in overflow with huge frames if (labs(overflow) > labs(twopass->overflow)) @@ -930,19 +1019,26 @@ } // Foxer: make sure overflow doesn't run away - if (overflow > bytes2 * 6 / 10) + if (overflow > bytes2 * codec->config.twopass_max_overflow_improvement / 100) { - bytes2 += (overflow <= bytes2) ? bytes2 * 6 / 10 : overflow * 6 / 10; + bytes2 += (overflow <= bytes2) ? bytes2 * codec->config.twopass_max_overflow_improvement / 100 : + overflow * codec->config.twopass_max_overflow_improvement / 100; } - else if (overflow < bytes2 * -6 / 10) + else if (overflow < bytes2 * codec->config.twopass_max_overflow_degradation / -100) { - bytes2 += bytes2 * -6 / 10; + bytes2 += bytes2 * codec->config.twopass_max_overflow_degradation / -100; } else { bytes2 += overflow; } + if (bytes2 > twopass->max_framesize) + { + capped_to_max_framesize = 1; + bytes2 = twopass->max_framesize; + } + if (bytes2 < 1) { bytes2 = 1; @@ -1006,7 +1102,7 @@ } // subsequent frame quants can only be +- 2 - if (last_quant) + if (last_quant && capped_to_max_framesize == 0) { if (frame->quant > last_quant + 2) { @@ -1021,7 +1117,8 @@ } } - last_quant = frame->quant; + if (capped_to_max_framesize == 0) + last_quant = frame->quant; if (codec->config.quant_type == QUANT_MODE_MOD) { @@ -1039,7 +1136,7 @@ NNSTATS nns1; DWORD wrote; - int credits_pos; + int credits_pos, tempdiv; char* quant_type; if (codec->framenum == 0) @@ -1062,7 +1159,8 @@ nns1.md_u = nns1.md_y = 0; nns1.mk_u = nns1.mk_y = 0; - nns1.quant = stats->quant; +// nns1.quant = stats->quant; + nns1.quant = 2; // ugly fix for lumi masking in 1st pass returning new quant if (frame->intra) { nns1.quant |= NNSTATS_KEYFRAME; @@ -1085,11 +1183,54 @@ case DLG_MODE_2PASS_2_INT : case DLG_MODE_2PASS_2_EXT : - codec->twopass.overflow += codec->twopass.desired_bytes2 - frame->length; - credits_pos = codec_is_in_credits(&codec->config, codec->framenum); if (!credits_pos) + { codec->twopass.quant_count[frame->quant]++; + if ((codec->twopass.nns1.quant & NNSTATS_KEYFRAME)) + { + // calculate how much to distribute per frame in + // order to make up for this keyframe's overflow + + codec->twopass.overflow += codec->twopass.KFoverflow; + codec->twopass.KFoverflow = codec->twopass.desired_bytes2 - frame->length; + + tempdiv = (codec->twopass.keyframe_locations[codec->twopass.KF_idx] - + codec->twopass.keyframe_locations[codec->twopass.KF_idx - 1]); + + if (tempdiv > 1) + { + // non-consecutive keyframes + codec->twopass.KFoverflow_partial = codec->twopass.KFoverflow / (tempdiv - 1); + } + else + { + // consecutive keyframes + codec->twopass.overflow += codec->twopass.KFoverflow; + codec->twopass.KFoverflow = 0; + codec->twopass.KFoverflow_partial = 0; + } + codec->twopass.KF_idx++; + } + else + { + // distribute part of the keyframe overflow + + codec->twopass.overflow += codec->twopass.desired_bytes2 - frame->length + + codec->twopass.KFoverflow_partial; + codec->twopass.KFoverflow -= codec->twopass.KFoverflow_partial; + } + } + else + { + codec->twopass.overflow += codec->twopass.desired_bytes2 - frame->length; + + // ugly fix for credits.. + codec->twopass.overflow += codec->twopass.KFoverflow; + codec->twopass.KFoverflow = 0; + codec->twopass.KFoverflow_partial = 0; + // end of ugly fix. + } DEBUG2ND(frame->quant, quant_type, frame->intra, codec->twopass.bytes1, codec->twopass.desired_bytes2, frame->length, codec->twopass.overflow, credits_pos) break;