Path: blob/master/thirdparty/libwebp/src/enc/frame_enc.c
21653 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 <assert.h>14#include <math.h>15#include <string.h>1617#include "src/dec/common_dec.h"18#include "src/webp/types.h"19#include "src/dsp/dsp.h"20#include "src/enc/cost_enc.h"21#include "src/enc/vp8i_enc.h"22#include "src/utils/bit_writer_utils.h"23#include "src/webp/encode.h"24#include "src/webp/format_constants.h" // RIFF constants2526#define SEGMENT_VISU 027#define DEBUG_SEARCH 0 // useful to track search convergence2829//------------------------------------------------------------------------------30// multi-pass convergence3132#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \33VP8_FRAME_HEADER_SIZE)34#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT35// we allow 2k of extra head-room in PARTITION0 limit.36#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)3738static float Clamp(float v, float min, float max) {39return (v < min) ? min : (v > max) ? max : v;40}4142typedef struct { // struct for organizing convergence in either size or PSNR43int is_first;44float dq;45float q, last_q;46float qmin, qmax;47double value, last_value; // PSNR or size48double target;49int do_size_search;50} PassStats;5152static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {53const uint64_t target_size = (uint64_t)enc->config->target_size;54const int do_size_search = (target_size != 0);55const float target_PSNR = enc->config->target_PSNR;5657s->is_first = 1;58s->dq = 10.f;59s->qmin = 1.f * enc->config->qmin;60s->qmax = 1.f * enc->config->qmax;61s->q = s->last_q = Clamp(enc->config->quality, s->qmin, s->qmax);62s->target = do_size_search ? (double)target_size63: (target_PSNR > 0.) ? target_PSNR64: 40.; // default, just in case65s->value = s->last_value = 0.;66s->do_size_search = do_size_search;67return do_size_search;68}6970static float ComputeNextQ(PassStats* const s) {71float dq;72if (s->is_first) {73dq = (s->value > s->target) ? -s->dq : s->dq;74s->is_first = 0;75} else if (s->value != s->last_value) {76const double slope = (s->target - s->value) / (s->last_value - s->value);77dq = (float)(slope * (s->last_q - s->q));78} else {79dq = 0.; // we're done?!80}81// Limit variable to avoid large swings.82s->dq = Clamp(dq, -30.f, 30.f);83s->last_q = s->q;84s->last_value = s->value;85s->q = Clamp(s->q + s->dq, s->qmin, s->qmax);86return s->q;87}8889//------------------------------------------------------------------------------90// Tables for level coding9192const uint8_t VP8Cat3[] = { 173, 148, 140 };93const uint8_t VP8Cat4[] = { 176, 155, 140, 135 };94const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 };95const uint8_t VP8Cat6[] =96{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };9798//------------------------------------------------------------------------------99// Reset the statistics about: number of skips, token proba, level cost,...100101static void ResetStats(VP8Encoder* const enc) {102VP8EncProba* const proba = &enc->proba;103VP8CalculateLevelCosts(proba);104proba->nb_skip = 0;105}106107//------------------------------------------------------------------------------108// Skip decision probability109110#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.111112static int CalcSkipProba(uint64_t nb, uint64_t total) {113return (int)(total ? (total - nb) * 255 / total : 255);114}115116// Returns the bit-cost for coding the skip probability.117static int FinalizeSkipProba(VP8Encoder* const enc) {118VP8EncProba* const proba = &enc->proba;119const int nb_mbs = enc->mb_w * enc->mb_h;120const int nb_events = proba->nb_skip;121int size;122proba->skip_proba = CalcSkipProba(nb_events, nb_mbs);123proba->use_skip_proba = (proba->skip_proba < SKIP_PROBA_THRESHOLD);124size = 256; // 'use_skip_proba' bit125if (proba->use_skip_proba) {126size += nb_events * VP8BitCost(1, proba->skip_proba)127+ (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba);128size += 8 * 256; // cost of signaling the 'skip_proba' itself.129}130return size;131}132133// Collect statistics and deduce probabilities for next coding pass.134// Return the total bit-cost for coding the probability updates.135static int CalcTokenProba(int nb, int total) {136assert(nb <= total);137return nb ? (255 - nb * 255 / total) : 255;138}139140// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.141static int BranchCost(int nb, int total, int proba) {142return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);143}144145static void ResetTokenStats(VP8Encoder* const enc) {146VP8EncProba* const proba = &enc->proba;147memset(proba->stats, 0, sizeof(proba->stats));148}149150static int FinalizeTokenProbas(VP8EncProba* const proba) {151int has_changed = 0;152int size = 0;153int t, b, c, p;154for (t = 0; t < NUM_TYPES; ++t) {155for (b = 0; b < NUM_BANDS; ++b) {156for (c = 0; c < NUM_CTX; ++c) {157for (p = 0; p < NUM_PROBAS; ++p) {158const proba_t stats = proba->stats[t][b][c][p];159const int nb = (stats >> 0) & 0xffff;160const int total = (stats >> 16) & 0xffff;161const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];162const int old_p = VP8CoeffsProba0[t][b][c][p];163const int new_p = CalcTokenProba(nb, total);164const int old_cost = BranchCost(nb, total, old_p)165+ VP8BitCost(0, update_proba);166const int new_cost = BranchCost(nb, total, new_p)167+ VP8BitCost(1, update_proba)168+ 8 * 256;169const int use_new_p = (old_cost > new_cost);170size += VP8BitCost(use_new_p, update_proba);171if (use_new_p) { // only use proba that seem meaningful enough.172proba->coeffs[t][b][c][p] = new_p;173has_changed |= (new_p != old_p);174size += 8 * 256;175} else {176proba->coeffs[t][b][c][p] = old_p;177}178}179}180}181}182proba->dirty = has_changed;183return size;184}185186//------------------------------------------------------------------------------187// Finalize Segment probability based on the coding tree188189static int GetProba(int a, int b) {190const int total = a + b;191return (total == 0) ? 255 // that's the default probability.192: (255 * a + total / 2) / total; // rounded proba193}194195static void ResetSegments(VP8Encoder* const enc) {196int n;197for (n = 0; n < enc->mb_w * enc->mb_h; ++n) {198enc->mb_info[n].segment = 0;199}200}201202static void SetSegmentProbas(VP8Encoder* const enc) {203int p[NUM_MB_SEGMENTS] = { 0 };204int n;205206for (n = 0; n < enc->mb_w * enc->mb_h; ++n) {207const VP8MBInfo* const mb = &enc->mb_info[n];208++p[mb->segment];209}210#if !defined(WEBP_DISABLE_STATS)211if (enc->pic->stats != NULL) {212for (n = 0; n < NUM_MB_SEGMENTS; ++n) {213enc->pic->stats->segment_size[n] = p[n];214}215}216#endif217if (enc->segment_hdr.num_segments > 1) {218uint8_t* const probas = enc->proba.segments;219probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);220probas[1] = GetProba(p[0], p[1]);221probas[2] = GetProba(p[2], p[3]);222223enc->segment_hdr.update_map =224(probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);225if (!enc->segment_hdr.update_map) ResetSegments(enc);226enc->segment_hdr.size =227p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +228p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +229p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +230p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));231} else {232enc->segment_hdr.update_map = 0;233enc->segment_hdr.size = 0;234}235}236237//------------------------------------------------------------------------------238// Coefficient coding239240static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) {241int n = res->first;242// should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1243const uint8_t* p = res->prob[n][ctx];244if (!VP8PutBit(bw, res->last >= 0, p[0])) {245return 0;246}247248while (n < 16) {249const int c = res->coeffs[n++];250const int sign = c < 0;251int v = sign ? -c : c;252if (!VP8PutBit(bw, v != 0, p[1])) {253p = res->prob[VP8EncBands[n]][0];254continue;255}256if (!VP8PutBit(bw, v > 1, p[2])) {257p = res->prob[VP8EncBands[n]][1];258} else {259if (!VP8PutBit(bw, v > 4, p[3])) {260if (VP8PutBit(bw, v != 2, p[4])) {261VP8PutBit(bw, v == 4, p[5]);262}263} else if (!VP8PutBit(bw, v > 10, p[6])) {264if (!VP8PutBit(bw, v > 6, p[7])) {265VP8PutBit(bw, v == 6, 159);266} else {267VP8PutBit(bw, v >= 9, 165);268VP8PutBit(bw, !(v & 1), 145);269}270} else {271int mask;272const uint8_t* tab;273if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)274VP8PutBit(bw, 0, p[8]);275VP8PutBit(bw, 0, p[9]);276v -= 3 + (8 << 0);277mask = 1 << 2;278tab = VP8Cat3;279} else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)280VP8PutBit(bw, 0, p[8]);281VP8PutBit(bw, 1, p[9]);282v -= 3 + (8 << 1);283mask = 1 << 3;284tab = VP8Cat4;285} else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)286VP8PutBit(bw, 1, p[8]);287VP8PutBit(bw, 0, p[10]);288v -= 3 + (8 << 2);289mask = 1 << 4;290tab = VP8Cat5;291} else { // VP8Cat6 (11b)292VP8PutBit(bw, 1, p[8]);293VP8PutBit(bw, 1, p[10]);294v -= 3 + (8 << 3);295mask = 1 << 10;296tab = VP8Cat6;297}298while (mask) {299VP8PutBit(bw, !!(v & mask), *tab++);300mask >>= 1;301}302}303p = res->prob[VP8EncBands[n]][2];304}305VP8PutBitUniform(bw, sign);306if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) {307return 1; // EOB308}309}310return 1;311}312313static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it,314const VP8ModeScore* const rd) {315int x, y, ch;316VP8Residual res;317uint64_t pos1, pos2, pos3;318const int i16 = (it->mb->type == 1);319const int segment = it->mb->segment;320VP8Encoder* const enc = it->enc;321322VP8IteratorNzToBytes(it);323324pos1 = VP8BitWriterPos(bw);325if (i16) {326VP8InitResidual(0, 1, enc, &res);327VP8SetResidualCoeffs(rd->y_dc_levels, &res);328it->top_nz[8] = it->left_nz[8] =329PutCoeffs(bw, it->top_nz[8] + it->left_nz[8], &res);330VP8InitResidual(1, 0, enc, &res);331} else {332VP8InitResidual(0, 3, enc, &res);333}334335// luma-AC336for (y = 0; y < 4; ++y) {337for (x = 0; x < 4; ++x) {338const int ctx = it->top_nz[x] + it->left_nz[y];339VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);340it->top_nz[x] = it->left_nz[y] = PutCoeffs(bw, ctx, &res);341}342}343pos2 = VP8BitWriterPos(bw);344345// U/V346VP8InitResidual(0, 2, enc, &res);347for (ch = 0; ch <= 2; ch += 2) {348for (y = 0; y < 2; ++y) {349for (x = 0; x < 2; ++x) {350const int ctx = it->top_nz[4 + ch + x] + it->left_nz[4 + ch + y];351VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);352it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] =353PutCoeffs(bw, ctx, &res);354}355}356}357pos3 = VP8BitWriterPos(bw);358it->luma_bits = pos2 - pos1;359it->uv_bits = pos3 - pos2;360it->bit_count[segment][i16] += it->luma_bits;361it->bit_count[segment][2] += it->uv_bits;362VP8IteratorBytesToNz(it);363}364365// Same as CodeResiduals, but doesn't actually write anything.366// Instead, it just records the event distribution.367static void RecordResiduals(VP8EncIterator* const it,368const VP8ModeScore* const rd) {369int x, y, ch;370VP8Residual res;371VP8Encoder* const enc = it->enc;372373VP8IteratorNzToBytes(it);374375if (it->mb->type == 1) { // i16x16376VP8InitResidual(0, 1, enc, &res);377VP8SetResidualCoeffs(rd->y_dc_levels, &res);378it->top_nz[8] = it->left_nz[8] =379VP8RecordCoeffs(it->top_nz[8] + it->left_nz[8], &res);380VP8InitResidual(1, 0, enc, &res);381} else {382VP8InitResidual(0, 3, enc, &res);383}384385// luma-AC386for (y = 0; y < 4; ++y) {387for (x = 0; x < 4; ++x) {388const int ctx = it->top_nz[x] + it->left_nz[y];389VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);390it->top_nz[x] = it->left_nz[y] = VP8RecordCoeffs(ctx, &res);391}392}393394// U/V395VP8InitResidual(0, 2, enc, &res);396for (ch = 0; ch <= 2; ch += 2) {397for (y = 0; y < 2; ++y) {398for (x = 0; x < 2; ++x) {399const int ctx = it->top_nz[4 + ch + x] + it->left_nz[4 + ch + y];400VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);401it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] =402VP8RecordCoeffs(ctx, &res);403}404}405}406407VP8IteratorBytesToNz(it);408}409410//------------------------------------------------------------------------------411// Token buffer412413#if !defined(DISABLE_TOKEN_BUFFER)414415static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,416VP8TBuffer* const tokens) {417int x, y, ch;418VP8Residual res;419VP8Encoder* const enc = it->enc;420421VP8IteratorNzToBytes(it);422if (it->mb->type == 1) { // i16x16423const int ctx = it->top_nz[8] + it->left_nz[8];424VP8InitResidual(0, 1, enc, &res);425VP8SetResidualCoeffs(rd->y_dc_levels, &res);426it->top_nz[8] = it->left_nz[8] =427VP8RecordCoeffTokens(ctx, &res, tokens);428VP8InitResidual(1, 0, enc, &res);429} else {430VP8InitResidual(0, 3, enc, &res);431}432433// luma-AC434for (y = 0; y < 4; ++y) {435for (x = 0; x < 4; ++x) {436const int ctx = it->top_nz[x] + it->left_nz[y];437VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);438it->top_nz[x] = it->left_nz[y] =439VP8RecordCoeffTokens(ctx, &res, tokens);440}441}442443// U/V444VP8InitResidual(0, 2, enc, &res);445for (ch = 0; ch <= 2; ch += 2) {446for (y = 0; y < 2; ++y) {447for (x = 0; x < 2; ++x) {448const int ctx = it->top_nz[4 + ch + x] + it->left_nz[4 + ch + y];449VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);450it->top_nz[4 + ch + x] = it->left_nz[4 + ch + y] =451VP8RecordCoeffTokens(ctx, &res, tokens);452}453}454}455VP8IteratorBytesToNz(it);456return !tokens->error;457}458459#endif // !DISABLE_TOKEN_BUFFER460461//------------------------------------------------------------------------------462// ExtraInfo map / Debug function463464#if !defined(WEBP_DISABLE_STATS)465466#if SEGMENT_VISU467static void SetBlock(uint8_t* p, int value, int size) {468int y;469for (y = 0; y < size; ++y) {470memset(p, value, size);471p += BPS;472}473}474#endif475476static void ResetSSE(VP8Encoder* const enc) {477enc->sse[0] = 0;478enc->sse[1] = 0;479enc->sse[2] = 0;480// Note: enc->sse[3] is managed by alpha.c481enc->sse_count = 0;482}483484static void StoreSSE(const VP8EncIterator* const it) {485VP8Encoder* const enc = it->enc;486const uint8_t* const in = it->yuv_in;487const uint8_t* const out = it->yuv_out;488// Note: not totally accurate at boundary. And doesn't include in-loop filter.489enc->sse[0] += VP8SSE16x16(in + Y_OFF_ENC, out + Y_OFF_ENC);490enc->sse[1] += VP8SSE8x8(in + U_OFF_ENC, out + U_OFF_ENC);491enc->sse[2] += VP8SSE8x8(in + V_OFF_ENC, out + V_OFF_ENC);492enc->sse_count += 16 * 16;493}494495static void StoreSideInfo(const VP8EncIterator* const it) {496VP8Encoder* const enc = it->enc;497const VP8MBInfo* const mb = it->mb;498WebPPicture* const pic = enc->pic;499500if (pic->stats != NULL) {501StoreSSE(it);502enc->block_count[0] += (mb->type == 0);503enc->block_count[1] += (mb->type == 1);504enc->block_count[2] += (mb->skip != 0);505}506507if (pic->extra_info != NULL) {508uint8_t* const info = &pic->extra_info[it->x + it->y * enc->mb_w];509switch (pic->extra_info_type) {510case 1: *info = mb->type; break;511case 2: *info = mb->segment; break;512case 3: *info = enc->dqm[mb->segment].quant; break;513case 4: *info = (mb->type == 1) ? it->preds[0] : 0xff; break;514case 5: *info = mb->uv_mode; break;515case 6: {516const int b = (int)((it->luma_bits + it->uv_bits + 7) >> 3);517*info = (b > 255) ? 255 : b; break;518}519case 7: *info = mb->alpha; break;520default: *info = 0; break;521}522}523#if SEGMENT_VISU // visualize segments and prediction modes524SetBlock(it->yuv_out + Y_OFF_ENC, mb->segment * 64, 16);525SetBlock(it->yuv_out + U_OFF_ENC, it->preds[0] * 64, 8);526SetBlock(it->yuv_out + V_OFF_ENC, mb->uv_mode * 64, 8);527#endif528}529530static void ResetSideInfo(const VP8EncIterator* const it) {531VP8Encoder* const enc = it->enc;532WebPPicture* const pic = enc->pic;533if (pic->stats != NULL) {534memset(enc->block_count, 0, sizeof(enc->block_count));535}536ResetSSE(enc);537}538#else // defined(WEBP_DISABLE_STATS)539static void ResetSSE(VP8Encoder* const enc) {540(void)enc;541}542static void StoreSideInfo(const VP8EncIterator* const it) {543VP8Encoder* const enc = it->enc;544WebPPicture* const pic = enc->pic;545if (pic->extra_info != NULL) {546if (it->x == 0 && it->y == 0) { // only do it once, at start547memset(pic->extra_info, 0,548enc->mb_w * enc->mb_h * sizeof(*pic->extra_info));549}550}551}552553static void ResetSideInfo(const VP8EncIterator* const it) {554(void)it;555}556#endif // !defined(WEBP_DISABLE_STATS)557558static double GetPSNR(uint64_t mse, uint64_t size) {559return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99;560}561562//------------------------------------------------------------------------------563// StatLoop(): only collect statistics (number of skips, token usage, ...).564// This is used for deciding optimal probabilities. It also modifies the565// quantizer value if some target (size, PSNR) was specified.566567static void SetLoopParams(VP8Encoder* const enc, float q) {568// Make sure the quality parameter is inside valid bounds569q = Clamp(q, 0.f, 100.f);570571VP8SetSegmentParams(enc, q); // setup segment quantizations and filters572SetSegmentProbas(enc); // compute segment probabilities573574ResetStats(enc);575ResetSSE(enc);576}577578static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,579int nb_mbs, int percent_delta,580PassStats* const s) {581VP8EncIterator it;582uint64_t size = 0;583uint64_t size_p0 = 0;584uint64_t distortion = 0;585const uint64_t pixel_count = (uint64_t)nb_mbs * 384;586587VP8IteratorInit(enc, &it);588SetLoopParams(enc, s->q);589do {590VP8ModeScore info;591VP8IteratorImport(&it, NULL);592if (VP8Decimate(&it, &info, rd_opt)) {593// Just record the number of skips and act like skip_proba is not used.594++enc->proba.nb_skip;595}596RecordResiduals(&it, &info);597size += info.R + info.H;598size_p0 += info.H;599distortion += info.D;600if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) {601return 0;602}603VP8IteratorSaveBoundary(&it);604} while (VP8IteratorNext(&it) && --nb_mbs > 0);605606size_p0 += enc->segment_hdr.size;607if (s->do_size_search) {608size += FinalizeSkipProba(enc);609size += FinalizeTokenProbas(&enc->proba);610size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE;611s->value = (double)size;612} else {613s->value = GetPSNR(distortion, pixel_count);614}615return size_p0;616}617618static int StatLoop(VP8Encoder* const enc) {619const int method = enc->method;620const int do_search = enc->do_search;621const int fast_probe = ((method == 0 || method == 3) && !do_search);622int num_pass_left = enc->config->pass;623const int task_percent = 20;624const int percent_per_pass =625(task_percent + num_pass_left / 2) / num_pass_left;626const int final_percent = enc->percent + task_percent;627const VP8RDLevel rd_opt =628(method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE;629int nb_mbs = enc->mb_w * enc->mb_h;630PassStats stats;631632InitPassStats(enc, &stats);633ResetTokenStats(enc);634635// Fast mode: quick analysis pass over few mbs. Better than nothing.636if (fast_probe) {637if (method == 3) { // we need more stats for method 3 to be reliable.638nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;639} else {640nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;641}642}643644while (num_pass_left-- > 0) {645const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||646(num_pass_left == 0) ||647(enc->max_i4_header_bits == 0);648const uint64_t size_p0 =649OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats);650if (size_p0 == 0) return 0;651#if (DEBUG_SEARCH > 0)652printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n",653num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q);654#endif655if (enc->max_i4_header_bits > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {656++num_pass_left;657enc->max_i4_header_bits >>= 1; // strengthen header bit limitation...658continue; // ...and start over659}660if (is_last_pass) {661break;662}663// If no target size: just do several pass without changing 'q'664if (do_search) {665ComputeNextQ(&stats);666if (fabs(stats.dq) <= DQ_LIMIT) break;667}668}669if (!do_search || !stats.do_size_search) {670// Need to finalize probas now, since it wasn't done during the search.671FinalizeSkipProba(enc);672FinalizeTokenProbas(&enc->proba);673}674VP8CalculateLevelCosts(&enc->proba); // finalize costs675return WebPReportProgress(enc->pic, final_percent, &enc->percent);676}677678//------------------------------------------------------------------------------679// Main loops680//681682static const uint8_t kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 };683684static int PreLoopInitialize(VP8Encoder* const enc) {685int p;686int ok = 1;687const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant >> 4];688const int bytes_per_parts =689enc->mb_w * enc->mb_h * average_bytes_per_MB / enc->num_parts;690// Initialize the bit-writers691for (p = 0; ok && p < enc->num_parts; ++p) {692ok = VP8BitWriterInit(enc->parts + p, bytes_per_parts);693}694if (!ok) {695VP8EncFreeBitWriters(enc); // malloc error occurred696return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);697}698return ok;699}700701static int PostLoopFinalize(VP8EncIterator* const it, int ok) {702VP8Encoder* const enc = it->enc;703if (ok) { // Finalize the partitions, check for extra errors.704int p;705for (p = 0; p < enc->num_parts; ++p) {706VP8BitWriterFinish(enc->parts + p);707ok &= !enc->parts[p].error;708}709}710711if (ok) { // All good. Finish up.712#if !defined(WEBP_DISABLE_STATS)713if (enc->pic->stats != NULL) { // finalize byte counters...714int i, s;715for (i = 0; i <= 2; ++i) {716for (s = 0; s < NUM_MB_SEGMENTS; ++s) {717enc->residual_bytes[i][s] = (int)((it->bit_count[s][i] + 7) >> 3);718}719}720}721#endif722VP8AdjustFilterStrength(it); // ...and store filter stats.723} else {724// Something bad happened -> need to do some memory cleanup.725VP8EncFreeBitWriters(enc);726return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);727}728return ok;729}730731//------------------------------------------------------------------------------732// VP8EncLoop(): does the final bitstream coding.733734static void ResetAfterSkip(VP8EncIterator* const it) {735if (it->mb->type == 1) {736*it->nz = 0; // reset all predictors737it->left_nz[8] = 0;738} else {739*it->nz &= (1 << 24); // preserve the dc_nz bit740}741}742743int VP8EncLoop(VP8Encoder* const enc) {744VP8EncIterator it;745int ok = PreLoopInitialize(enc);746if (!ok) return 0;747748StatLoop(enc); // stats-collection loop749750VP8IteratorInit(enc, &it);751VP8InitFilter(&it);752do {753VP8ModeScore info;754const int dont_use_skip = !enc->proba.use_skip_proba;755const VP8RDLevel rd_opt = enc->rd_opt_level;756757VP8IteratorImport(&it, NULL);758// Warning! order is important: first call VP8Decimate() and759// *then* decide how to code the skip decision if there's one.760if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {761CodeResiduals(it.bw, &it, &info);762if (it.bw->error) {763// enc->pic->error_code is set in PostLoopFinalize().764ok = 0;765break;766}767} else { // reset predictors after a skip768ResetAfterSkip(&it);769}770StoreSideInfo(&it);771VP8StoreFilterStats(&it);772VP8IteratorExport(&it);773ok = VP8IteratorProgress(&it, 20);774VP8IteratorSaveBoundary(&it);775} while (ok && VP8IteratorNext(&it));776777return PostLoopFinalize(&it, ok);778}779780//------------------------------------------------------------------------------781// Single pass using Token Buffer.782783#if !defined(DISABLE_TOKEN_BUFFER)784785#define MIN_COUNT 96 // minimum number of macroblocks before updating stats786787int VP8EncTokenLoop(VP8Encoder* const enc) {788// Roughly refresh the proba eight times per pass789int max_count = (enc->mb_w * enc->mb_h) >> 3;790int num_pass_left = enc->config->pass;791int remaining_progress = 40; // percents792const int do_search = enc->do_search;793VP8EncIterator it;794VP8EncProba* const proba = &enc->proba;795const VP8RDLevel rd_opt = enc->rd_opt_level;796const uint64_t pixel_count = (uint64_t)enc->mb_w * enc->mb_h * 384;797PassStats stats;798int ok;799800InitPassStats(enc, &stats);801ok = PreLoopInitialize(enc);802if (!ok) return 0;803804if (max_count < MIN_COUNT) max_count = MIN_COUNT;805806assert(enc->num_parts == 1);807assert(enc->use_tokens);808assert(proba->use_skip_proba == 0);809assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful810assert(num_pass_left > 0);811812while (ok && num_pass_left-- > 0) {813const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||814(num_pass_left == 0) ||815(enc->max_i4_header_bits == 0);816uint64_t size_p0 = 0;817uint64_t distortion = 0;818int cnt = max_count;819// The final number of passes is not trivial to know in advance.820const int pass_progress = remaining_progress / (2 + num_pass_left);821remaining_progress -= pass_progress;822VP8IteratorInit(enc, &it);823SetLoopParams(enc, stats.q);824if (is_last_pass) {825ResetTokenStats(enc);826VP8InitFilter(&it); // don't collect stats until last pass (too costly)827}828VP8TBufferClear(&enc->tokens);829do {830VP8ModeScore info;831VP8IteratorImport(&it, NULL);832if (--cnt < 0) {833FinalizeTokenProbas(proba);834VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt835cnt = max_count;836}837VP8Decimate(&it, &info, rd_opt);838ok = RecordTokens(&it, &info, &enc->tokens);839if (!ok) {840WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);841break;842}843size_p0 += info.H;844distortion += info.D;845if (is_last_pass) {846StoreSideInfo(&it);847VP8StoreFilterStats(&it);848VP8IteratorExport(&it);849ok = VP8IteratorProgress(&it, pass_progress);850}851VP8IteratorSaveBoundary(&it);852} while (ok && VP8IteratorNext(&it));853if (!ok) break;854855size_p0 += enc->segment_hdr.size;856if (stats.do_size_search) {857uint64_t size = FinalizeTokenProbas(&enc->proba);858size += VP8EstimateTokenSize(&enc->tokens,859(const uint8_t*)proba->coeffs);860size = (size + size_p0 + 1024) >> 11; // -> size in bytes861size += HEADER_SIZE_ESTIMATE;862stats.value = (double)size;863} else { // compute and store PSNR864stats.value = GetPSNR(distortion, pixel_count);865}866867#if (DEBUG_SEARCH > 0)868printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf "869" range:[%.1f, %.1f]\n",870num_pass_left, stats.last_value, stats.value,871stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax);872#endif873if (enc->max_i4_header_bits > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {874++num_pass_left;875enc->max_i4_header_bits >>= 1; // strengthen header bit limitation...876if (is_last_pass) {877ResetSideInfo(&it);878}879continue; // ...and start over880}881if (is_last_pass) {882break; // done883}884if (do_search) {885ComputeNextQ(&stats); // Adjust q886}887}888if (ok) {889if (!stats.do_size_search) {890FinalizeTokenProbas(&enc->proba);891}892ok = VP8EmitTokens(&enc->tokens, enc->parts + 0,893(const uint8_t*)proba->coeffs, 1);894}895ok = ok && WebPReportProgress(enc->pic, enc->percent + remaining_progress,896&enc->percent);897return PostLoopFinalize(&it, ok);898}899900#else901902int VP8EncTokenLoop(VP8Encoder* const enc) {903(void)enc;904return 0; // we shouldn't be here.905}906907#endif // DISABLE_TOKEN_BUFFER908909//------------------------------------------------------------------------------910911912