Path: blob/master/thirdparty/libwebp/src/enc/frame_enc.c
9913 views
// Copyright 2011 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// frame coding and analysis10//11// Author: Skal ([email protected])1213#include <string.h>14#include <math.h>1516#include "src/enc/cost_enc.h"17#include "src/enc/vp8i_enc.h"18#include "src/dsp/dsp.h"19#include "src/webp/format_constants.h" // RIFF constants2021#define SEGMENT_VISU 022#define DEBUG_SEARCH 0 // useful to track search convergence2324//------------------------------------------------------------------------------25// multi-pass convergence2627#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \28VP8_FRAME_HEADER_SIZE)29#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT30// we allow 2k of extra head-room in PARTITION0 limit.31#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)3233static float Clamp(float v, float min, float max) {34return (v < min) ? min : (v > max) ? max : v;35}3637typedef struct { // struct for organizing convergence in either size or PSNR38int is_first;39float dq;40float q, last_q;41float qmin, qmax;42double value, last_value; // PSNR or size43double target;44int do_size_search;45} PassStats;4647static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {48const uint64_t target_size = (uint64_t)enc->config_->target_size;49const int do_size_search = (target_size != 0);50const float target_PSNR = enc->config_->target_PSNR;5152s->is_first = 1;53s->dq = 10.f;54s->qmin = 1.f * enc->config_->qmin;55s->qmax = 1.f * enc->config_->qmax;56s->q = s->last_q = Clamp(enc->config_->quality, s->qmin, s->qmax);57s->target = do_size_search ? (double)target_size58: (target_PSNR > 0.) ? target_PSNR59: 40.; // default, just in case60s->value = s->last_value = 0.;61s->do_size_search = do_size_search;62return do_size_search;63}6465static float ComputeNextQ(PassStats* const s) {66float dq;67if (s->is_first) {68dq = (s->value > s->target) ? -s->dq : s->dq;69s->is_first = 0;70} else if (s->value != s->last_value) {71const double slope = (s->target - s->value) / (s->last_value - s->value);72dq = (float)(slope * (s->last_q - s->q));73} else {74dq = 0.; // we're done?!75}76// Limit variable to avoid large swings.77s->dq = Clamp(dq, -30.f, 30.f);78s->last_q = s->q;79s->last_value = s->value;80s->q = Clamp(s->q + s->dq, s->qmin, s->qmax);81return s->q;82}8384//------------------------------------------------------------------------------85// Tables for level coding8687const uint8_t VP8Cat3[] = { 173, 148, 140 };88const uint8_t VP8Cat4[] = { 176, 155, 140, 135 };89const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 };90const uint8_t VP8Cat6[] =91{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };9293//------------------------------------------------------------------------------94// Reset the statistics about: number of skips, token proba, level cost,...9596static void ResetStats(VP8Encoder* const enc) {97VP8EncProba* const proba = &enc->proba_;98VP8CalculateLevelCosts(proba);99proba->nb_skip_ = 0;100}101102//------------------------------------------------------------------------------103// Skip decision probability104105#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.106107static int CalcSkipProba(uint64_t nb, uint64_t total) {108return (int)(total ? (total - nb) * 255 / total : 255);109}110111// Returns the bit-cost for coding the skip probability.112static int FinalizeSkipProba(VP8Encoder* const enc) {113VP8EncProba* const proba = &enc->proba_;114const int nb_mbs = enc->mb_w_ * enc->mb_h_;115const int nb_events = proba->nb_skip_;116int size;117proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);118proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);119size = 256; // 'use_skip_proba' bit120if (proba->use_skip_proba_) {121size += nb_events * VP8BitCost(1, proba->skip_proba_)122+ (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_);123size += 8 * 256; // cost of signaling the skip_proba_ itself.124}125return size;126}127128// Collect statistics and deduce probabilities for next coding pass.129// Return the total bit-cost for coding the probability updates.130static int CalcTokenProba(int nb, int total) {131assert(nb <= total);132return nb ? (255 - nb * 255 / total) : 255;133}134135// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.136static int BranchCost(int nb, int total, int proba) {137return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);138}139140static void ResetTokenStats(VP8Encoder* const enc) {141VP8EncProba* const proba = &enc->proba_;142memset(proba->stats_, 0, sizeof(proba->stats_));143}144145static int FinalizeTokenProbas(VP8EncProba* const proba) {146int has_changed = 0;147int size = 0;148int t, b, c, p;149for (t = 0; t < NUM_TYPES; ++t) {150for (b = 0; b < NUM_BANDS; ++b) {151for (c = 0; c < NUM_CTX; ++c) {152for (p = 0; p < NUM_PROBAS; ++p) {153const proba_t stats = proba->stats_[t][b][c][p];154const int nb = (stats >> 0) & 0xffff;155const int total = (stats >> 16) & 0xffff;156const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];157const int old_p = VP8CoeffsProba0[t][b][c][p];158const int new_p = CalcTokenProba(nb, total);159const int old_cost = BranchCost(nb, total, old_p)160+ VP8BitCost(0, update_proba);161const int new_cost = BranchCost(nb, total, new_p)162+ VP8BitCost(1, update_proba)163+ 8 * 256;164const int use_new_p = (old_cost > new_cost);165size += VP8BitCost(use_new_p, update_proba);166if (use_new_p) { // only use proba that seem meaningful enough.167proba->coeffs_[t][b][c][p] = new_p;168has_changed |= (new_p != old_p);169size += 8 * 256;170} else {171proba->coeffs_[t][b][c][p] = old_p;172}173}174}175}176}177proba->dirty_ = has_changed;178return size;179}180181//------------------------------------------------------------------------------182// Finalize Segment probability based on the coding tree183184static int GetProba(int a, int b) {185const int total = a + b;186return (total == 0) ? 255 // that's the default probability.187: (255 * a + total / 2) / total; // rounded proba188}189190static void ResetSegments(VP8Encoder* const enc) {191int n;192for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {193enc->mb_info_[n].segment_ = 0;194}195}196197static void SetSegmentProbas(VP8Encoder* const enc) {198int p[NUM_MB_SEGMENTS] = { 0 };199int n;200201for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {202const VP8MBInfo* const mb = &enc->mb_info_[n];203++p[mb->segment_];204}205#if !defined(WEBP_DISABLE_STATS)206if (enc->pic_->stats != NULL) {207for (n = 0; n < NUM_MB_SEGMENTS; ++n) {208enc->pic_->stats->segment_size[n] = p[n];209}210}211#endif212if (enc->segment_hdr_.num_segments_ > 1) {213uint8_t* const probas = enc->proba_.segments_;214probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);215probas[1] = GetProba(p[0], p[1]);216probas[2] = GetProba(p[2], p[3]);217218enc->segment_hdr_.update_map_ =219(probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);220if (!enc->segment_hdr_.update_map_) ResetSegments(enc);221enc->segment_hdr_.size_ =222p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +223p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +224p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +225p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));226} else {227enc->segment_hdr_.update_map_ = 0;228enc->segment_hdr_.size_ = 0;229}230}231232//------------------------------------------------------------------------------233// Coefficient coding234235static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) {236int n = res->first;237// should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1238const uint8_t* p = res->prob[n][ctx];239if (!VP8PutBit(bw, res->last >= 0, p[0])) {240return 0;241}242243while (n < 16) {244const int c = res->coeffs[n++];245const int sign = c < 0;246int v = sign ? -c : c;247if (!VP8PutBit(bw, v != 0, p[1])) {248p = res->prob[VP8EncBands[n]][0];249continue;250}251if (!VP8PutBit(bw, v > 1, p[2])) {252p = res->prob[VP8EncBands[n]][1];253} else {254if (!VP8PutBit(bw, v > 4, p[3])) {255if (VP8PutBit(bw, v != 2, p[4])) {256VP8PutBit(bw, v == 4, p[5]);257}258} else if (!VP8PutBit(bw, v > 10, p[6])) {259if (!VP8PutBit(bw, v > 6, p[7])) {260VP8PutBit(bw, v == 6, 159);261} else {262VP8PutBit(bw, v >= 9, 165);263VP8PutBit(bw, !(v & 1), 145);264}265} else {266int mask;267const uint8_t* tab;268if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)269VP8PutBit(bw, 0, p[8]);270VP8PutBit(bw, 0, p[9]);271v -= 3 + (8 << 0);272mask = 1 << 2;273tab = VP8Cat3;274} else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)275VP8PutBit(bw, 0, p[8]);276VP8PutBit(bw, 1, p[9]);277v -= 3 + (8 << 1);278mask = 1 << 3;279tab = VP8Cat4;280} else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)281VP8PutBit(bw, 1, p[8]);282VP8PutBit(bw, 0, p[10]);283v -= 3 + (8 << 2);284mask = 1 << 4;285tab = VP8Cat5;286} else { // VP8Cat6 (11b)287VP8PutBit(bw, 1, p[8]);288VP8PutBit(bw, 1, p[10]);289v -= 3 + (8 << 3);290mask = 1 << 10;291tab = VP8Cat6;292}293while (mask) {294VP8PutBit(bw, !!(v & mask), *tab++);295mask >>= 1;296}297}298p = res->prob[VP8EncBands[n]][2];299}300VP8PutBitUniform(bw, sign);301if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) {302return 1; // EOB303}304}305return 1;306}307308static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it,309const VP8ModeScore* const rd) {310int x, y, ch;311VP8Residual res;312uint64_t pos1, pos2, pos3;313const int i16 = (it->mb_->type_ == 1);314const int segment = it->mb_->segment_;315VP8Encoder* const enc = it->enc_;316317VP8IteratorNzToBytes(it);318319pos1 = VP8BitWriterPos(bw);320if (i16) {321VP8InitResidual(0, 1, enc, &res);322VP8SetResidualCoeffs(rd->y_dc_levels, &res);323it->top_nz_[8] = it->left_nz_[8] =324PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res);325VP8InitResidual(1, 0, enc, &res);326} else {327VP8InitResidual(0, 3, enc, &res);328}329330// luma-AC331for (y = 0; y < 4; ++y) {332for (x = 0; x < 4; ++x) {333const int ctx = it->top_nz_[x] + it->left_nz_[y];334VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);335it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res);336}337}338pos2 = VP8BitWriterPos(bw);339340// U/V341VP8InitResidual(0, 2, enc, &res);342for (ch = 0; ch <= 2; ch += 2) {343for (y = 0; y < 2; ++y) {344for (x = 0; x < 2; ++x) {345const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];346VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);347it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =348PutCoeffs(bw, ctx, &res);349}350}351}352pos3 = VP8BitWriterPos(bw);353it->luma_bits_ = pos2 - pos1;354it->uv_bits_ = pos3 - pos2;355it->bit_count_[segment][i16] += it->luma_bits_;356it->bit_count_[segment][2] += it->uv_bits_;357VP8IteratorBytesToNz(it);358}359360// Same as CodeResiduals, but doesn't actually write anything.361// Instead, it just records the event distribution.362static void RecordResiduals(VP8EncIterator* const it,363const VP8ModeScore* const rd) {364int x, y, ch;365VP8Residual res;366VP8Encoder* const enc = it->enc_;367368VP8IteratorNzToBytes(it);369370if (it->mb_->type_ == 1) { // i16x16371VP8InitResidual(0, 1, enc, &res);372VP8SetResidualCoeffs(rd->y_dc_levels, &res);373it->top_nz_[8] = it->left_nz_[8] =374VP8RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res);375VP8InitResidual(1, 0, enc, &res);376} else {377VP8InitResidual(0, 3, enc, &res);378}379380// luma-AC381for (y = 0; y < 4; ++y) {382for (x = 0; x < 4; ++x) {383const int ctx = it->top_nz_[x] + it->left_nz_[y];384VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);385it->top_nz_[x] = it->left_nz_[y] = VP8RecordCoeffs(ctx, &res);386}387}388389// U/V390VP8InitResidual(0, 2, enc, &res);391for (ch = 0; ch <= 2; ch += 2) {392for (y = 0; y < 2; ++y) {393for (x = 0; x < 2; ++x) {394const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];395VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);396it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =397VP8RecordCoeffs(ctx, &res);398}399}400}401402VP8IteratorBytesToNz(it);403}404405//------------------------------------------------------------------------------406// Token buffer407408#if !defined(DISABLE_TOKEN_BUFFER)409410static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,411VP8TBuffer* const tokens) {412int x, y, ch;413VP8Residual res;414VP8Encoder* const enc = it->enc_;415416VP8IteratorNzToBytes(it);417if (it->mb_->type_ == 1) { // i16x16418const int ctx = it->top_nz_[8] + it->left_nz_[8];419VP8InitResidual(0, 1, enc, &res);420VP8SetResidualCoeffs(rd->y_dc_levels, &res);421it->top_nz_[8] = it->left_nz_[8] =422VP8RecordCoeffTokens(ctx, &res, tokens);423VP8InitResidual(1, 0, enc, &res);424} else {425VP8InitResidual(0, 3, enc, &res);426}427428// luma-AC429for (y = 0; y < 4; ++y) {430for (x = 0; x < 4; ++x) {431const int ctx = it->top_nz_[x] + it->left_nz_[y];432VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);433it->top_nz_[x] = it->left_nz_[y] =434VP8RecordCoeffTokens(ctx, &res, tokens);435}436}437438// U/V439VP8InitResidual(0, 2, enc, &res);440for (ch = 0; ch <= 2; ch += 2) {441for (y = 0; y < 2; ++y) {442for (x = 0; x < 2; ++x) {443const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];444VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);445it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =446VP8RecordCoeffTokens(ctx, &res, tokens);447}448}449}450VP8IteratorBytesToNz(it);451return !tokens->error_;452}453454#endif // !DISABLE_TOKEN_BUFFER455456//------------------------------------------------------------------------------457// ExtraInfo map / Debug function458459#if !defined(WEBP_DISABLE_STATS)460461#if SEGMENT_VISU462static void SetBlock(uint8_t* p, int value, int size) {463int y;464for (y = 0; y < size; ++y) {465memset(p, value, size);466p += BPS;467}468}469#endif470471static void ResetSSE(VP8Encoder* const enc) {472enc->sse_[0] = 0;473enc->sse_[1] = 0;474enc->sse_[2] = 0;475// Note: enc->sse_[3] is managed by alpha.c476enc->sse_count_ = 0;477}478479static void StoreSSE(const VP8EncIterator* const it) {480VP8Encoder* const enc = it->enc_;481const uint8_t* const in = it->yuv_in_;482const uint8_t* const out = it->yuv_out_;483// Note: not totally accurate at boundary. And doesn't include in-loop filter.484enc->sse_[0] += VP8SSE16x16(in + Y_OFF_ENC, out + Y_OFF_ENC);485enc->sse_[1] += VP8SSE8x8(in + U_OFF_ENC, out + U_OFF_ENC);486enc->sse_[2] += VP8SSE8x8(in + V_OFF_ENC, out + V_OFF_ENC);487enc->sse_count_ += 16 * 16;488}489490static void StoreSideInfo(const VP8EncIterator* const it) {491VP8Encoder* const enc = it->enc_;492const VP8MBInfo* const mb = it->mb_;493WebPPicture* const pic = enc->pic_;494495if (pic->stats != NULL) {496StoreSSE(it);497enc->block_count_[0] += (mb->type_ == 0);498enc->block_count_[1] += (mb->type_ == 1);499enc->block_count_[2] += (mb->skip_ != 0);500}501502if (pic->extra_info != NULL) {503uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_];504switch (pic->extra_info_type) {505case 1: *info = mb->type_; break;506case 2: *info = mb->segment_; break;507case 3: *info = enc->dqm_[mb->segment_].quant_; break;508case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break;509case 5: *info = mb->uv_mode_; break;510case 6: {511const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3);512*info = (b > 255) ? 255 : b; break;513}514case 7: *info = mb->alpha_; break;515default: *info = 0; break;516}517}518#if SEGMENT_VISU // visualize segments and prediction modes519SetBlock(it->yuv_out_ + Y_OFF_ENC, mb->segment_ * 64, 16);520SetBlock(it->yuv_out_ + U_OFF_ENC, it->preds_[0] * 64, 8);521SetBlock(it->yuv_out_ + V_OFF_ENC, mb->uv_mode_ * 64, 8);522#endif523}524525static void ResetSideInfo(const VP8EncIterator* const it) {526VP8Encoder* const enc = it->enc_;527WebPPicture* const pic = enc->pic_;528if (pic->stats != NULL) {529memset(enc->block_count_, 0, sizeof(enc->block_count_));530}531ResetSSE(enc);532}533#else // defined(WEBP_DISABLE_STATS)534static void ResetSSE(VP8Encoder* const enc) {535(void)enc;536}537static void StoreSideInfo(const VP8EncIterator* const it) {538VP8Encoder* const enc = it->enc_;539WebPPicture* const pic = enc->pic_;540if (pic->extra_info != NULL) {541if (it->x_ == 0 && it->y_ == 0) { // only do it once, at start542memset(pic->extra_info, 0,543enc->mb_w_ * enc->mb_h_ * sizeof(*pic->extra_info));544}545}546}547548static void ResetSideInfo(const VP8EncIterator* const it) {549(void)it;550}551#endif // !defined(WEBP_DISABLE_STATS)552553static double GetPSNR(uint64_t mse, uint64_t size) {554return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99;555}556557//------------------------------------------------------------------------------558// StatLoop(): only collect statistics (number of skips, token usage, ...).559// This is used for deciding optimal probabilities. It also modifies the560// quantizer value if some target (size, PSNR) was specified.561562static void SetLoopParams(VP8Encoder* const enc, float q) {563// Make sure the quality parameter is inside valid bounds564q = Clamp(q, 0.f, 100.f);565566VP8SetSegmentParams(enc, q); // setup segment quantizations and filters567SetSegmentProbas(enc); // compute segment probabilities568569ResetStats(enc);570ResetSSE(enc);571}572573static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,574int nb_mbs, int percent_delta,575PassStats* const s) {576VP8EncIterator it;577uint64_t size = 0;578uint64_t size_p0 = 0;579uint64_t distortion = 0;580const uint64_t pixel_count = (uint64_t)nb_mbs * 384;581582VP8IteratorInit(enc, &it);583SetLoopParams(enc, s->q);584do {585VP8ModeScore info;586VP8IteratorImport(&it, NULL);587if (VP8Decimate(&it, &info, rd_opt)) {588// Just record the number of skips and act like skip_proba is not used.589++enc->proba_.nb_skip_;590}591RecordResiduals(&it, &info);592size += info.R + info.H;593size_p0 += info.H;594distortion += info.D;595if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) {596return 0;597}598VP8IteratorSaveBoundary(&it);599} while (VP8IteratorNext(&it) && --nb_mbs > 0);600601size_p0 += enc->segment_hdr_.size_;602if (s->do_size_search) {603size += FinalizeSkipProba(enc);604size += FinalizeTokenProbas(&enc->proba_);605size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE;606s->value = (double)size;607} else {608s->value = GetPSNR(distortion, pixel_count);609}610return size_p0;611}612613static int StatLoop(VP8Encoder* const enc) {614const int method = enc->method_;615const int do_search = enc->do_search_;616const int fast_probe = ((method == 0 || method == 3) && !do_search);617int num_pass_left = enc->config_->pass;618const int task_percent = 20;619const int percent_per_pass =620(task_percent + num_pass_left / 2) / num_pass_left;621const int final_percent = enc->percent_ + task_percent;622const VP8RDLevel rd_opt =623(method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE;624int nb_mbs = enc->mb_w_ * enc->mb_h_;625PassStats stats;626627InitPassStats(enc, &stats);628ResetTokenStats(enc);629630// Fast mode: quick analysis pass over few mbs. Better than nothing.631if (fast_probe) {632if (method == 3) { // we need more stats for method 3 to be reliable.633nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;634} else {635nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;636}637}638639while (num_pass_left-- > 0) {640const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||641(num_pass_left == 0) ||642(enc->max_i4_header_bits_ == 0);643const uint64_t size_p0 =644OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats);645if (size_p0 == 0) return 0;646#if (DEBUG_SEARCH > 0)647printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n",648num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q);649#endif650if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {651++num_pass_left;652enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...653continue; // ...and start over654}655if (is_last_pass) {656break;657}658// If no target size: just do several pass without changing 'q'659if (do_search) {660ComputeNextQ(&stats);661if (fabs(stats.dq) <= DQ_LIMIT) break;662}663}664if (!do_search || !stats.do_size_search) {665// Need to finalize probas now, since it wasn't done during the search.666FinalizeSkipProba(enc);667FinalizeTokenProbas(&enc->proba_);668}669VP8CalculateLevelCosts(&enc->proba_); // finalize costs670return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);671}672673//------------------------------------------------------------------------------674// Main loops675//676677static const uint8_t kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 };678679static int PreLoopInitialize(VP8Encoder* const enc) {680int p;681int ok = 1;682const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4];683const int bytes_per_parts =684enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_;685// Initialize the bit-writers686for (p = 0; ok && p < enc->num_parts_; ++p) {687ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);688}689if (!ok) {690VP8EncFreeBitWriters(enc); // malloc error occurred691return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);692}693return ok;694}695696static int PostLoopFinalize(VP8EncIterator* const it, int ok) {697VP8Encoder* const enc = it->enc_;698if (ok) { // Finalize the partitions, check for extra errors.699int p;700for (p = 0; p < enc->num_parts_; ++p) {701VP8BitWriterFinish(enc->parts_ + p);702ok &= !enc->parts_[p].error_;703}704}705706if (ok) { // All good. Finish up.707#if !defined(WEBP_DISABLE_STATS)708if (enc->pic_->stats != NULL) { // finalize byte counters...709int i, s;710for (i = 0; i <= 2; ++i) {711for (s = 0; s < NUM_MB_SEGMENTS; ++s) {712enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3);713}714}715}716#endif717VP8AdjustFilterStrength(it); // ...and store filter stats.718} else {719// Something bad happened -> need to do some memory cleanup.720VP8EncFreeBitWriters(enc);721return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);722}723return ok;724}725726//------------------------------------------------------------------------------727// VP8EncLoop(): does the final bitstream coding.728729static void ResetAfterSkip(VP8EncIterator* const it) {730if (it->mb_->type_ == 1) {731*it->nz_ = 0; // reset all predictors732it->left_nz_[8] = 0;733} else {734*it->nz_ &= (1 << 24); // preserve the dc_nz bit735}736}737738int VP8EncLoop(VP8Encoder* const enc) {739VP8EncIterator it;740int ok = PreLoopInitialize(enc);741if (!ok) return 0;742743StatLoop(enc); // stats-collection loop744745VP8IteratorInit(enc, &it);746VP8InitFilter(&it);747do {748VP8ModeScore info;749const int dont_use_skip = !enc->proba_.use_skip_proba_;750const VP8RDLevel rd_opt = enc->rd_opt_level_;751752VP8IteratorImport(&it, NULL);753// Warning! order is important: first call VP8Decimate() and754// *then* decide how to code the skip decision if there's one.755if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {756CodeResiduals(it.bw_, &it, &info);757if (it.bw_->error_) {758// enc->pic_->error_code is set in PostLoopFinalize().759ok = 0;760break;761}762} else { // reset predictors after a skip763ResetAfterSkip(&it);764}765StoreSideInfo(&it);766VP8StoreFilterStats(&it);767VP8IteratorExport(&it);768ok = VP8IteratorProgress(&it, 20);769VP8IteratorSaveBoundary(&it);770} while (ok && VP8IteratorNext(&it));771772return PostLoopFinalize(&it, ok);773}774775//------------------------------------------------------------------------------776// Single pass using Token Buffer.777778#if !defined(DISABLE_TOKEN_BUFFER)779780#define MIN_COUNT 96 // minimum number of macroblocks before updating stats781782int VP8EncTokenLoop(VP8Encoder* const enc) {783// Roughly refresh the proba eight times per pass784int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;785int num_pass_left = enc->config_->pass;786int remaining_progress = 40; // percents787const int do_search = enc->do_search_;788VP8EncIterator it;789VP8EncProba* const proba = &enc->proba_;790const VP8RDLevel rd_opt = enc->rd_opt_level_;791const uint64_t pixel_count = (uint64_t)enc->mb_w_ * enc->mb_h_ * 384;792PassStats stats;793int ok;794795InitPassStats(enc, &stats);796ok = PreLoopInitialize(enc);797if (!ok) return 0;798799if (max_count < MIN_COUNT) max_count = MIN_COUNT;800801assert(enc->num_parts_ == 1);802assert(enc->use_tokens_);803assert(proba->use_skip_proba_ == 0);804assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful805assert(num_pass_left > 0);806807while (ok && num_pass_left-- > 0) {808const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||809(num_pass_left == 0) ||810(enc->max_i4_header_bits_ == 0);811uint64_t size_p0 = 0;812uint64_t distortion = 0;813int cnt = max_count;814// The final number of passes is not trivial to know in advance.815const int pass_progress = remaining_progress / (2 + num_pass_left);816remaining_progress -= pass_progress;817VP8IteratorInit(enc, &it);818SetLoopParams(enc, stats.q);819if (is_last_pass) {820ResetTokenStats(enc);821VP8InitFilter(&it); // don't collect stats until last pass (too costly)822}823VP8TBufferClear(&enc->tokens_);824do {825VP8ModeScore info;826VP8IteratorImport(&it, NULL);827if (--cnt < 0) {828FinalizeTokenProbas(proba);829VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt830cnt = max_count;831}832VP8Decimate(&it, &info, rd_opt);833ok = RecordTokens(&it, &info, &enc->tokens_);834if (!ok) {835WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);836break;837}838size_p0 += info.H;839distortion += info.D;840if (is_last_pass) {841StoreSideInfo(&it);842VP8StoreFilterStats(&it);843VP8IteratorExport(&it);844ok = VP8IteratorProgress(&it, pass_progress);845}846VP8IteratorSaveBoundary(&it);847} while (ok && VP8IteratorNext(&it));848if (!ok) break;849850size_p0 += enc->segment_hdr_.size_;851if (stats.do_size_search) {852uint64_t size = FinalizeTokenProbas(&enc->proba_);853size += VP8EstimateTokenSize(&enc->tokens_,854(const uint8_t*)proba->coeffs_);855size = (size + size_p0 + 1024) >> 11; // -> size in bytes856size += HEADER_SIZE_ESTIMATE;857stats.value = (double)size;858} else { // compute and store PSNR859stats.value = GetPSNR(distortion, pixel_count);860}861862#if (DEBUG_SEARCH > 0)863printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf "864" range:[%.1f, %.1f]\n",865num_pass_left, stats.last_value, stats.value,866stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax);867#endif868if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {869++num_pass_left;870enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...871if (is_last_pass) {872ResetSideInfo(&it);873}874continue; // ...and start over875}876if (is_last_pass) {877break; // done878}879if (do_search) {880ComputeNextQ(&stats); // Adjust q881}882}883if (ok) {884if (!stats.do_size_search) {885FinalizeTokenProbas(&enc->proba_);886}887ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,888(const uint8_t*)proba->coeffs_, 1);889}890ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + remaining_progress,891&enc->percent_);892return PostLoopFinalize(&it, ok);893}894895#else896897int VP8EncTokenLoop(VP8Encoder* const enc) {898(void)enc;899return 0; // we shouldn't be here.900}901902#endif // DISABLE_TOKEN_BUFFER903904//------------------------------------------------------------------------------905906907