// Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // frame coding and analysis // // Author: Skal (pascal.massimino@gmail.com)
#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \
VP8_FRAME_HEADER_SIZE) #define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT // we allow 2k of extra head-room in PARTITION0 limit. #define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
staticfloat Clamp(float v, float min, float max) { return (v < min) ? min : (v > max) ? max : v;
}
typedefstruct { // struct for organizing convergence in either size or PSNR int is_first; float dq; float q, last_q; float qmin, qmax; double value, last_value; // PSNR or size double target; int do_size_search;
} PassStats;
//------------------------------------------------------------------------------ // Reset the statistics about: number of skips, token proba, level cost,...
// Returns the bit-cost for coding the skip probability. staticint FinalizeSkipProba(VP8Encoder* const enc) {
VP8EncProba* const proba = &enc->proba_; constint nb_mbs = enc->mb_w_ * enc->mb_h_; constint nb_events = proba->nb_skip_; int size;
proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);
proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);
size = 256; // 'use_skip_proba' bit if (proba->use_skip_proba_) {
size += nb_events * VP8BitCost(1, proba->skip_proba_)
+ (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_);
size += 8 * 256; // cost of signaling the skip_proba_ itself.
} return size;
}
// Collect statistics and deduce probabilities for next coding pass. // Return the total bit-cost for coding the probability updates. staticint CalcTokenProba(int nb, int total) {
assert(nb <= total); return nb ? (255 - nb * 255 / total) : 255;
}
// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability. staticint BranchCost(int nb, int total, int proba) { return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
}
staticint FinalizeTokenProbas(VP8EncProba* const proba) { int has_changed = 0; int size = 0; int t, b, c, p; for (t = 0; t < NUM_TYPES; ++t) { for (b = 0; b < NUM_BANDS; ++b) { for (c = 0; c < NUM_CTX; ++c) { for (p = 0; p < NUM_PROBAS; ++p) { const proba_t stats = proba->stats_[t][b][c][p]; constint nb = (stats >> 0) & 0xffff; constint total = (stats >> 16) & 0xffff; constint update_proba = VP8CoeffsUpdateProba[t][b][c][p]; constint old_p = VP8CoeffsProba0[t][b][c][p]; constint new_p = CalcTokenProba(nb, total); constint old_cost = BranchCost(nb, total, old_p)
+ VP8BitCost(0, update_proba); constint new_cost = BranchCost(nb, total, new_p)
+ VP8BitCost(1, update_proba)
+ 8 * 256; constint use_new_p = (old_cost > new_cost);
size += VP8BitCost(use_new_p, update_proba); if (use_new_p) { // only use proba that seem meaningful enough.
proba->coeffs_[t][b][c][p] = new_p;
has_changed |= (new_p != old_p);
size += 8 * 256;
} else {
proba->coeffs_[t][b][c][p] = old_p;
}
}
}
}
}
proba->dirty_ = has_changed; return size;
}
//------------------------------------------------------------------------------ // Finalize Segment probability based on the coding tree
staticint GetProba(int a, int b) { constint total = a + b; return (total == 0) ? 255 // that's the default probability.
: (255 * a + total / 2) / total; // rounded proba
}
staticvoid ResetSegments(VP8Encoder* const enc) { int n; for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
enc->mb_info_[n].segment_ = 0;
}
}
staticvoid SetSegmentProbas(VP8Encoder* const enc) { int p[NUM_MB_SEGMENTS] = { 0 }; int n;
staticint PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { int n = res->first; // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1 const uint8_t* p = res->prob[n][ctx]; if (!VP8PutBit(bw, res->last >= 0, p[0])) { return 0;
}
while (n < 16) { constint c = res->coeffs[n++]; constint sign = c < 0; int v = sign ? -c : c; if (!VP8PutBit(bw, v != 0, p[1])) {
p = res->prob[VP8EncBands[n]][0]; continue;
} if (!VP8PutBit(bw, v > 1, p[2])) {
p = res->prob[VP8EncBands[n]][1];
} else { if (!VP8PutBit(bw, v > 4, p[3])) { if (VP8PutBit(bw, v != 2, p[4])) {
VP8PutBit(bw, v == 4, p[5]);
}
} elseif (!VP8PutBit(bw, v > 10, p[6])) { if (!VP8PutBit(bw, v > 6, p[7])) {
VP8PutBit(bw, v == 6, 159);
} else {
VP8PutBit(bw, v >= 9, 165);
VP8PutBit(bw, !(v & 1), 145);
}
} else { int mask; const uint8_t* tab; if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
VP8PutBit(bw, 0, p[8]);
VP8PutBit(bw, 0, p[9]);
v -= 3 + (8 << 0);
mask = 1 << 2;
tab = VP8Cat3;
} elseif (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
VP8PutBit(bw, 0, p[8]);
VP8PutBit(bw, 1, p[9]);
v -= 3 + (8 << 1);
mask = 1 << 3;
tab = VP8Cat4;
} elseif (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
VP8PutBit(bw, 1, p[8]);
VP8PutBit(bw, 0, p[10]);
v -= 3 + (8 << 2);
mask = 1 << 4;
tab = VP8Cat5;
} else { // VP8Cat6 (11b)
VP8PutBit(bw, 1, p[8]);
VP8PutBit(bw, 1, p[10]);
v -= 3 + (8 << 3);
mask = 1 << 10;
tab = VP8Cat6;
} while (mask) {
VP8PutBit(bw, !!(v & mask), *tab++);
mask >>= 1;
}
}
p = res->prob[VP8EncBands[n]][2];
}
VP8PutBitUniform(bw, sign); if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) { return 1; // EOB
}
} return 1;
}
// Same as CodeResiduals, but doesn't actually write anything. // Instead, it just records the event distribution. staticvoid RecordResiduals(VP8EncIterator* const it, const VP8ModeScore* const rd) { int x, y, ch;
VP8Residual res;
VP8Encoder* const enc = it->enc_;
//------------------------------------------------------------------------------ // ExtraInfo map / Debug function
#if !defined(WEBP_DISABLE_STATS)
#if SEGMENT_VISU staticvoid SetBlock(uint8_t* p, int value, int size) { int y; for (y = 0; y < size; ++y) {
memset(p, value, size);
p += BPS;
}
} #endif
//------------------------------------------------------------------------------ // StatLoop(): only collect statistics (number of skips, token usage, ...). // This is used for deciding optimal probabilities. It also modifies the // quantizer value if some target (size, PSNR) was specified.
staticvoid SetLoopParams(VP8Encoder* const enc, float q) { // Make sure the quality parameter is inside valid bounds
q = Clamp(q, 0.f, 100.f);
VP8IteratorInit(enc, &it);
SetLoopParams(enc, s->q); do {
VP8ModeScore info;
VP8IteratorImport(&it, NULL); if (VP8Decimate(&it, &info, rd_opt)) { // Just record the number of skips and act like skip_proba is not used.
++enc->proba_.nb_skip_;
}
RecordResiduals(&it, &info);
size += info.R + info.H;
size_p0 += info.H;
distortion += info.D; if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) { return 0;
}
VP8IteratorSaveBoundary(&it);
} while (VP8IteratorNext(&it) && --nb_mbs > 0);
// Fast mode: quick analysis pass over few mbs. Better than nothing. if (fast_probe) { if (method == 3) { // we need more stats for method 3 to be reliable.
nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
} else {
nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;
}
}
while (num_pass_left-- > 0) { constint is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
(num_pass_left == 0) ||
(enc->max_i4_header_bits_ == 0); const uint64_t size_p0 =
OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats); if (size_p0 == 0) return 0; #if (DEBUG_SEARCH > 0)
printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n",
num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q); #endif if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
++num_pass_left;
enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation... continue; // ...and start over
} if (is_last_pass) { break;
} // If no target size: just do several pass without changing 'q' if (do_search) {
ComputeNextQ(&stats); if (fabs(stats.dq) <= DQ_LIMIT) break;
}
} if (!do_search || !stats.do_size_search) { // Need to finalize probas now, since it wasn't done during the search.
FinalizeSkipProba(enc);
FinalizeTokenProbas(&enc->proba_);
}
VP8CalculateLevelCosts(&enc->proba_); // finalize costs return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
}
//------------------------------------------------------------------------------ // Main loops //
staticint PreLoopInitialize(VP8Encoder* const enc) { int p; int ok = 1; constint average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4]; constint bytes_per_parts =
enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_; // Initialize the bit-writers for (p = 0; ok && p < enc->num_parts_; ++p) {
ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
} if (!ok) {
VP8EncFreeBitWriters(enc); // malloc error occurred return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
} return ok;
}
staticint PostLoopFinalize(VP8EncIterator* const it, int ok) {
VP8Encoder* const enc = it->enc_; if (ok) { // Finalize the partitions, check for extra errors. int p; for (p = 0; p < enc->num_parts_; ++p) {
VP8BitWriterFinish(enc->parts_ + p);
ok &= !enc->parts_[p].error_;
}
}
if (ok) { // All good. Finish up. #if !defined(WEBP_DISABLE_STATS) if (enc->pic_->stats != NULL) { // finalize byte counters... int i, s; for (i = 0; i <= 2; ++i) { for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3);
}
}
} #endif
VP8AdjustFilterStrength(it); // ...and store filter stats.
} else { // Something bad happened -> need to do some memory cleanup.
VP8EncFreeBitWriters(enc); return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
} return ok;
}
//------------------------------------------------------------------------------ // VP8EncLoop(): does the final bitstream coding.
staticvoid ResetAfterSkip(VP8EncIterator* const it) { if (it->mb_->type_ == 1) {
*it->nz_ = 0; // reset all predictors
it->left_nz_[8] = 0;
} else {
*it->nz_ &= (1 << 24); // preserve the dc_nz bit
}
}
int VP8EncLoop(VP8Encoder* const enc) {
VP8EncIterator it; int ok = PreLoopInitialize(enc); if (!ok) return 0;
VP8IteratorImport(&it, NULL); // Warning! order is important: first call VP8Decimate() and // *then* decide how to code the skip decision if there's one. if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
CodeResiduals(it.bw_, &it, &info); if (it.bw_->error_) { // enc->pic_->error_code is set in PostLoopFinalize().
ok = 0; break;
}
} else { // reset predictors after a skip
ResetAfterSkip(&it);
}
StoreSideInfo(&it);
VP8StoreFilterStats(&it);
VP8IteratorExport(&it);
ok = VP8IteratorProgress(&it, 20);
VP8IteratorSaveBoundary(&it);
} while (ok && VP8IteratorNext(&it));
return PostLoopFinalize(&it, ok);
}
//------------------------------------------------------------------------------ // Single pass using Token Buffer.
#if !defined(DISABLE_TOKEN_BUFFER)
#define MIN_COUNT 96 // minimum number of macroblocks before updating stats
int VP8EncTokenLoop(VP8Encoder* const enc) { // Roughly refresh the proba eight times per pass int max_count = (enc->mb_w_ * enc->mb_h_) >> 3; int num_pass_left = enc->config_->pass; int remaining_progress = 40; // percents constint do_search = enc->do_search_;
VP8EncIterator it;
VP8EncProba* const proba = &enc->proba_; const VP8RDLevel rd_opt = enc->rd_opt_level_; const uint64_t pixel_count = (uint64_t)enc->mb_w_ * enc->mb_h_ * 384;
PassStats stats; int ok;
InitPassStats(enc, &stats);
ok = PreLoopInitialize(enc); if (!ok) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.