Moving RD-opt related code from vp9_encoder.h to vp9_rdopt.h. Squashed-Change-Id: I8fab776c8801e19d3f5027ed55a6aa69eee951de gen_msvs_proj: fix in tree configure under cygwin strip trailing '/' from paths, this is later converted to '\' which causes execution errors for obj_int_extract/yasm. vs10+ wasn't affected by this issue, but make the same change for consistency. gen_msvs_proj: + add missing '"' to obj_int_extract call unlike gen_msvs_vcproj, the block is duplicated missed in:1e3d9b9build/msvs: fix builds in source dirs with spaces Squashed-Change-Id: I76208e6cdc66dc5a0a7ffa8aa1edbefe31e4b130 Improve vp9_rb_bytes_read Squashed-Change-Id: I69eba120eb3d8ec43b5552451c8a9bd009390795 Removing decode_one_iter() function. When superframe index is available we completely rely on it and use frame size values from the index. Squashed-Change-Id: I0011d08b223303a8b912c2bcc8a02b74d0426ee0 iosbuild.sh: Add vpx_config.h and vpx_version.h to VPX.framework. - Rename build_targets to build_framework - Add functions for creating the vpx_config shim and obtaining preproc symbols. Squashed-Change-Id: Ieca6938b9779077eefa26bf4cfee64286d1840b0 Implemented vp9_denoiser_{alloc,free}() Squashed-Change-Id: I79eba79f7c52eec19ef2356278597e06620d5e27 Update running avg for VP9 denoiser Squashed-Change-Id: I9577d648542064052795bf5770428fbd5c276b7b Changed buf_2ds in vp9 denoiser to YV12 buffers Changed alloc, free, and running average code as necessary. Squashed-Change-Id: Ifc4d9ccca462164214019963b3768a457791b9c1 sse4 regular quantize Squashed-Change-Id: Ibd95df0adf9cc9143006ee9032b4cb2ebfd5dd1b Modify non-rd intra mode checking Speed 6 uses small tx size, namely 8x8. max_intra_bsize needs to be modified accordingly to ensure valid intra mode checking. Borg test on RTC set showed an overall PSNR gain of 0.335% in speed -6. This also changes speed -5 encoding by allowing DC_PRED checking for block32x32. Borg test on RTC set showed a slight PSNR gain of 0.145%, and no noticeable speed change. Squashed-Change-Id: I1502978d8fbe265b3bb235db0f9c35ba0703cd45 Implemented COPY_BLOCK case for vp9 denoiser Squashed-Change-Id: Ie89ad1e3aebbd474e1a0db69c1961b4d1ddcd33e Improved vp9 denoiser running avg update. Squashed-Change-Id: Ie0aa41fb7957755544321897b3bb2dd92f392027 Separate rate-distortion modeling for DC and AC coefficients This is the first step to rework the rate-distortion modeling used in rtc coding mode. The overall goal is to make the modeling customized for the statistics encountered in the rtc coding. This commit makes encoder to perform rate-distortion modeling for DC and AC coefficients separately. No speed changes observed. The coding performance for pedestrian_area_1080p is largely improved: speed -5, from 79558 b/f, 37.871 dB -> 79598 b/f, 38.600 dB speed -6, from 79515 b/f, 37.822 dB -> 79544 b/f, 38.130 dB Overall performance for rtc set at speed -6 is improved by 0.67%. Squashed-Change-Id: I9153444567e5f75ccdcaac043c2365992c005c0c Add superframe support for frame parallel decoding. A superframe is a bunch of frames that bundled as one frame. It is mostly used to combine one or more non-displayable frames and one displayable frame. For frame parallel decoding, libvpx decoder will only support decoding one normal frame or a super frame with superframe index. If an application pass a superframe without superframe index or a chunk of displayable frames without superframe index to libvpx decoder, libvpx will not decode it in frame parallel mode. But libvpx decoder still could decode it in serial mode. Squashed-Change-Id: I04c9f2c828373d64e880a8c7bcade5307015ce35 Fixes in VP9 alloc, free, and COPY_FRAME case Squashed-Change-Id: I1216f17e2206ef521fe219b6d72d8e41d1ba1147 Remove labels from quantize Use break instead of goto for early exit. Unbreaks Visual Studio builds. Squashed-Change-Id: I96dee43a3c82145d4abe0d6a99af6e6e1a3991b5 Added CFLAG for outputting vp9 denoised signal Squashed-Change-Id: Iab9b4e11cad927f3282e486c203564e1a658f377 Allow key frame more flexibility in mode search This commit allows the key frame to search through more prediction modes and more flexible block sizes. No speed change observed. The coding performance for rtc set is improved by 1.7% for speed -5 and 3.0% for speed -6. Squashed-Change-Id: Ifd1bc28558017851b210b4004f2d80838938bcc5 VP9 denoiser bugfixes s/stdint.h/vpx\/vpx_int.h Added missing 'break;'s Also included other minor changes, mostly cosmetic. Squashed-Change-Id: I852bba3e85e794f1d4af854c45c16a23a787e6a3 Don't return value for void functions Clears "warning: 'return' with a value, in function returning void" Squashed-Change-Id: I93972610d67e243ec772a1021d2fdfcfc689c8c2 Include type defines Clears error: unknown type name 'uint8_t' Squashed-Change-Id: I9b6eff66a5c69bc24aeaeb5ade29255a164ef0e2 Validate error checking code in decoder. This patch adds a mechanism for insuring error checking on invalid files by creating a unit test that runs the decoder and tests that the error code matches what's expected on each frame in the decoder. Disabled for now as this unit test will segfault with existing code. Squashed-Change-Id: I896f9686d9ebcbf027426933adfbea7b8c5d956e Introduce FrameWorker for decoding. When decoding in serial mode, there will be only one FrameWorker doing decoding. When decoding in parallel mode, there will be several FrameWorkers doing decoding in parallel. Squashed-Change-Id: If53fc5c49c7a0bf5e773f1ce7008b8a62fdae257 Add back libmkv ebml writer files. Another project in ChromeOS is using these files. To make libvpx rolls simpler, add these files back unitl the other project removes the dependency. crbug.com/387246 tracking bug to remove dependency. Squashed-Change-Id: If9c197081c845c4a4e5c5488d4e0190380bcb1e4 Added Test vector that tests more show existing frames. Squashed-Change-Id: I0ddd7dd55313ee62d231ed4b9040e08c3761b3fe fix peek_si to enable 1 byte show existing frames. The test for this is in test vector code ( show existing frames will fail ). I can't check it in disabled as I'm changing the generic test code to do this: https://gerrit.chromium.org/gerrit/#/c/70569/ Squashed-Change-Id: I5ab324f0cb7df06316a949af0f7fc089f4a3d466 Fix bug in error handling that causes segfault See: https://code.google.com/p/chromium/issues/detail?id=362697 The code properly catches an invalid stream but seg faults instead of returning an error due to a buffer not having been initialized. This code fixes that. Squashed-Change-Id: I695595e742cb08807e1dfb2f00bc097b3eae3a9b Revert 3 patches from Hangyu to get Chrome to build: Avoids failures: MSE_ClearKey/EncryptedMediaTest.Playback_VP9Video_WebM/0 MSE_ClearKey_Prefixed/EncryptedMediaTest.Playback_VP9Video_WebM/0 MSE_ExternalClearKey_Prefixed/EncryptedMediaTest.Playback_VP9Video_WebM/0 MSE_ExternalClearKey/EncryptedMediaTest.Playback_VP9Video_WebM/0 MSE_ExternalClearKeyDecryptOnly/EncryptedMediaTest.Playback_VP9Video_WebM/0 MSE_ExternalClearKeyDecryptOnly_Prefixed/EncryptedMediaTest.Playback_VP9Video_WebM/0 SRC_ExternalClearKey/EncryptedMediaTest.Playback_VP9Video_WebM/0 SRC_ExternalClearKey_Prefixed/EncryptedMediaTest.Playback_VP9Video_WebM/0 SRC_ClearKey_Prefixed/EncryptedMediaTest.Playback_VP9Video_WebM/0 Patches are This reverts commit9bc040859bThis reverts commit6f5aba069aThis reverts commit9bc040859bI1f250441 Revert "Refactor the vp9_get_frame code for frame parallel." Ibfdddce5 Revert "Delay decreasing reference count in frame-parallel decoding." I00ce6771 Revert "Introduce FrameWorker for decoding." Need better testing in libvpx for these commits Squashed-Change-Id: Ifa1f279b0cabf4b47c051ec26018f9301c1e130e error check vp9 superframe parsing This patch insures that the last byte of a chunk that contains a valid superframe marker byte, actually has a proper superframe index. If not it returns an error. As part of doing that the file : vp90-2-15-fuzz-flicker.webm now fails to decode properly and moves to the invalid file test from the test vector suite. Squashed-Change-Id: I5f1da7eb37282ec0c6394df5c73251a2df9c1744 Remove unused vp9_init_quant_tables function This function is not effectively used, hence removed. Squashed-Change-Id: I2e8e48fa07c7518931690f3b04bae920cb360e49 Actually skip blocks in skip segments in non-rd encoder. Copy split from macroblock to pick mode context so it doesn't get lost. Squashed-Change-Id: Ie37aa12558dbe65c4f8076cf808250fffb7f27a8 Add Check for Peek Stream validity to decoder test. Squashed-Change-Id: I9b745670a9f842582c47e6001dc77480b31fb6a1 Allocate buffers based on correct chroma format The encoder currently allocates frame buffers before it establishes what the chroma sub-sampling factor is, always allocating based on the 4:4:4 format. This patch detects the chroma format as early as possible allowing the encoder to allocate buffers of the correct size. Future patches will change the encoder to allocate frame buffers on demand to further reduce the memory profile of the encoder and rationalize the buffer management in the encoder and decoder. Squashed-Change-Id: Ifd41dd96e67d0011719ba40fada0bae74f3a0d57 Fork vp9_rd_pick_inter_mode_sb_seg_skip Squashed-Change-Id: I549868725b789f0f4f89828005a65972c20df888 Switch active map implementation to segment based. Squashed-Change-Id: Ibb841a1fa4d08d164cf5461246ec290f582b1f80 Experiment for mid group second arf. This patch implements a mechanism for inserting a second arf at the mid position of arf groups. It is currently disabled by default using the flag multi_arf_enabled. Results are currently down somewhat in initial testing if multi-arf is enabled. Most of the loss is attributable to the fact that code to preserve the previous golden frame (in the arf buffer) in cases where we are coding an overlay frame, is currently disabled in the multi-arf case. Squashed-Change-Id: I1d777318ca09f147db2e8c86d7315fe86168c865 Clean out old CONFIG_MULTIPLE_ARF code. Remove the old experimental multi arf code that was under the flag CONFIG_MULTIPLE_ARF. Squashed-Change-Id: Ib24865abc11691d6ac8cb0434ada1da674368a61 Fix some bugs in multi-arf Fix some bugs relating to the use of buffers in the overlay frames. Fix bug where a mid sequence overlay was propagating large partition and transform sizes into the subsequent frame because of :- sf->last_partitioning_redo_frequency > 1 and sf->tx_size_search_method == USE_LARGESTALL Squashed-Change-Id: Ibf9ef39a5a5150f8cbdd2c9275abb0316c67873a Further dual arf changes: multi_arf_allowed. Add multi_arf_allowed flag. Re-initialize buffer indices every kf. Add some const indicators. Squashed-Change-Id: If86c39153517c427182691d2d4d4b7e90594be71 Fixed VP9 denoiser COPY_BLOCK case Now copies the src to the correct location in the running average buffer. Squashed-Change-Id: I9c83c96dc7a97f42c8df16ab4a9f18b733181f34 Fix test on maximum downscaling limits There is a normative scaling range of (x1/2, x16) for VP9. This patch fixes the maximum downscaling tests that are applied in the convolve function. The code used a maximum downscaling limit of x1/5 for historic reasons related to the scalable coding work. Since the downsampling in this application is non-normative it will revert to using a separate non-normative scaler. Squashed-Change-Id: Ide80ed712cee82fe5cb3c55076ac428295a6019f Add unit test to test user_priv parameter. Squashed-Change-Id: I6ba6171e43e0a43331ee0a7b698590b143979c44 vp9: check tile column count the max is 6. there are assumptions throughout the decode regarding this; fixes a crash with a fuzzed bitstream $ zzuf -s 5861 -r 0.01:0.05 -b 6- \ < vp90-2-00-quantizer-00.webm.ivf \ | dd of=invalid-vp90-2-00-quantizer-00.webm.ivf.s5861_r01-05_b6-.ivf \ bs=1 count=81883 Squashed-Change-Id: I6af41bb34252e88bc156a4c27c80d505d45f5642 Adjust arf Q limits with multi-arf. Adjust enforced minimum arf Q deltas for non primary arfs in the middle of an arf/gf group. Squashed-Change-Id: Ie8034ffb3ac00f887d74ae1586d4cac91d6cace2 Dual ARF changes: Buffer index selection. Add indirection to the section of buffer indices. This is to help simplify things in the future if we have other codec features that switch indices. Limit the max GF interval for static sections to fit the gf_group structures. Squashed-Change-Id: I38310daaf23fd906004c0e8ee3e99e15570f84cb Reuse inter prediction result in real-time speed 6 In real-time speed 6, no partition search is done. The inter prediction results got from picking mode can be reused in the following encoding process. A speed feature reuse_inter_pred_sby is added to only enable the resue in speed 6. This patch doesn't change encoding result. RTC set tests showed that the encoding speed gain is 2% - 5%. Squashed-Change-Id: I3884780f64ef95dd8be10562926542528713b92c Add vp9_ prefix to mv_pred and setup_pred_block functions Make these two functions accessible by both RD and non-RD coding modes. Squashed-Change-Id: Iecb39dbf3d65436286ea3c7ffaa9920d0b3aff85 Replace cpi->common with preset variable cm This commit replaces a few use cases of cpi->common with preset variable cm, to avoid unnecessary pointer fetch in the non-RD coding mode. Squashed-Change-Id: I4038f1c1a47373b8fd7bc5d69af61346103702f6 [spatial svc]Implement lag in frames for spatial svc Squashed-Change-Id: I930dced169c9d53f8044d2754a04332138347409 [spatial svc]Don't skip motion search in first pass encoding Squashed-Change-Id: Ia6bcdaf5a5b80e68176f60d8d00e9b5cf3f9bfe3 decode_test_driver: fix type size warning like vpx_codec_decode(), vpx_codec_peek_stream_info() takes an unsigned int, not size_t, parameter for buffer size Squashed-Change-Id: I4ce0e1fbbde461c2e1b8fcbaac3cd203ed707460 decode_test_driver: check HasFailure() in RunLoop avoids unnecessary errors due to e.g., read (Next()) failures Squashed-Change-Id: I70b1d09766456f1c55367d98299b5abd7afff842 Allow lossless breakout in non-rd mode decision. This is very helpful for large moving windows in screencasts. Squashed-Change-Id: I91b5f9acb133281ee85ccd8f843e6bae5cadefca Revert "Revert 3 patches from Hangyu to get Chrome to build:" This patch reverts the previous revert from Jim and also add a variable user_priv in the FrameWorker to save the user_priv passed from the application. In the decoder_get_frame function, the user_priv will be binded with the img. This change is needed or it will fail the unit test added here: https://gerrit.chromium.org/gerrit/#/c/70610/ This reverts commit9be46e4565. Squashed-Change-Id: I376d9a12ee196faffdf3c792b59e6137c56132c1 test.mk: remove renamed file vp90-2-15-fuzz-flicker.webm was renamed in:c3db2d8error check vp9 superframe parsing Squashed-Change-Id: I229dd6ca4c662802c457beea0f7b4128153a65dc vp9cx.mk: move avx c files outside of x86inc block same reasoning as:9f3a0dbvp9_rtcd: correct avx2 references these are all intrinsics, so don't depend on x86inc.asm Squashed-Change-Id: I915beaef318a28f64bfa5469e5efe90e4af5b827 Dual arf: Name changes. Cosmetic patch only in response to comments on previous patches suggesting a couple of name changes for consistency and clarity. Squashed-Change-Id: Ida3a359b0d5755345660d304a7697a3a3686b2a3 Make non-RD intra mode search txfm size dependent This commit fixes the potential issue in the non-RD mode decision flow that only checks part of the block to estimate the cost. It was due to the use of fixed transform size, in replacing the largest transform block size. This commit enables per transform block cost estimation of the intra prediction mode in the non-RD mode decision. Squashed-Change-Id: I14ff92065e193e3e731c2bbf7ec89db676f1e132 Fix quality regression for multi arf off case. Bug introduced during multiple iterations on: I3831* gf_group->arf_update_idx[] cannot currently be used to select the arf buffer index if buffer flipping on overlays is enabled (still currently the case when multi arf OFF). Squashed-Change-Id: I4ce9ea08f1dd03ac3ad8b3e27375a91ee1d964dc Enable real-time version reference motion vector search This commit enables a fast reference motion vector search scheme. It checks the nearest top and left neighboring blocks to decide the most probable predicted motion vector. If it finds the two have the same motion vectors, it then skip finding exterior range for the second most probable motion vector, and correspondingly skips the check for NEARMV. The runtime of speed -5 goes down pedestrian at 1080p 29377 ms -> 27783 ms vidyo at 720p 11830 ms -> 10990 ms i.e., 6%-8% speed-up. For rtc set, the compression performance goes down by about -1.3% for both speed -5 and -6. Squashed-Change-Id: I2a7794fa99734f739f8b30519ad4dfd511ab91a5 Add const mark to const values in non-RD coding mode Squashed-Change-Id: I65209fd1e06fc06833f6647cb028b414391a7017 Change-Id: Ic0be67ac9ef48f64a8878a0b8f1b336f136bceac
2220 lines
78 KiB
C
2220 lines
78 KiB
C
/*
|
|
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE 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.
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
#include "./vpx_scale_rtcd.h"
|
|
|
|
#include "vpx_mem/vpx_mem.h"
|
|
#include "vpx_scale/vpx_scale.h"
|
|
#include "vpx_scale/yv12config.h"
|
|
|
|
#include "vp9/common/vp9_entropymv.h"
|
|
#include "vp9/common/vp9_quant_common.h"
|
|
#include "vp9/common/vp9_reconinter.h" // vp9_setup_dst_planes()
|
|
#include "vp9/common/vp9_systemdependent.h"
|
|
|
|
#include "vp9/encoder/vp9_aq_variance.h"
|
|
#include "vp9/encoder/vp9_block.h"
|
|
#include "vp9/encoder/vp9_encodeframe.h"
|
|
#include "vp9/encoder/vp9_encodemb.h"
|
|
#include "vp9/encoder/vp9_encodemv.h"
|
|
#include "vp9/encoder/vp9_encoder.h"
|
|
#include "vp9/encoder/vp9_extend.h"
|
|
#include "vp9/encoder/vp9_firstpass.h"
|
|
#include "vp9/encoder/vp9_mcomp.h"
|
|
#include "vp9/encoder/vp9_quantize.h"
|
|
#include "vp9/encoder/vp9_rdopt.h"
|
|
#include "vp9/encoder/vp9_variance.h"
|
|
|
|
#define OUTPUT_FPF 0
|
|
|
|
#define IIFACTOR 12.5
|
|
#define IIKFACTOR1 12.5
|
|
#define IIKFACTOR2 15.0
|
|
#define RMAX 512.0
|
|
#define GF_RMAX 96.0
|
|
#define ERR_DIVISOR 150.0
|
|
#define MIN_DECAY_FACTOR 0.1
|
|
#define SVC_FACTOR_PT_LOW 0.45
|
|
#define FACTOR_PT_LOW 0.5
|
|
#define FACTOR_PT_HIGH 0.9
|
|
|
|
#define KF_MB_INTRA_MIN 150
|
|
#define GF_MB_INTRA_MIN 100
|
|
|
|
#define DOUBLE_DIVIDE_CHECK(x) ((x) < 0 ? (x) - 0.000001 : (x) + 0.000001)
|
|
|
|
#define MIN_KF_BOOST 300
|
|
#define MIN_GF_INTERVAL 4
|
|
#define LONG_TERM_VBR_CORRECTION
|
|
|
|
static void swap_yv12(YV12_BUFFER_CONFIG *a, YV12_BUFFER_CONFIG *b) {
|
|
YV12_BUFFER_CONFIG temp = *a;
|
|
*a = *b;
|
|
*b = temp;
|
|
}
|
|
|
|
static int gfboost_qadjust(int qindex) {
|
|
const double q = vp9_convert_qindex_to_q(qindex);
|
|
return (int)((0.00000828 * q * q * q) +
|
|
(-0.0055 * q * q) +
|
|
(1.32 * q) + 79.3);
|
|
}
|
|
|
|
// Resets the first pass file to the given position using a relative seek from
|
|
// the current position.
|
|
static void reset_fpf_position(TWO_PASS *p,
|
|
const FIRSTPASS_STATS *position) {
|
|
p->stats_in = position;
|
|
}
|
|
|
|
static int lookup_next_frame_stats(const TWO_PASS *p,
|
|
FIRSTPASS_STATS *next_frame) {
|
|
if (p->stats_in >= p->stats_in_end)
|
|
return EOF;
|
|
|
|
*next_frame = *p->stats_in;
|
|
return 1;
|
|
}
|
|
|
|
|
|
// Read frame stats at an offset from the current position.
|
|
static int read_frame_stats(const TWO_PASS *p,
|
|
FIRSTPASS_STATS *frame_stats, int offset) {
|
|
const FIRSTPASS_STATS *fps_ptr = p->stats_in;
|
|
|
|
// Check legality of offset.
|
|
if (offset >= 0) {
|
|
if (&fps_ptr[offset] >= p->stats_in_end)
|
|
return EOF;
|
|
} else if (offset < 0) {
|
|
if (&fps_ptr[offset] < p->stats_in_start)
|
|
return EOF;
|
|
}
|
|
|
|
*frame_stats = fps_ptr[offset];
|
|
return 1;
|
|
}
|
|
|
|
static int input_stats(TWO_PASS *p, FIRSTPASS_STATS *fps) {
|
|
if (p->stats_in >= p->stats_in_end)
|
|
return EOF;
|
|
|
|
*fps = *p->stats_in;
|
|
++p->stats_in;
|
|
return 1;
|
|
}
|
|
|
|
static void output_stats(FIRSTPASS_STATS *stats,
|
|
struct vpx_codec_pkt_list *pktlist) {
|
|
struct vpx_codec_cx_pkt pkt;
|
|
pkt.kind = VPX_CODEC_STATS_PKT;
|
|
pkt.data.twopass_stats.buf = stats;
|
|
pkt.data.twopass_stats.sz = sizeof(FIRSTPASS_STATS);
|
|
vpx_codec_pkt_list_add(pktlist, &pkt);
|
|
|
|
// TEMP debug code
|
|
#if OUTPUT_FPF
|
|
{
|
|
FILE *fpfile;
|
|
fpfile = fopen("firstpass.stt", "a");
|
|
|
|
fprintf(fpfile, "%12.0f %12.0f %12.0f %12.0f %12.4f %12.4f"
|
|
"%12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f"
|
|
"%12.0f %12.0f %12.4f %12.0f %12.0f %12.4f\n",
|
|
stats->frame,
|
|
stats->intra_error,
|
|
stats->coded_error,
|
|
stats->sr_coded_error,
|
|
stats->pcnt_inter,
|
|
stats->pcnt_motion,
|
|
stats->pcnt_second_ref,
|
|
stats->pcnt_neutral,
|
|
stats->MVr,
|
|
stats->mvr_abs,
|
|
stats->MVc,
|
|
stats->mvc_abs,
|
|
stats->MVrv,
|
|
stats->MVcv,
|
|
stats->mv_in_out_count,
|
|
stats->new_mv_count,
|
|
stats->count,
|
|
stats->duration);
|
|
fclose(fpfile);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void zero_stats(FIRSTPASS_STATS *section) {
|
|
section->frame = 0.0;
|
|
section->intra_error = 0.0;
|
|
section->coded_error = 0.0;
|
|
section->sr_coded_error = 0.0;
|
|
section->pcnt_inter = 0.0;
|
|
section->pcnt_motion = 0.0;
|
|
section->pcnt_second_ref = 0.0;
|
|
section->pcnt_neutral = 0.0;
|
|
section->MVr = 0.0;
|
|
section->mvr_abs = 0.0;
|
|
section->MVc = 0.0;
|
|
section->mvc_abs = 0.0;
|
|
section->MVrv = 0.0;
|
|
section->MVcv = 0.0;
|
|
section->mv_in_out_count = 0.0;
|
|
section->new_mv_count = 0.0;
|
|
section->count = 0.0;
|
|
section->duration = 1.0;
|
|
section->spatial_layer_id = 0;
|
|
}
|
|
|
|
static void accumulate_stats(FIRSTPASS_STATS *section,
|
|
const FIRSTPASS_STATS *frame) {
|
|
section->frame += frame->frame;
|
|
section->spatial_layer_id = frame->spatial_layer_id;
|
|
section->intra_error += frame->intra_error;
|
|
section->coded_error += frame->coded_error;
|
|
section->sr_coded_error += frame->sr_coded_error;
|
|
section->pcnt_inter += frame->pcnt_inter;
|
|
section->pcnt_motion += frame->pcnt_motion;
|
|
section->pcnt_second_ref += frame->pcnt_second_ref;
|
|
section->pcnt_neutral += frame->pcnt_neutral;
|
|
section->MVr += frame->MVr;
|
|
section->mvr_abs += frame->mvr_abs;
|
|
section->MVc += frame->MVc;
|
|
section->mvc_abs += frame->mvc_abs;
|
|
section->MVrv += frame->MVrv;
|
|
section->MVcv += frame->MVcv;
|
|
section->mv_in_out_count += frame->mv_in_out_count;
|
|
section->new_mv_count += frame->new_mv_count;
|
|
section->count += frame->count;
|
|
section->duration += frame->duration;
|
|
}
|
|
|
|
static void subtract_stats(FIRSTPASS_STATS *section,
|
|
const FIRSTPASS_STATS *frame) {
|
|
section->frame -= frame->frame;
|
|
section->intra_error -= frame->intra_error;
|
|
section->coded_error -= frame->coded_error;
|
|
section->sr_coded_error -= frame->sr_coded_error;
|
|
section->pcnt_inter -= frame->pcnt_inter;
|
|
section->pcnt_motion -= frame->pcnt_motion;
|
|
section->pcnt_second_ref -= frame->pcnt_second_ref;
|
|
section->pcnt_neutral -= frame->pcnt_neutral;
|
|
section->MVr -= frame->MVr;
|
|
section->mvr_abs -= frame->mvr_abs;
|
|
section->MVc -= frame->MVc;
|
|
section->mvc_abs -= frame->mvc_abs;
|
|
section->MVrv -= frame->MVrv;
|
|
section->MVcv -= frame->MVcv;
|
|
section->mv_in_out_count -= frame->mv_in_out_count;
|
|
section->new_mv_count -= frame->new_mv_count;
|
|
section->count -= frame->count;
|
|
section->duration -= frame->duration;
|
|
}
|
|
|
|
static void avg_stats(FIRSTPASS_STATS *section) {
|
|
if (section->count < 1.0)
|
|
return;
|
|
|
|
section->intra_error /= section->count;
|
|
section->coded_error /= section->count;
|
|
section->sr_coded_error /= section->count;
|
|
section->pcnt_inter /= section->count;
|
|
section->pcnt_second_ref /= section->count;
|
|
section->pcnt_neutral /= section->count;
|
|
section->pcnt_motion /= section->count;
|
|
section->MVr /= section->count;
|
|
section->mvr_abs /= section->count;
|
|
section->MVc /= section->count;
|
|
section->mvc_abs /= section->count;
|
|
section->MVrv /= section->count;
|
|
section->MVcv /= section->count;
|
|
section->mv_in_out_count /= section->count;
|
|
section->duration /= section->count;
|
|
}
|
|
|
|
// Calculate a modified Error used in distributing bits between easier and
|
|
// harder frames.
|
|
static double calculate_modified_err(const TWO_PASS *twopass,
|
|
const VP9EncoderConfig *oxcf,
|
|
const FIRSTPASS_STATS *this_frame) {
|
|
const FIRSTPASS_STATS *const stats = &twopass->total_stats;
|
|
const double av_err = stats->coded_error / stats->count;
|
|
const double modified_error = av_err *
|
|
pow(this_frame->coded_error / DOUBLE_DIVIDE_CHECK(av_err),
|
|
oxcf->two_pass_vbrbias / 100.0);
|
|
return fclamp(modified_error,
|
|
twopass->modified_error_min, twopass->modified_error_max);
|
|
}
|
|
|
|
// This function returns the maximum target rate per frame.
|
|
static int frame_max_bits(const RATE_CONTROL *rc,
|
|
const VP9EncoderConfig *oxcf) {
|
|
int64_t max_bits = ((int64_t)rc->avg_frame_bandwidth *
|
|
(int64_t)oxcf->two_pass_vbrmax_section) / 100;
|
|
if (max_bits < 0)
|
|
max_bits = 0;
|
|
else if (max_bits > rc->max_frame_bandwidth)
|
|
max_bits = rc->max_frame_bandwidth;
|
|
|
|
return (int)max_bits;
|
|
}
|
|
|
|
void vp9_init_first_pass(VP9_COMP *cpi) {
|
|
zero_stats(&cpi->twopass.total_stats);
|
|
}
|
|
|
|
void vp9_end_first_pass(VP9_COMP *cpi) {
|
|
if (cpi->use_svc && cpi->svc.number_temporal_layers == 1) {
|
|
int i;
|
|
for (i = 0; i < cpi->svc.number_spatial_layers; ++i) {
|
|
output_stats(&cpi->svc.layer_context[i].twopass.total_stats,
|
|
cpi->output_pkt_list);
|
|
}
|
|
} else {
|
|
output_stats(&cpi->twopass.total_stats, cpi->output_pkt_list);
|
|
}
|
|
}
|
|
|
|
static vp9_variance_fn_t get_block_variance_fn(BLOCK_SIZE bsize) {
|
|
switch (bsize) {
|
|
case BLOCK_8X8:
|
|
return vp9_mse8x8;
|
|
case BLOCK_16X8:
|
|
return vp9_mse16x8;
|
|
case BLOCK_8X16:
|
|
return vp9_mse8x16;
|
|
default:
|
|
return vp9_mse16x16;
|
|
}
|
|
}
|
|
|
|
static unsigned int get_prediction_error(BLOCK_SIZE bsize,
|
|
const struct buf_2d *src,
|
|
const struct buf_2d *ref) {
|
|
unsigned int sse;
|
|
const vp9_variance_fn_t fn = get_block_variance_fn(bsize);
|
|
fn(src->buf, src->stride, ref->buf, ref->stride, &sse);
|
|
return sse;
|
|
}
|
|
|
|
// Refine the motion search range according to the frame dimension
|
|
// for first pass test.
|
|
static int get_search_range(const VP9_COMMON *cm) {
|
|
int sr = 0;
|
|
const int dim = MIN(cm->width, cm->height);
|
|
|
|
while ((dim << sr) < MAX_FULL_PEL_VAL)
|
|
++sr;
|
|
return sr;
|
|
}
|
|
|
|
static void first_pass_motion_search(VP9_COMP *cpi, MACROBLOCK *x,
|
|
const MV *ref_mv, MV *best_mv,
|
|
int *best_motion_err) {
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
MV tmp_mv = {0, 0};
|
|
MV ref_mv_full = {ref_mv->row >> 3, ref_mv->col >> 3};
|
|
int num00, tmp_err, n;
|
|
const BLOCK_SIZE bsize = xd->mi[0]->mbmi.sb_type;
|
|
vp9_variance_fn_ptr_t v_fn_ptr = cpi->fn_ptr[bsize];
|
|
const int new_mv_mode_penalty = 256;
|
|
|
|
int step_param = 3;
|
|
int further_steps = (MAX_MVSEARCH_STEPS - 1) - step_param;
|
|
const int sr = get_search_range(&cpi->common);
|
|
step_param += sr;
|
|
further_steps -= sr;
|
|
|
|
// Override the default variance function to use MSE.
|
|
v_fn_ptr.vf = get_block_variance_fn(bsize);
|
|
|
|
// Center the initial step/diamond search on best mv.
|
|
tmp_err = cpi->diamond_search_sad(x, &cpi->ss_cfg, &ref_mv_full, &tmp_mv,
|
|
step_param,
|
|
x->sadperbit16, &num00, &v_fn_ptr, ref_mv);
|
|
if (tmp_err < INT_MAX)
|
|
tmp_err = vp9_get_mvpred_var(x, &tmp_mv, ref_mv, &v_fn_ptr, 1);
|
|
if (tmp_err < INT_MAX - new_mv_mode_penalty)
|
|
tmp_err += new_mv_mode_penalty;
|
|
|
|
if (tmp_err < *best_motion_err) {
|
|
*best_motion_err = tmp_err;
|
|
*best_mv = tmp_mv;
|
|
}
|
|
|
|
// Carry out further step/diamond searches as necessary.
|
|
n = num00;
|
|
num00 = 0;
|
|
|
|
while (n < further_steps) {
|
|
++n;
|
|
|
|
if (num00) {
|
|
--num00;
|
|
} else {
|
|
tmp_err = cpi->diamond_search_sad(x, &cpi->ss_cfg, &ref_mv_full, &tmp_mv,
|
|
step_param + n, x->sadperbit16,
|
|
&num00, &v_fn_ptr, ref_mv);
|
|
if (tmp_err < INT_MAX)
|
|
tmp_err = vp9_get_mvpred_var(x, &tmp_mv, ref_mv, &v_fn_ptr, 1);
|
|
if (tmp_err < INT_MAX - new_mv_mode_penalty)
|
|
tmp_err += new_mv_mode_penalty;
|
|
|
|
if (tmp_err < *best_motion_err) {
|
|
*best_motion_err = tmp_err;
|
|
*best_mv = tmp_mv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static BLOCK_SIZE get_bsize(const VP9_COMMON *cm, int mb_row, int mb_col) {
|
|
if (2 * mb_col + 1 < cm->mi_cols) {
|
|
return 2 * mb_row + 1 < cm->mi_rows ? BLOCK_16X16
|
|
: BLOCK_16X8;
|
|
} else {
|
|
return 2 * mb_row + 1 < cm->mi_rows ? BLOCK_8X16
|
|
: BLOCK_8X8;
|
|
}
|
|
}
|
|
|
|
static int find_fp_qindex() {
|
|
int i;
|
|
|
|
for (i = 0; i < QINDEX_RANGE; ++i)
|
|
if (vp9_convert_qindex_to_q(i) >= 30.0)
|
|
break;
|
|
|
|
if (i == QINDEX_RANGE)
|
|
i--;
|
|
|
|
return i;
|
|
}
|
|
|
|
static void set_first_pass_params(VP9_COMP *cpi) {
|
|
VP9_COMMON *const cm = &cpi->common;
|
|
if (!cpi->refresh_alt_ref_frame &&
|
|
(cm->current_video_frame == 0 ||
|
|
(cpi->frame_flags & FRAMEFLAGS_KEY))) {
|
|
cm->frame_type = KEY_FRAME;
|
|
} else {
|
|
cm->frame_type = INTER_FRAME;
|
|
}
|
|
// Do not use periodic key frames.
|
|
cpi->rc.frames_to_key = INT_MAX;
|
|
}
|
|
|
|
void vp9_first_pass(VP9_COMP *cpi) {
|
|
int mb_row, mb_col;
|
|
MACROBLOCK *const x = &cpi->mb;
|
|
VP9_COMMON *const cm = &cpi->common;
|
|
MACROBLOCKD *const xd = &x->e_mbd;
|
|
TileInfo tile;
|
|
struct macroblock_plane *const p = x->plane;
|
|
struct macroblockd_plane *const pd = xd->plane;
|
|
const PICK_MODE_CONTEXT *ctx = &cpi->pc_root->none;
|
|
int i;
|
|
|
|
int recon_yoffset, recon_uvoffset;
|
|
YV12_BUFFER_CONFIG *const lst_yv12 = get_ref_frame_buffer(cpi, LAST_FRAME);
|
|
YV12_BUFFER_CONFIG *gld_yv12 = get_ref_frame_buffer(cpi, GOLDEN_FRAME);
|
|
YV12_BUFFER_CONFIG *const new_yv12 = get_frame_new_buffer(cm);
|
|
int recon_y_stride = lst_yv12->y_stride;
|
|
int recon_uv_stride = lst_yv12->uv_stride;
|
|
int uv_mb_height = 16 >> (lst_yv12->y_height > lst_yv12->uv_height);
|
|
int64_t intra_error = 0;
|
|
int64_t coded_error = 0;
|
|
int64_t sr_coded_error = 0;
|
|
|
|
int sum_mvr = 0, sum_mvc = 0;
|
|
int sum_mvr_abs = 0, sum_mvc_abs = 0;
|
|
int64_t sum_mvrs = 0, sum_mvcs = 0;
|
|
int mvcount = 0;
|
|
int intercount = 0;
|
|
int second_ref_count = 0;
|
|
int intrapenalty = 256;
|
|
int neutral_count = 0;
|
|
int new_mv_count = 0;
|
|
int sum_in_vectors = 0;
|
|
uint32_t lastmv_as_int = 0;
|
|
TWO_PASS *twopass = &cpi->twopass;
|
|
const MV zero_mv = {0, 0};
|
|
const YV12_BUFFER_CONFIG *first_ref_buf = lst_yv12;
|
|
|
|
vp9_clear_system_state();
|
|
|
|
set_first_pass_params(cpi);
|
|
vp9_set_quantizer(cm, find_fp_qindex());
|
|
|
|
if (cpi->use_svc && cpi->svc.number_temporal_layers == 1) {
|
|
MV_REFERENCE_FRAME ref_frame = LAST_FRAME;
|
|
const YV12_BUFFER_CONFIG *scaled_ref_buf = NULL;
|
|
twopass = &cpi->svc.layer_context[cpi->svc.spatial_layer_id].twopass;
|
|
|
|
vp9_scale_references(cpi);
|
|
|
|
// Use either last frame or alt frame for motion search.
|
|
if (cpi->ref_frame_flags & VP9_LAST_FLAG) {
|
|
scaled_ref_buf = vp9_get_scaled_ref_frame(cpi, LAST_FRAME);
|
|
ref_frame = LAST_FRAME;
|
|
} else if (cpi->ref_frame_flags & VP9_ALT_FLAG) {
|
|
scaled_ref_buf = vp9_get_scaled_ref_frame(cpi, ALTREF_FRAME);
|
|
ref_frame = ALTREF_FRAME;
|
|
}
|
|
|
|
if (scaled_ref_buf != NULL) {
|
|
// Update the stride since we are using scaled reference buffer
|
|
first_ref_buf = scaled_ref_buf;
|
|
recon_y_stride = first_ref_buf->y_stride;
|
|
recon_uv_stride = first_ref_buf->uv_stride;
|
|
uv_mb_height = 16 >> (first_ref_buf->y_height > first_ref_buf->uv_height);
|
|
}
|
|
|
|
// Disable golden frame for svc first pass for now.
|
|
gld_yv12 = NULL;
|
|
set_ref_ptrs(cm, xd, ref_frame, NONE);
|
|
|
|
cpi->Source = vp9_scale_if_required(cm, cpi->un_scaled_source,
|
|
&cpi->scaled_source);
|
|
}
|
|
|
|
vp9_setup_block_planes(&x->e_mbd, cm->subsampling_x, cm->subsampling_y);
|
|
|
|
vp9_setup_src_planes(x, cpi->Source, 0, 0);
|
|
vp9_setup_pre_planes(xd, 0, first_ref_buf, 0, 0, NULL);
|
|
vp9_setup_dst_planes(xd->plane, new_yv12, 0, 0);
|
|
|
|
xd->mi = cm->mi_grid_visible;
|
|
xd->mi[0] = cm->mi;
|
|
|
|
vp9_frame_init_quantizer(cpi);
|
|
|
|
for (i = 0; i < MAX_MB_PLANE; ++i) {
|
|
p[i].coeff = ctx->coeff_pbuf[i][1];
|
|
p[i].qcoeff = ctx->qcoeff_pbuf[i][1];
|
|
pd[i].dqcoeff = ctx->dqcoeff_pbuf[i][1];
|
|
p[i].eobs = ctx->eobs_pbuf[i][1];
|
|
}
|
|
x->skip_recode = 0;
|
|
|
|
vp9_init_mv_probs(cm);
|
|
vp9_initialize_rd_consts(cpi);
|
|
|
|
// Tiling is ignored in the first pass.
|
|
vp9_tile_init(&tile, cm, 0, 0);
|
|
|
|
for (mb_row = 0; mb_row < cm->mb_rows; ++mb_row) {
|
|
int_mv best_ref_mv;
|
|
|
|
best_ref_mv.as_int = 0;
|
|
|
|
// Reset above block coeffs.
|
|
xd->up_available = (mb_row != 0);
|
|
recon_yoffset = (mb_row * recon_y_stride * 16);
|
|
recon_uvoffset = (mb_row * recon_uv_stride * uv_mb_height);
|
|
|
|
// Set up limit values for motion vectors to prevent them extending
|
|
// outside the UMV borders.
|
|
x->mv_row_min = -((mb_row * 16) + BORDER_MV_PIXELS_B16);
|
|
x->mv_row_max = ((cm->mb_rows - 1 - mb_row) * 16)
|
|
+ BORDER_MV_PIXELS_B16;
|
|
|
|
for (mb_col = 0; mb_col < cm->mb_cols; ++mb_col) {
|
|
int this_error;
|
|
const int use_dc_pred = (mb_col || mb_row) && (!mb_col || !mb_row);
|
|
double error_weight = 1.0;
|
|
const BLOCK_SIZE bsize = get_bsize(cm, mb_row, mb_col);
|
|
|
|
vp9_clear_system_state();
|
|
|
|
xd->plane[0].dst.buf = new_yv12->y_buffer + recon_yoffset;
|
|
xd->plane[1].dst.buf = new_yv12->u_buffer + recon_uvoffset;
|
|
xd->plane[2].dst.buf = new_yv12->v_buffer + recon_uvoffset;
|
|
xd->left_available = (mb_col != 0);
|
|
xd->mi[0]->mbmi.sb_type = bsize;
|
|
xd->mi[0]->mbmi.ref_frame[0] = INTRA_FRAME;
|
|
set_mi_row_col(xd, &tile,
|
|
mb_row << 1, num_8x8_blocks_high_lookup[bsize],
|
|
mb_col << 1, num_8x8_blocks_wide_lookup[bsize],
|
|
cm->mi_rows, cm->mi_cols);
|
|
|
|
if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
|
|
const int energy = vp9_block_energy(cpi, x, bsize);
|
|
error_weight = vp9_vaq_inv_q_ratio(energy);
|
|
}
|
|
|
|
// Do intra 16x16 prediction.
|
|
x->skip_encode = 0;
|
|
xd->mi[0]->mbmi.mode = DC_PRED;
|
|
xd->mi[0]->mbmi.tx_size = use_dc_pred ?
|
|
(bsize >= BLOCK_16X16 ? TX_16X16 : TX_8X8) : TX_4X4;
|
|
vp9_encode_intra_block_plane(x, bsize, 0);
|
|
this_error = vp9_get_mb_ss(x->plane[0].src_diff);
|
|
|
|
if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
|
|
vp9_clear_system_state();
|
|
this_error = (int)(this_error * error_weight);
|
|
}
|
|
|
|
// Intrapenalty below deals with situations where the intra and inter
|
|
// error scores are very low (e.g. a plain black frame).
|
|
// We do not have special cases in first pass for 0,0 and nearest etc so
|
|
// all inter modes carry an overhead cost estimate for the mv.
|
|
// When the error score is very low this causes us to pick all or lots of
|
|
// INTRA modes and throw lots of key frames.
|
|
// This penalty adds a cost matching that of a 0,0 mv to the intra case.
|
|
this_error += intrapenalty;
|
|
|
|
// Accumulate the intra error.
|
|
intra_error += (int64_t)this_error;
|
|
|
|
// Set up limit values for motion vectors to prevent them extending
|
|
// outside the UMV borders.
|
|
x->mv_col_min = -((mb_col * 16) + BORDER_MV_PIXELS_B16);
|
|
x->mv_col_max = ((cm->mb_cols - 1 - mb_col) * 16) + BORDER_MV_PIXELS_B16;
|
|
|
|
// Other than for the first frame do a motion search.
|
|
if (cm->current_video_frame > 0) {
|
|
int tmp_err, motion_error, raw_motion_error;
|
|
int_mv mv, tmp_mv;
|
|
struct buf_2d unscaled_last_source_buf_2d;
|
|
|
|
xd->plane[0].pre[0].buf = first_ref_buf->y_buffer + recon_yoffset;
|
|
motion_error = get_prediction_error(bsize, &x->plane[0].src,
|
|
&xd->plane[0].pre[0]);
|
|
// Assume 0,0 motion with no mv overhead.
|
|
mv.as_int = tmp_mv.as_int = 0;
|
|
|
|
// Compute the motion error of the 0,0 motion using the last source
|
|
// frame as the reference. Skip the further motion search on
|
|
// reconstructed frame if this error is small.
|
|
unscaled_last_source_buf_2d.buf =
|
|
cpi->unscaled_last_source->y_buffer + recon_yoffset;
|
|
unscaled_last_source_buf_2d.stride =
|
|
cpi->unscaled_last_source->y_stride;
|
|
raw_motion_error = get_prediction_error(bsize, &x->plane[0].src,
|
|
&unscaled_last_source_buf_2d);
|
|
|
|
// TODO(pengchong): Replace the hard-coded threshold
|
|
if (raw_motion_error > 25 ||
|
|
(cpi->use_svc && cpi->svc.number_temporal_layers == 1)) {
|
|
// Test last reference frame using the previous best mv as the
|
|
// starting point (best reference) for the search.
|
|
first_pass_motion_search(cpi, x, &best_ref_mv.as_mv, &mv.as_mv,
|
|
&motion_error);
|
|
if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
|
|
vp9_clear_system_state();
|
|
motion_error = (int)(motion_error * error_weight);
|
|
}
|
|
|
|
// If the current best reference mv is not centered on 0,0 then do a
|
|
// 0,0 based search as well.
|
|
if (best_ref_mv.as_int) {
|
|
tmp_err = INT_MAX;
|
|
first_pass_motion_search(cpi, x, &zero_mv, &tmp_mv.as_mv, &tmp_err);
|
|
if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
|
|
vp9_clear_system_state();
|
|
tmp_err = (int)(tmp_err * error_weight);
|
|
}
|
|
|
|
if (tmp_err < motion_error) {
|
|
motion_error = tmp_err;
|
|
mv.as_int = tmp_mv.as_int;
|
|
}
|
|
}
|
|
|
|
// Search in an older reference frame.
|
|
if (cm->current_video_frame > 1 && gld_yv12 != NULL) {
|
|
// Assume 0,0 motion with no mv overhead.
|
|
int gf_motion_error;
|
|
|
|
xd->plane[0].pre[0].buf = gld_yv12->y_buffer + recon_yoffset;
|
|
gf_motion_error = get_prediction_error(bsize, &x->plane[0].src,
|
|
&xd->plane[0].pre[0]);
|
|
|
|
first_pass_motion_search(cpi, x, &zero_mv, &tmp_mv.as_mv,
|
|
&gf_motion_error);
|
|
if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
|
|
vp9_clear_system_state();
|
|
gf_motion_error = (int)(gf_motion_error * error_weight);
|
|
}
|
|
|
|
if (gf_motion_error < motion_error && gf_motion_error < this_error)
|
|
++second_ref_count;
|
|
|
|
// Reset to last frame as reference buffer.
|
|
xd->plane[0].pre[0].buf = first_ref_buf->y_buffer + recon_yoffset;
|
|
xd->plane[1].pre[0].buf = first_ref_buf->u_buffer + recon_uvoffset;
|
|
xd->plane[2].pre[0].buf = first_ref_buf->v_buffer + recon_uvoffset;
|
|
|
|
// In accumulating a score for the older reference frame take the
|
|
// best of the motion predicted score and the intra coded error
|
|
// (just as will be done for) accumulation of "coded_error" for
|
|
// the last frame.
|
|
if (gf_motion_error < this_error)
|
|
sr_coded_error += gf_motion_error;
|
|
else
|
|
sr_coded_error += this_error;
|
|
} else {
|
|
sr_coded_error += motion_error;
|
|
}
|
|
} else {
|
|
sr_coded_error += motion_error;
|
|
}
|
|
|
|
// Start by assuming that intra mode is best.
|
|
best_ref_mv.as_int = 0;
|
|
|
|
if (motion_error <= this_error) {
|
|
// Keep a count of cases where the inter and intra were very close
|
|
// and very low. This helps with scene cut detection for example in
|
|
// cropped clips with black bars at the sides or top and bottom.
|
|
if (((this_error - intrapenalty) * 9 <= motion_error * 10) &&
|
|
this_error < 2 * intrapenalty)
|
|
++neutral_count;
|
|
|
|
mv.as_mv.row *= 8;
|
|
mv.as_mv.col *= 8;
|
|
this_error = motion_error;
|
|
xd->mi[0]->mbmi.mode = NEWMV;
|
|
xd->mi[0]->mbmi.mv[0] = mv;
|
|
xd->mi[0]->mbmi.tx_size = TX_4X4;
|
|
xd->mi[0]->mbmi.ref_frame[0] = LAST_FRAME;
|
|
xd->mi[0]->mbmi.ref_frame[1] = NONE;
|
|
vp9_build_inter_predictors_sby(xd, mb_row << 1, mb_col << 1, bsize);
|
|
vp9_encode_sby_pass1(x, bsize);
|
|
sum_mvr += mv.as_mv.row;
|
|
sum_mvr_abs += abs(mv.as_mv.row);
|
|
sum_mvc += mv.as_mv.col;
|
|
sum_mvc_abs += abs(mv.as_mv.col);
|
|
sum_mvrs += mv.as_mv.row * mv.as_mv.row;
|
|
sum_mvcs += mv.as_mv.col * mv.as_mv.col;
|
|
++intercount;
|
|
|
|
best_ref_mv.as_int = mv.as_int;
|
|
|
|
if (mv.as_int) {
|
|
++mvcount;
|
|
|
|
// Non-zero vector, was it different from the last non zero vector?
|
|
if (mv.as_int != lastmv_as_int)
|
|
++new_mv_count;
|
|
lastmv_as_int = mv.as_int;
|
|
|
|
// Does the row vector point inwards or outwards?
|
|
if (mb_row < cm->mb_rows / 2) {
|
|
if (mv.as_mv.row > 0)
|
|
--sum_in_vectors;
|
|
else if (mv.as_mv.row < 0)
|
|
++sum_in_vectors;
|
|
} else if (mb_row > cm->mb_rows / 2) {
|
|
if (mv.as_mv.row > 0)
|
|
++sum_in_vectors;
|
|
else if (mv.as_mv.row < 0)
|
|
--sum_in_vectors;
|
|
}
|
|
|
|
// Does the col vector point inwards or outwards?
|
|
if (mb_col < cm->mb_cols / 2) {
|
|
if (mv.as_mv.col > 0)
|
|
--sum_in_vectors;
|
|
else if (mv.as_mv.col < 0)
|
|
++sum_in_vectors;
|
|
} else if (mb_col > cm->mb_cols / 2) {
|
|
if (mv.as_mv.col > 0)
|
|
++sum_in_vectors;
|
|
else if (mv.as_mv.col < 0)
|
|
--sum_in_vectors;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
sr_coded_error += (int64_t)this_error;
|
|
}
|
|
coded_error += (int64_t)this_error;
|
|
|
|
// Adjust to the next column of MBs.
|
|
x->plane[0].src.buf += 16;
|
|
x->plane[1].src.buf += uv_mb_height;
|
|
x->plane[2].src.buf += uv_mb_height;
|
|
|
|
recon_yoffset += 16;
|
|
recon_uvoffset += uv_mb_height;
|
|
}
|
|
|
|
// Adjust to the next row of MBs.
|
|
x->plane[0].src.buf += 16 * x->plane[0].src.stride - 16 * cm->mb_cols;
|
|
x->plane[1].src.buf += uv_mb_height * x->plane[1].src.stride -
|
|
uv_mb_height * cm->mb_cols;
|
|
x->plane[2].src.buf += uv_mb_height * x->plane[1].src.stride -
|
|
uv_mb_height * cm->mb_cols;
|
|
|
|
vp9_clear_system_state();
|
|
}
|
|
|
|
vp9_clear_system_state();
|
|
{
|
|
FIRSTPASS_STATS fps;
|
|
|
|
fps.frame = cm->current_video_frame;
|
|
fps.spatial_layer_id = cpi->svc.spatial_layer_id;
|
|
fps.intra_error = (double)(intra_error >> 8);
|
|
fps.coded_error = (double)(coded_error >> 8);
|
|
fps.sr_coded_error = (double)(sr_coded_error >> 8);
|
|
fps.count = 1.0;
|
|
fps.pcnt_inter = (double)intercount / cm->MBs;
|
|
fps.pcnt_second_ref = (double)second_ref_count / cm->MBs;
|
|
fps.pcnt_neutral = (double)neutral_count / cm->MBs;
|
|
|
|
if (mvcount > 0) {
|
|
fps.MVr = (double)sum_mvr / mvcount;
|
|
fps.mvr_abs = (double)sum_mvr_abs / mvcount;
|
|
fps.MVc = (double)sum_mvc / mvcount;
|
|
fps.mvc_abs = (double)sum_mvc_abs / mvcount;
|
|
fps.MVrv = ((double)sum_mvrs - (fps.MVr * fps.MVr / mvcount)) / mvcount;
|
|
fps.MVcv = ((double)sum_mvcs - (fps.MVc * fps.MVc / mvcount)) / mvcount;
|
|
fps.mv_in_out_count = (double)sum_in_vectors / (mvcount * 2);
|
|
fps.new_mv_count = new_mv_count;
|
|
fps.pcnt_motion = (double)mvcount / cm->MBs;
|
|
} else {
|
|
fps.MVr = 0.0;
|
|
fps.mvr_abs = 0.0;
|
|
fps.MVc = 0.0;
|
|
fps.mvc_abs = 0.0;
|
|
fps.MVrv = 0.0;
|
|
fps.MVcv = 0.0;
|
|
fps.mv_in_out_count = 0.0;
|
|
fps.new_mv_count = 0.0;
|
|
fps.pcnt_motion = 0.0;
|
|
}
|
|
|
|
// TODO(paulwilkins): Handle the case when duration is set to 0, or
|
|
// something less than the full time between subsequent values of
|
|
// cpi->source_time_stamp.
|
|
fps.duration = (double)(cpi->source->ts_end - cpi->source->ts_start);
|
|
|
|
// Don't want to do output stats with a stack variable!
|
|
twopass->this_frame_stats = fps;
|
|
output_stats(&twopass->this_frame_stats, cpi->output_pkt_list);
|
|
accumulate_stats(&twopass->total_stats, &fps);
|
|
}
|
|
|
|
// Copy the previous Last Frame back into gf and and arf buffers if
|
|
// the prediction is good enough... but also don't allow it to lag too far.
|
|
if ((twopass->sr_update_lag > 3) ||
|
|
((cm->current_video_frame > 0) &&
|
|
(twopass->this_frame_stats.pcnt_inter > 0.20) &&
|
|
((twopass->this_frame_stats.intra_error /
|
|
DOUBLE_DIVIDE_CHECK(twopass->this_frame_stats.coded_error)) > 2.0))) {
|
|
if (gld_yv12 != NULL) {
|
|
vp8_yv12_copy_frame(lst_yv12, gld_yv12);
|
|
}
|
|
twopass->sr_update_lag = 1;
|
|
} else {
|
|
++twopass->sr_update_lag;
|
|
}
|
|
|
|
vp9_extend_frame_borders(new_yv12);
|
|
|
|
if (cpi->use_svc && cpi->svc.number_temporal_layers == 1) {
|
|
vp9_update_reference_frames(cpi);
|
|
} else {
|
|
// Swap frame pointers so last frame refers to the frame we just compressed.
|
|
swap_yv12(lst_yv12, new_yv12);
|
|
}
|
|
|
|
// Special case for the first frame. Copy into the GF buffer as a second
|
|
// reference.
|
|
if (cm->current_video_frame == 0 && gld_yv12 != NULL) {
|
|
vp8_yv12_copy_frame(lst_yv12, gld_yv12);
|
|
}
|
|
|
|
// Use this to see what the first pass reconstruction looks like.
|
|
if (0) {
|
|
char filename[512];
|
|
FILE *recon_file;
|
|
snprintf(filename, sizeof(filename), "enc%04d.yuv",
|
|
(int)cm->current_video_frame);
|
|
|
|
if (cm->current_video_frame == 0)
|
|
recon_file = fopen(filename, "wb");
|
|
else
|
|
recon_file = fopen(filename, "ab");
|
|
|
|
(void)fwrite(lst_yv12->buffer_alloc, lst_yv12->frame_size, 1, recon_file);
|
|
fclose(recon_file);
|
|
}
|
|
|
|
++cm->current_video_frame;
|
|
}
|
|
|
|
static double calc_correction_factor(double err_per_mb,
|
|
double err_divisor,
|
|
double pt_low,
|
|
double pt_high,
|
|
int q) {
|
|
const double error_term = err_per_mb / err_divisor;
|
|
|
|
// Adjustment based on actual quantizer to power term.
|
|
const double power_term = MIN(vp9_convert_qindex_to_q(q) * 0.0125 + pt_low,
|
|
pt_high);
|
|
|
|
// Calculate correction factor.
|
|
if (power_term < 1.0)
|
|
assert(error_term >= 0.0);
|
|
|
|
return fclamp(pow(error_term, power_term), 0.05, 5.0);
|
|
}
|
|
|
|
static int get_twopass_worst_quality(const VP9_COMP *cpi,
|
|
const FIRSTPASS_STATS *stats,
|
|
int section_target_bandwidth) {
|
|
const RATE_CONTROL *const rc = &cpi->rc;
|
|
const VP9EncoderConfig *const oxcf = &cpi->oxcf;
|
|
|
|
if (section_target_bandwidth <= 0) {
|
|
return rc->worst_quality; // Highest value allowed
|
|
} else {
|
|
const int num_mbs = cpi->common.MBs;
|
|
const double section_err = stats->coded_error / stats->count;
|
|
const double err_per_mb = section_err / num_mbs;
|
|
const double speed_term = 1.0 + 0.04 * oxcf->speed;
|
|
const int target_norm_bits_per_mb = ((uint64_t)section_target_bandwidth <<
|
|
BPER_MB_NORMBITS) / num_mbs;
|
|
int q;
|
|
int is_svc_upper_layer = 0;
|
|
if (cpi->use_svc && cpi->svc.number_temporal_layers == 1 &&
|
|
cpi->svc.spatial_layer_id > 0) {
|
|
is_svc_upper_layer = 1;
|
|
}
|
|
|
|
// Try and pick a max Q that will be high enough to encode the
|
|
// content at the given rate.
|
|
for (q = rc->best_quality; q < rc->worst_quality; ++q) {
|
|
const double factor =
|
|
calc_correction_factor(err_per_mb, ERR_DIVISOR,
|
|
is_svc_upper_layer ? SVC_FACTOR_PT_LOW :
|
|
FACTOR_PT_LOW, FACTOR_PT_HIGH, q);
|
|
const int bits_per_mb = vp9_rc_bits_per_mb(INTER_FRAME, q,
|
|
factor * speed_term);
|
|
if (bits_per_mb <= target_norm_bits_per_mb)
|
|
break;
|
|
}
|
|
|
|
// Restriction on active max q for constrained quality mode.
|
|
if (cpi->oxcf.rc_mode == VPX_CQ)
|
|
q = MAX(q, oxcf->cq_level);
|
|
return q;
|
|
}
|
|
}
|
|
|
|
extern void vp9_new_framerate(VP9_COMP *cpi, double framerate);
|
|
|
|
void vp9_init_second_pass(VP9_COMP *cpi) {
|
|
SVC *const svc = &cpi->svc;
|
|
const VP9EncoderConfig *const oxcf = &cpi->oxcf;
|
|
const int is_spatial_svc = (svc->number_spatial_layers > 1) &&
|
|
(svc->number_temporal_layers == 1);
|
|
TWO_PASS *const twopass = is_spatial_svc ?
|
|
&svc->layer_context[svc->spatial_layer_id].twopass : &cpi->twopass;
|
|
double frame_rate;
|
|
FIRSTPASS_STATS *stats;
|
|
|
|
zero_stats(&twopass->total_stats);
|
|
zero_stats(&twopass->total_left_stats);
|
|
|
|
if (!twopass->stats_in_end)
|
|
return;
|
|
|
|
stats = &twopass->total_stats;
|
|
|
|
*stats = *twopass->stats_in_end;
|
|
twopass->total_left_stats = *stats;
|
|
|
|
frame_rate = 10000000.0 * stats->count / stats->duration;
|
|
// Each frame can have a different duration, as the frame rate in the source
|
|
// isn't guaranteed to be constant. The frame rate prior to the first frame
|
|
// encoded in the second pass is a guess. However, the sum duration is not.
|
|
// It is calculated based on the actual durations of all frames from the
|
|
// first pass.
|
|
|
|
if (is_spatial_svc) {
|
|
vp9_update_spatial_layer_framerate(cpi, frame_rate);
|
|
twopass->bits_left = (int64_t)(stats->duration *
|
|
svc->layer_context[svc->spatial_layer_id].target_bandwidth /
|
|
10000000.0);
|
|
} else {
|
|
vp9_new_framerate(cpi, frame_rate);
|
|
twopass->bits_left = (int64_t)(stats->duration * oxcf->target_bandwidth /
|
|
10000000.0);
|
|
}
|
|
|
|
// Calculate a minimum intra value to be used in determining the IIratio
|
|
// scores used in the second pass. We have this minimum to make sure
|
|
// that clips that are static but "low complexity" in the intra domain
|
|
// are still boosted appropriately for KF/GF/ARF.
|
|
if (!is_spatial_svc) {
|
|
// We don't know the number of MBs for each layer at this point.
|
|
// So we will do it later.
|
|
twopass->kf_intra_err_min = KF_MB_INTRA_MIN * cpi->common.MBs;
|
|
twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs;
|
|
}
|
|
|
|
// This variable monitors how far behind the second ref update is lagging.
|
|
twopass->sr_update_lag = 1;
|
|
|
|
// Scan the first pass file and calculate a modified total error based upon
|
|
// the bias/power function used to allocate bits.
|
|
{
|
|
const double avg_error = stats->coded_error /
|
|
DOUBLE_DIVIDE_CHECK(stats->count);
|
|
const FIRSTPASS_STATS *s = twopass->stats_in;
|
|
double modified_error_total = 0.0;
|
|
twopass->modified_error_min = (avg_error *
|
|
oxcf->two_pass_vbrmin_section) / 100;
|
|
twopass->modified_error_max = (avg_error *
|
|
oxcf->two_pass_vbrmax_section) / 100;
|
|
while (s < twopass->stats_in_end) {
|
|
modified_error_total += calculate_modified_err(twopass, oxcf, s);
|
|
++s;
|
|
}
|
|
twopass->modified_error_left = modified_error_total;
|
|
}
|
|
|
|
// Reset the vbr bits off target counter
|
|
cpi->rc.vbr_bits_off_target = 0;
|
|
}
|
|
|
|
// This function gives an estimate of how badly we believe the prediction
|
|
// quality is decaying from frame to frame.
|
|
static double get_prediction_decay_rate(const VP9_COMMON *cm,
|
|
const FIRSTPASS_STATS *next_frame) {
|
|
// Look at the observed drop in prediction quality between the last frame
|
|
// and the GF buffer (which contains an older frame).
|
|
const double mb_sr_err_diff = (next_frame->sr_coded_error -
|
|
next_frame->coded_error) / cm->MBs;
|
|
const double second_ref_decay = mb_sr_err_diff <= 512.0
|
|
? fclamp(pow(1.0 - (mb_sr_err_diff / 512.0), 0.5), 0.85, 1.0)
|
|
: 0.85;
|
|
|
|
return MIN(second_ref_decay, next_frame->pcnt_inter);
|
|
}
|
|
|
|
// Function to test for a condition where a complex transition is followed
|
|
// by a static section. For example in slide shows where there is a fade
|
|
// between slides. This is to help with more optimal kf and gf positioning.
|
|
static int detect_transition_to_still(TWO_PASS *twopass,
|
|
int frame_interval, int still_interval,
|
|
double loop_decay_rate,
|
|
double last_decay_rate) {
|
|
int trans_to_still = 0;
|
|
|
|
// Break clause to detect very still sections after motion
|
|
// For example a static image after a fade or other transition
|
|
// instead of a clean scene cut.
|
|
if (frame_interval > MIN_GF_INTERVAL &&
|
|
loop_decay_rate >= 0.999 &&
|
|
last_decay_rate < 0.9) {
|
|
int j;
|
|
const FIRSTPASS_STATS *position = twopass->stats_in;
|
|
FIRSTPASS_STATS tmp_next_frame;
|
|
|
|
// Look ahead a few frames to see if static condition persists...
|
|
for (j = 0; j < still_interval; ++j) {
|
|
if (EOF == input_stats(twopass, &tmp_next_frame))
|
|
break;
|
|
|
|
if (tmp_next_frame.pcnt_inter - tmp_next_frame.pcnt_motion < 0.999)
|
|
break;
|
|
}
|
|
|
|
reset_fpf_position(twopass, position);
|
|
|
|
// Only if it does do we signal a transition to still.
|
|
if (j == still_interval)
|
|
trans_to_still = 1;
|
|
}
|
|
|
|
return trans_to_still;
|
|
}
|
|
|
|
// This function detects a flash through the high relative pcnt_second_ref
|
|
// score in the frame following a flash frame. The offset passed in should
|
|
// reflect this.
|
|
static int detect_flash(const TWO_PASS *twopass, int offset) {
|
|
FIRSTPASS_STATS next_frame;
|
|
|
|
int flash_detected = 0;
|
|
|
|
// Read the frame data.
|
|
// The return is FALSE (no flash detected) if not a valid frame
|
|
if (read_frame_stats(twopass, &next_frame, offset) != EOF) {
|
|
// What we are looking for here is a situation where there is a
|
|
// brief break in prediction (such as a flash) but subsequent frames
|
|
// are reasonably well predicted by an earlier (pre flash) frame.
|
|
// The recovery after a flash is indicated by a high pcnt_second_ref
|
|
// compared to pcnt_inter.
|
|
if (next_frame.pcnt_second_ref > next_frame.pcnt_inter &&
|
|
next_frame.pcnt_second_ref >= 0.5)
|
|
flash_detected = 1;
|
|
}
|
|
|
|
return flash_detected;
|
|
}
|
|
|
|
// Update the motion related elements to the GF arf boost calculation.
|
|
static void accumulate_frame_motion_stats(const FIRSTPASS_STATS *stats,
|
|
double *mv_in_out,
|
|
double *mv_in_out_accumulator,
|
|
double *abs_mv_in_out_accumulator,
|
|
double *mv_ratio_accumulator) {
|
|
const double pct = stats->pcnt_motion;
|
|
|
|
// Accumulate Motion In/Out of frame stats.
|
|
*mv_in_out = stats->mv_in_out_count * pct;
|
|
*mv_in_out_accumulator += *mv_in_out;
|
|
*abs_mv_in_out_accumulator += fabs(*mv_in_out);
|
|
|
|
// Accumulate a measure of how uniform (or conversely how random) the motion
|
|
// field is (a ratio of abs(mv) / mv).
|
|
if (pct > 0.05) {
|
|
const double mvr_ratio = fabs(stats->mvr_abs) /
|
|
DOUBLE_DIVIDE_CHECK(fabs(stats->MVr));
|
|
const double mvc_ratio = fabs(stats->mvc_abs) /
|
|
DOUBLE_DIVIDE_CHECK(fabs(stats->MVc));
|
|
|
|
*mv_ratio_accumulator += pct * (mvr_ratio < stats->mvr_abs ?
|
|
mvr_ratio : stats->mvr_abs);
|
|
*mv_ratio_accumulator += pct * (mvc_ratio < stats->mvc_abs ?
|
|
mvc_ratio : stats->mvc_abs);
|
|
}
|
|
}
|
|
|
|
// Calculate a baseline boost number for the current frame.
|
|
static double calc_frame_boost(const TWO_PASS *twopass,
|
|
const FIRSTPASS_STATS *this_frame,
|
|
double this_frame_mv_in_out) {
|
|
double frame_boost;
|
|
|
|
// Underlying boost factor is based on inter intra error ratio.
|
|
if (this_frame->intra_error > twopass->gf_intra_err_min)
|
|
frame_boost = (IIFACTOR * this_frame->intra_error /
|
|
DOUBLE_DIVIDE_CHECK(this_frame->coded_error));
|
|
else
|
|
frame_boost = (IIFACTOR * twopass->gf_intra_err_min /
|
|
DOUBLE_DIVIDE_CHECK(this_frame->coded_error));
|
|
|
|
// Increase boost for frames where new data coming into frame (e.g. zoom out).
|
|
// Slightly reduce boost if there is a net balance of motion out of the frame
|
|
// (zoom in). The range for this_frame_mv_in_out is -1.0 to +1.0.
|
|
if (this_frame_mv_in_out > 0.0)
|
|
frame_boost += frame_boost * (this_frame_mv_in_out * 2.0);
|
|
// In the extreme case the boost is halved.
|
|
else
|
|
frame_boost += frame_boost * (this_frame_mv_in_out / 2.0);
|
|
|
|
return MIN(frame_boost, GF_RMAX);
|
|
}
|
|
|
|
static int calc_arf_boost(VP9_COMP *cpi, int offset,
|
|
int f_frames, int b_frames,
|
|
int *f_boost, int *b_boost) {
|
|
FIRSTPASS_STATS this_frame;
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
int i;
|
|
double boost_score = 0.0;
|
|
double mv_ratio_accumulator = 0.0;
|
|
double decay_accumulator = 1.0;
|
|
double this_frame_mv_in_out = 0.0;
|
|
double mv_in_out_accumulator = 0.0;
|
|
double abs_mv_in_out_accumulator = 0.0;
|
|
int arf_boost;
|
|
int flash_detected = 0;
|
|
|
|
// Search forward from the proposed arf/next gf position.
|
|
for (i = 0; i < f_frames; ++i) {
|
|
if (read_frame_stats(twopass, &this_frame, (i + offset)) == EOF)
|
|
break;
|
|
|
|
// Update the motion related elements to the boost calculation.
|
|
accumulate_frame_motion_stats(&this_frame,
|
|
&this_frame_mv_in_out, &mv_in_out_accumulator,
|
|
&abs_mv_in_out_accumulator,
|
|
&mv_ratio_accumulator);
|
|
|
|
// We want to discount the flash frame itself and the recovery
|
|
// frame that follows as both will have poor scores.
|
|
flash_detected = detect_flash(twopass, i + offset) ||
|
|
detect_flash(twopass, i + offset + 1);
|
|
|
|
// Accumulate the effect of prediction quality decay.
|
|
if (!flash_detected) {
|
|
decay_accumulator *= get_prediction_decay_rate(&cpi->common, &this_frame);
|
|
decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
|
|
? MIN_DECAY_FACTOR : decay_accumulator;
|
|
}
|
|
|
|
boost_score += decay_accumulator * calc_frame_boost(twopass, &this_frame,
|
|
this_frame_mv_in_out);
|
|
}
|
|
|
|
*f_boost = (int)boost_score;
|
|
|
|
// Reset for backward looking loop.
|
|
boost_score = 0.0;
|
|
mv_ratio_accumulator = 0.0;
|
|
decay_accumulator = 1.0;
|
|
this_frame_mv_in_out = 0.0;
|
|
mv_in_out_accumulator = 0.0;
|
|
abs_mv_in_out_accumulator = 0.0;
|
|
|
|
// Search backward towards last gf position.
|
|
for (i = -1; i >= -b_frames; --i) {
|
|
if (read_frame_stats(twopass, &this_frame, (i + offset)) == EOF)
|
|
break;
|
|
|
|
// Update the motion related elements to the boost calculation.
|
|
accumulate_frame_motion_stats(&this_frame,
|
|
&this_frame_mv_in_out, &mv_in_out_accumulator,
|
|
&abs_mv_in_out_accumulator,
|
|
&mv_ratio_accumulator);
|
|
|
|
// We want to discount the the flash frame itself and the recovery
|
|
// frame that follows as both will have poor scores.
|
|
flash_detected = detect_flash(twopass, i + offset) ||
|
|
detect_flash(twopass, i + offset + 1);
|
|
|
|
// Cumulative effect of prediction quality decay.
|
|
if (!flash_detected) {
|
|
decay_accumulator *= get_prediction_decay_rate(&cpi->common, &this_frame);
|
|
decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
|
|
? MIN_DECAY_FACTOR : decay_accumulator;
|
|
}
|
|
|
|
boost_score += decay_accumulator * calc_frame_boost(twopass, &this_frame,
|
|
this_frame_mv_in_out);
|
|
}
|
|
*b_boost = (int)boost_score;
|
|
|
|
arf_boost = (*f_boost + *b_boost);
|
|
if (arf_boost < ((b_frames + f_frames) * 20))
|
|
arf_boost = ((b_frames + f_frames) * 20);
|
|
|
|
return arf_boost;
|
|
}
|
|
|
|
// Calculate a section intra ratio used in setting max loop filter.
|
|
static int calculate_section_intra_ratio(const FIRSTPASS_STATS *begin,
|
|
const FIRSTPASS_STATS *end,
|
|
int section_length) {
|
|
const FIRSTPASS_STATS *s = begin;
|
|
double intra_error = 0.0;
|
|
double coded_error = 0.0;
|
|
int i = 0;
|
|
|
|
while (s < end && i < section_length) {
|
|
intra_error += s->intra_error;
|
|
coded_error += s->coded_error;
|
|
++s;
|
|
++i;
|
|
}
|
|
|
|
return (int)(intra_error / DOUBLE_DIVIDE_CHECK(coded_error));
|
|
}
|
|
|
|
// Calculate the total bits to allocate in this GF/ARF group.
|
|
static int64_t calculate_total_gf_group_bits(VP9_COMP *cpi,
|
|
double gf_group_err) {
|
|
const RATE_CONTROL *const rc = &cpi->rc;
|
|
const TWO_PASS *const twopass = &cpi->twopass;
|
|
const int max_bits = frame_max_bits(rc, &cpi->oxcf);
|
|
int64_t total_group_bits;
|
|
|
|
// Calculate the bits to be allocated to the group as a whole.
|
|
if ((twopass->kf_group_bits > 0) && (twopass->kf_group_error_left > 0)) {
|
|
total_group_bits = (int64_t)(twopass->kf_group_bits *
|
|
(gf_group_err / twopass->kf_group_error_left));
|
|
} else {
|
|
total_group_bits = 0;
|
|
}
|
|
|
|
// Clamp odd edge cases.
|
|
total_group_bits = (total_group_bits < 0) ?
|
|
0 : (total_group_bits > twopass->kf_group_bits) ?
|
|
twopass->kf_group_bits : total_group_bits;
|
|
|
|
// Clip based on user supplied data rate variability limit.
|
|
if (total_group_bits > (int64_t)max_bits * rc->baseline_gf_interval)
|
|
total_group_bits = (int64_t)max_bits * rc->baseline_gf_interval;
|
|
|
|
return total_group_bits;
|
|
}
|
|
|
|
// Calculate the number bits extra to assign to boosted frames in a group.
|
|
static int calculate_boost_bits(int frame_count,
|
|
int boost, int64_t total_group_bits) {
|
|
int allocation_chunks;
|
|
|
|
// return 0 for invalid inputs (could arise e.g. through rounding errors)
|
|
if (!boost || (total_group_bits <= 0) || (frame_count <= 0) )
|
|
return 0;
|
|
|
|
allocation_chunks = (frame_count * 100) + boost;
|
|
|
|
// Prevent overflow.
|
|
if (boost > 1023) {
|
|
int divisor = boost >> 10;
|
|
boost /= divisor;
|
|
allocation_chunks /= divisor;
|
|
}
|
|
|
|
// Calculate the number of extra bits for use in the boosted frame or frames.
|
|
return MAX((int)(((int64_t)boost * total_group_bits) / allocation_chunks), 0);
|
|
}
|
|
|
|
// Current limit on maximum number of active arfs in a GF/ARF group.
|
|
#define MAX_ACTIVE_ARFS 2
|
|
#define ARF_SLOT1 2
|
|
#define ARF_SLOT2 3
|
|
// This function indirects the choice of buffers for arfs.
|
|
// At the moment the values are fixed but this may change as part of
|
|
// the integration process with other codec features that swap buffers around.
|
|
static void get_arf_buffer_indices(unsigned char *arf_buffer_indices) {
|
|
arf_buffer_indices[0] = ARF_SLOT1;
|
|
arf_buffer_indices[1] = ARF_SLOT2;
|
|
}
|
|
|
|
static void allocate_gf_group_bits(VP9_COMP *cpi, int64_t gf_group_bits,
|
|
double group_error, int gf_arf_bits) {
|
|
RATE_CONTROL *const rc = &cpi->rc;
|
|
const VP9EncoderConfig *const oxcf = &cpi->oxcf;
|
|
TWO_PASS *twopass = &cpi->twopass;
|
|
FIRSTPASS_STATS frame_stats;
|
|
int i;
|
|
int frame_index = 1;
|
|
int target_frame_size;
|
|
int key_frame;
|
|
const int max_bits = frame_max_bits(&cpi->rc, &cpi->oxcf);
|
|
int64_t total_group_bits = gf_group_bits;
|
|
double modified_err = 0.0;
|
|
double err_fraction;
|
|
int mid_boost_bits = 0;
|
|
int mid_frame_idx;
|
|
unsigned char arf_buffer_indices[MAX_ACTIVE_ARFS];
|
|
|
|
key_frame = cpi->common.frame_type == KEY_FRAME ||
|
|
vp9_is_upper_layer_key_frame(cpi);
|
|
|
|
get_arf_buffer_indices(arf_buffer_indices);
|
|
|
|
// For key frames the frame target rate is already set and it
|
|
// is also the golden frame.
|
|
if (!key_frame) {
|
|
if (rc->source_alt_ref_active) {
|
|
twopass->gf_group.update_type[0] = OVERLAY_UPDATE;
|
|
twopass->gf_group.rf_level[0] = INTER_NORMAL;
|
|
twopass->gf_group.bit_allocation[0] = 0;
|
|
twopass->gf_group.arf_update_idx[0] = arf_buffer_indices[0];
|
|
twopass->gf_group.arf_ref_idx[0] = arf_buffer_indices[0];
|
|
} else {
|
|
twopass->gf_group.update_type[0] = GF_UPDATE;
|
|
twopass->gf_group.rf_level[0] = GF_ARF_STD;
|
|
twopass->gf_group.bit_allocation[0] = gf_arf_bits;
|
|
twopass->gf_group.arf_update_idx[0] = arf_buffer_indices[0];
|
|
twopass->gf_group.arf_ref_idx[0] = arf_buffer_indices[0];
|
|
}
|
|
|
|
// Step over the golden frame / overlay frame
|
|
if (EOF == input_stats(twopass, &frame_stats))
|
|
return;
|
|
}
|
|
|
|
// Deduct the boost bits for arf (or gf if it is not a key frame)
|
|
// from the group total.
|
|
if (rc->source_alt_ref_pending || !key_frame)
|
|
total_group_bits -= gf_arf_bits;
|
|
|
|
// Store the bits to spend on the ARF if there is one.
|
|
if (rc->source_alt_ref_pending) {
|
|
if (cpi->multi_arf_enabled) {
|
|
// A portion of the gf / arf extra bits are set asside for lower level
|
|
// boosted frames in the middle of the group.
|
|
mid_boost_bits += gf_arf_bits >> 5;
|
|
gf_arf_bits -= (gf_arf_bits >> 5);
|
|
}
|
|
|
|
twopass->gf_group.update_type[frame_index] = ARF_UPDATE;
|
|
twopass->gf_group.rf_level[frame_index] = GF_ARF_STD;
|
|
twopass->gf_group.bit_allocation[frame_index] = gf_arf_bits;
|
|
twopass->gf_group.arf_src_offset[frame_index] =
|
|
(unsigned char)(rc->baseline_gf_interval - 1);
|
|
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[0];
|
|
twopass->gf_group.arf_ref_idx[frame_index] = arf_buffer_indices[0];
|
|
++frame_index;
|
|
|
|
if (cpi->multi_arf_enabled) {
|
|
// Set aside a slot for a level 1 arf.
|
|
twopass->gf_group.update_type[frame_index] = ARF_UPDATE;
|
|
twopass->gf_group.rf_level[frame_index] = GF_ARF_LOW;
|
|
twopass->gf_group.arf_src_offset[frame_index] =
|
|
(unsigned char)((rc->baseline_gf_interval >> 1) - 1);
|
|
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[1];
|
|
twopass->gf_group.arf_ref_idx[frame_index] = arf_buffer_indices[0];
|
|
++frame_index;
|
|
}
|
|
}
|
|
|
|
// Define middle frame
|
|
mid_frame_idx = frame_index + (rc->baseline_gf_interval >> 1) - 1;
|
|
|
|
// Allocate bits to the other frames in the group.
|
|
for (i = 0; i < rc->baseline_gf_interval - 1; ++i) {
|
|
int arf_idx = 0;
|
|
if (EOF == input_stats(twopass, &frame_stats))
|
|
break;
|
|
|
|
modified_err = calculate_modified_err(twopass, oxcf, &frame_stats);
|
|
|
|
if (group_error > 0)
|
|
err_fraction = modified_err / DOUBLE_DIVIDE_CHECK(group_error);
|
|
else
|
|
err_fraction = 0.0;
|
|
|
|
target_frame_size = (int)((double)total_group_bits * err_fraction);
|
|
|
|
if (rc->source_alt_ref_pending && cpi->multi_arf_enabled) {
|
|
mid_boost_bits += (target_frame_size >> 4);
|
|
target_frame_size -= (target_frame_size >> 4);
|
|
|
|
if (frame_index <= mid_frame_idx)
|
|
arf_idx = 1;
|
|
}
|
|
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[arf_idx];
|
|
twopass->gf_group.arf_ref_idx[frame_index] = arf_buffer_indices[arf_idx];
|
|
|
|
target_frame_size = clamp(target_frame_size, 0,
|
|
MIN(max_bits, (int)total_group_bits));
|
|
|
|
twopass->gf_group.update_type[frame_index] = LF_UPDATE;
|
|
twopass->gf_group.rf_level[frame_index] = INTER_NORMAL;
|
|
|
|
twopass->gf_group.bit_allocation[frame_index] = target_frame_size;
|
|
++frame_index;
|
|
}
|
|
|
|
// Note:
|
|
// We need to configure the frame at the end of the sequence + 1 that will be
|
|
// the start frame for the next group. Otherwise prior to the call to
|
|
// vp9_rc_get_second_pass_params() the data will be undefined.
|
|
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[0];
|
|
twopass->gf_group.arf_ref_idx[frame_index] = arf_buffer_indices[0];
|
|
|
|
if (rc->source_alt_ref_pending) {
|
|
twopass->gf_group.update_type[frame_index] = OVERLAY_UPDATE;
|
|
twopass->gf_group.rf_level[frame_index] = INTER_NORMAL;
|
|
|
|
// Final setup for second arf and its overlay.
|
|
if (cpi->multi_arf_enabled) {
|
|
twopass->gf_group.bit_allocation[2] =
|
|
twopass->gf_group.bit_allocation[mid_frame_idx] + mid_boost_bits;
|
|
twopass->gf_group.update_type[mid_frame_idx] = OVERLAY_UPDATE;
|
|
twopass->gf_group.bit_allocation[mid_frame_idx] = 0;
|
|
}
|
|
} else {
|
|
twopass->gf_group.update_type[frame_index] = GF_UPDATE;
|
|
twopass->gf_group.rf_level[frame_index] = GF_ARF_STD;
|
|
}
|
|
}
|
|
|
|
// Analyse and define a gf/arf group.
|
|
static void define_gf_group(VP9_COMP *cpi, FIRSTPASS_STATS *this_frame) {
|
|
RATE_CONTROL *const rc = &cpi->rc;
|
|
const VP9EncoderConfig *const oxcf = &cpi->oxcf;
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
FIRSTPASS_STATS next_frame;
|
|
const FIRSTPASS_STATS *const start_pos = twopass->stats_in;
|
|
int i;
|
|
|
|
double boost_score = 0.0;
|
|
double old_boost_score = 0.0;
|
|
double gf_group_err = 0.0;
|
|
double gf_first_frame_err = 0.0;
|
|
double mod_frame_err = 0.0;
|
|
|
|
double mv_ratio_accumulator = 0.0;
|
|
double decay_accumulator = 1.0;
|
|
double zero_motion_accumulator = 1.0;
|
|
|
|
double loop_decay_rate = 1.00;
|
|
double last_loop_decay_rate = 1.00;
|
|
|
|
double this_frame_mv_in_out = 0.0;
|
|
double mv_in_out_accumulator = 0.0;
|
|
double abs_mv_in_out_accumulator = 0.0;
|
|
double mv_ratio_accumulator_thresh;
|
|
unsigned int allow_alt_ref = is_altref_enabled(oxcf);
|
|
|
|
int f_boost = 0;
|
|
int b_boost = 0;
|
|
int flash_detected;
|
|
int active_max_gf_interval;
|
|
int64_t gf_group_bits;
|
|
double gf_group_error_left;
|
|
int gf_arf_bits;
|
|
|
|
// Reset the GF group data structures unless this is a key
|
|
// frame in which case it will already have been done.
|
|
if (cpi->common.frame_type != KEY_FRAME) {
|
|
vp9_zero(twopass->gf_group);
|
|
}
|
|
|
|
vp9_clear_system_state();
|
|
vp9_zero(next_frame);
|
|
|
|
gf_group_bits = 0;
|
|
|
|
// Load stats for the current frame.
|
|
mod_frame_err = calculate_modified_err(twopass, oxcf, this_frame);
|
|
|
|
// Note the error of the frame at the start of the group. This will be
|
|
// the GF frame error if we code a normal gf.
|
|
gf_first_frame_err = mod_frame_err;
|
|
|
|
// If this is a key frame or the overlay from a previous arf then
|
|
// the error score / cost of this frame has already been accounted for.
|
|
if (cpi->common.frame_type == KEY_FRAME || rc->source_alt_ref_active)
|
|
gf_group_err -= gf_first_frame_err;
|
|
|
|
// Motion breakout threshold for loop below depends on image size.
|
|
mv_ratio_accumulator_thresh = (cpi->common.width + cpi->common.height) / 10.0;
|
|
|
|
// Work out a maximum interval for the GF.
|
|
// If the image appears completely static we can extend beyond this.
|
|
// The value chosen depends on the active Q range. At low Q we have
|
|
// bits to spare and are better with a smaller interval and smaller boost.
|
|
// At high Q when there are few bits to spare we are better with a longer
|
|
// interval to spread the cost of the GF.
|
|
//
|
|
active_max_gf_interval =
|
|
12 + ((int)vp9_convert_qindex_to_q(rc->last_q[INTER_FRAME]) >> 5);
|
|
|
|
if (active_max_gf_interval > rc->max_gf_interval)
|
|
active_max_gf_interval = rc->max_gf_interval;
|
|
|
|
i = 0;
|
|
while (i < rc->static_scene_max_gf_interval && i < rc->frames_to_key) {
|
|
++i;
|
|
|
|
// Accumulate error score of frames in this gf group.
|
|
mod_frame_err = calculate_modified_err(twopass, oxcf, this_frame);
|
|
gf_group_err += mod_frame_err;
|
|
|
|
if (EOF == input_stats(twopass, &next_frame))
|
|
break;
|
|
|
|
// Test for the case where there is a brief flash but the prediction
|
|
// quality back to an earlier frame is then restored.
|
|
flash_detected = detect_flash(twopass, 0);
|
|
|
|
// Update the motion related elements to the boost calculation.
|
|
accumulate_frame_motion_stats(&next_frame,
|
|
&this_frame_mv_in_out, &mv_in_out_accumulator,
|
|
&abs_mv_in_out_accumulator,
|
|
&mv_ratio_accumulator);
|
|
|
|
// Accumulate the effect of prediction quality decay.
|
|
if (!flash_detected) {
|
|
last_loop_decay_rate = loop_decay_rate;
|
|
loop_decay_rate = get_prediction_decay_rate(&cpi->common, &next_frame);
|
|
decay_accumulator = decay_accumulator * loop_decay_rate;
|
|
|
|
// Monitor for static sections.
|
|
if ((next_frame.pcnt_inter - next_frame.pcnt_motion) <
|
|
zero_motion_accumulator) {
|
|
zero_motion_accumulator = next_frame.pcnt_inter -
|
|
next_frame.pcnt_motion;
|
|
}
|
|
|
|
// Break clause to detect very still sections after motion. For example,
|
|
// a static image after a fade or other transition.
|
|
if (detect_transition_to_still(twopass, i, 5, loop_decay_rate,
|
|
last_loop_decay_rate)) {
|
|
allow_alt_ref = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Calculate a boost number for this frame.
|
|
boost_score += decay_accumulator * calc_frame_boost(twopass, &next_frame,
|
|
this_frame_mv_in_out);
|
|
|
|
// Break out conditions.
|
|
if (
|
|
// Break at active_max_gf_interval unless almost totally static.
|
|
(i >= active_max_gf_interval && (zero_motion_accumulator < 0.995)) ||
|
|
(
|
|
// Don't break out with a very short interval.
|
|
(i > MIN_GF_INTERVAL) &&
|
|
((boost_score > 125.0) || (next_frame.pcnt_inter < 0.75)) &&
|
|
(!flash_detected) &&
|
|
((mv_ratio_accumulator > mv_ratio_accumulator_thresh) ||
|
|
(abs_mv_in_out_accumulator > 3.0) ||
|
|
(mv_in_out_accumulator < -2.0) ||
|
|
((boost_score - old_boost_score) < IIFACTOR)))) {
|
|
boost_score = old_boost_score;
|
|
break;
|
|
}
|
|
|
|
*this_frame = next_frame;
|
|
|
|
old_boost_score = boost_score;
|
|
}
|
|
|
|
twopass->gf_zeromotion_pct = (int)(zero_motion_accumulator * 1000.0);
|
|
|
|
// Don't allow a gf too near the next kf.
|
|
if ((rc->frames_to_key - i) < MIN_GF_INTERVAL) {
|
|
while (i < (rc->frames_to_key + !rc->next_key_frame_forced)) {
|
|
++i;
|
|
|
|
if (EOF == input_stats(twopass, this_frame))
|
|
break;
|
|
|
|
if (i < rc->frames_to_key) {
|
|
mod_frame_err = calculate_modified_err(twopass, oxcf, this_frame);
|
|
gf_group_err += mod_frame_err;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the interval until the next gf.
|
|
if (cpi->common.frame_type == KEY_FRAME || rc->source_alt_ref_active)
|
|
rc->baseline_gf_interval = i - 1;
|
|
else
|
|
rc->baseline_gf_interval = i;
|
|
|
|
rc->frames_till_gf_update_due = rc->baseline_gf_interval;
|
|
|
|
// Should we use the alternate reference frame.
|
|
if (allow_alt_ref &&
|
|
(i < cpi->oxcf.lag_in_frames) &&
|
|
(i >= MIN_GF_INTERVAL) &&
|
|
// For real scene cuts (not forced kfs) don't allow arf very near kf.
|
|
(rc->next_key_frame_forced ||
|
|
(i <= (rc->frames_to_key - MIN_GF_INTERVAL)))) {
|
|
// Calculate the boost for alt ref.
|
|
rc->gfu_boost = calc_arf_boost(cpi, 0, (i - 1), (i - 1), &f_boost,
|
|
&b_boost);
|
|
rc->source_alt_ref_pending = 1;
|
|
|
|
} else {
|
|
rc->gfu_boost = (int)boost_score;
|
|
rc->source_alt_ref_pending = 0;
|
|
}
|
|
|
|
// Reset the file position.
|
|
reset_fpf_position(twopass, start_pos);
|
|
|
|
// Calculate the bits to be allocated to the gf/arf group as a whole
|
|
gf_group_bits = calculate_total_gf_group_bits(cpi, gf_group_err);
|
|
|
|
// Calculate the extra bits to be used for boosted frame(s)
|
|
{
|
|
int q = rc->last_q[INTER_FRAME];
|
|
int boost = (rc->gfu_boost * gfboost_qadjust(q)) / 100;
|
|
|
|
// Set max and minimum boost and hence minimum allocation.
|
|
boost = clamp(boost, 125, (rc->baseline_gf_interval + 1) * 200);
|
|
|
|
// Calculate the extra bits to be used for boosted frame(s)
|
|
gf_arf_bits = calculate_boost_bits(rc->baseline_gf_interval,
|
|
boost, gf_group_bits);
|
|
}
|
|
|
|
// Adjust KF group bits and error remaining.
|
|
twopass->kf_group_error_left -= (int64_t)gf_group_err;
|
|
|
|
// If this is an arf update we want to remove the score for the overlay
|
|
// frame at the end which will usually be very cheap to code.
|
|
// The overlay frame has already, in effect, been coded so we want to spread
|
|
// the remaining bits among the other frames.
|
|
// For normal GFs remove the score for the GF itself unless this is
|
|
// also a key frame in which case it has already been accounted for.
|
|
if (rc->source_alt_ref_pending) {
|
|
gf_group_error_left = gf_group_err - mod_frame_err;
|
|
} else if (cpi->common.frame_type != KEY_FRAME) {
|
|
gf_group_error_left = gf_group_err - gf_first_frame_err;
|
|
} else {
|
|
gf_group_error_left = gf_group_err;
|
|
}
|
|
|
|
// Allocate bits to each of the frames in the GF group.
|
|
allocate_gf_group_bits(cpi, gf_group_bits, gf_group_error_left, gf_arf_bits);
|
|
|
|
// Reset the file position.
|
|
reset_fpf_position(twopass, start_pos);
|
|
|
|
// Calculate a section intra ratio used in setting max loop filter.
|
|
if (cpi->common.frame_type != KEY_FRAME) {
|
|
twopass->section_intra_rating =
|
|
calculate_section_intra_ratio(start_pos, twopass->stats_in_end,
|
|
rc->baseline_gf_interval);
|
|
}
|
|
}
|
|
|
|
static int test_candidate_kf(TWO_PASS *twopass,
|
|
const FIRSTPASS_STATS *last_frame,
|
|
const FIRSTPASS_STATS *this_frame,
|
|
const FIRSTPASS_STATS *next_frame) {
|
|
int is_viable_kf = 0;
|
|
|
|
// Does the frame satisfy the primary criteria of a key frame?
|
|
// If so, then examine how well it predicts subsequent frames.
|
|
if ((this_frame->pcnt_second_ref < 0.10) &&
|
|
(next_frame->pcnt_second_ref < 0.10) &&
|
|
((this_frame->pcnt_inter < 0.05) ||
|
|
(((this_frame->pcnt_inter - this_frame->pcnt_neutral) < 0.35) &&
|
|
((this_frame->intra_error /
|
|
DOUBLE_DIVIDE_CHECK(this_frame->coded_error)) < 2.5) &&
|
|
((fabs(last_frame->coded_error - this_frame->coded_error) /
|
|
DOUBLE_DIVIDE_CHECK(this_frame->coded_error) > 0.40) ||
|
|
(fabs(last_frame->intra_error - this_frame->intra_error) /
|
|
DOUBLE_DIVIDE_CHECK(this_frame->intra_error) > 0.40) ||
|
|
((next_frame->intra_error /
|
|
DOUBLE_DIVIDE_CHECK(next_frame->coded_error)) > 3.5))))) {
|
|
int i;
|
|
const FIRSTPASS_STATS *start_pos = twopass->stats_in;
|
|
FIRSTPASS_STATS local_next_frame = *next_frame;
|
|
double boost_score = 0.0;
|
|
double old_boost_score = 0.0;
|
|
double decay_accumulator = 1.0;
|
|
|
|
// Examine how well the key frame predicts subsequent frames.
|
|
for (i = 0; i < 16; ++i) {
|
|
double next_iiratio = (IIKFACTOR1 * local_next_frame.intra_error /
|
|
DOUBLE_DIVIDE_CHECK(local_next_frame.coded_error));
|
|
|
|
if (next_iiratio > RMAX)
|
|
next_iiratio = RMAX;
|
|
|
|
// Cumulative effect of decay in prediction quality.
|
|
if (local_next_frame.pcnt_inter > 0.85)
|
|
decay_accumulator *= local_next_frame.pcnt_inter;
|
|
else
|
|
decay_accumulator *= (0.85 + local_next_frame.pcnt_inter) / 2.0;
|
|
|
|
// Keep a running total.
|
|
boost_score += (decay_accumulator * next_iiratio);
|
|
|
|
// Test various breakout clauses.
|
|
if ((local_next_frame.pcnt_inter < 0.05) ||
|
|
(next_iiratio < 1.5) ||
|
|
(((local_next_frame.pcnt_inter -
|
|
local_next_frame.pcnt_neutral) < 0.20) &&
|
|
(next_iiratio < 3.0)) ||
|
|
((boost_score - old_boost_score) < 3.0) ||
|
|
(local_next_frame.intra_error < 200)) {
|
|
break;
|
|
}
|
|
|
|
old_boost_score = boost_score;
|
|
|
|
// Get the next frame details
|
|
if (EOF == input_stats(twopass, &local_next_frame))
|
|
break;
|
|
}
|
|
|
|
// If there is tolerable prediction for at least the next 3 frames then
|
|
// break out else discard this potential key frame and move on
|
|
if (boost_score > 30.0 && (i > 3)) {
|
|
is_viable_kf = 1;
|
|
} else {
|
|
// Reset the file position
|
|
reset_fpf_position(twopass, start_pos);
|
|
|
|
is_viable_kf = 0;
|
|
}
|
|
}
|
|
|
|
return is_viable_kf;
|
|
}
|
|
|
|
static void find_next_key_frame(VP9_COMP *cpi, FIRSTPASS_STATS *this_frame) {
|
|
int i, j;
|
|
RATE_CONTROL *const rc = &cpi->rc;
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
const VP9EncoderConfig *const oxcf = &cpi->oxcf;
|
|
const FIRSTPASS_STATS first_frame = *this_frame;
|
|
const FIRSTPASS_STATS *const start_position = twopass->stats_in;
|
|
FIRSTPASS_STATS next_frame;
|
|
FIRSTPASS_STATS last_frame;
|
|
int kf_bits = 0;
|
|
double decay_accumulator = 1.0;
|
|
double zero_motion_accumulator = 1.0;
|
|
double boost_score = 0.0;
|
|
double kf_mod_err = 0.0;
|
|
double kf_group_err = 0.0;
|
|
double recent_loop_decay[8] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
|
|
|
|
vp9_zero(next_frame);
|
|
|
|
cpi->common.frame_type = KEY_FRAME;
|
|
|
|
// Reset the GF group data structures.
|
|
vp9_zero(twopass->gf_group);
|
|
|
|
// Is this a forced key frame by interval.
|
|
rc->this_key_frame_forced = rc->next_key_frame_forced;
|
|
|
|
// Clear the alt ref active flag as this can never be active on a key frame.
|
|
rc->source_alt_ref_active = 0;
|
|
|
|
// KF is always a GF so clear frames till next gf counter.
|
|
rc->frames_till_gf_update_due = 0;
|
|
|
|
rc->frames_to_key = 1;
|
|
|
|
twopass->kf_group_bits = 0; // Total bits available to kf group
|
|
twopass->kf_group_error_left = 0; // Group modified error score.
|
|
|
|
kf_mod_err = calculate_modified_err(twopass, oxcf, this_frame);
|
|
|
|
// Find the next keyframe.
|
|
i = 0;
|
|
while (twopass->stats_in < twopass->stats_in_end &&
|
|
rc->frames_to_key < cpi->oxcf.key_freq) {
|
|
// Accumulate kf group error.
|
|
kf_group_err += calculate_modified_err(twopass, oxcf, this_frame);
|
|
|
|
// Load the next frame's stats.
|
|
last_frame = *this_frame;
|
|
input_stats(twopass, this_frame);
|
|
|
|
// Provided that we are not at the end of the file...
|
|
if (cpi->oxcf.auto_key &&
|
|
lookup_next_frame_stats(twopass, &next_frame) != EOF) {
|
|
double loop_decay_rate;
|
|
|
|
// Check for a scene cut.
|
|
if (test_candidate_kf(twopass, &last_frame, this_frame, &next_frame))
|
|
break;
|
|
|
|
// How fast is the prediction quality decaying?
|
|
loop_decay_rate = get_prediction_decay_rate(&cpi->common, &next_frame);
|
|
|
|
// We want to know something about the recent past... rather than
|
|
// as used elsewhere where we are concerned with decay in prediction
|
|
// quality since the last GF or KF.
|
|
recent_loop_decay[i % 8] = loop_decay_rate;
|
|
decay_accumulator = 1.0;
|
|
for (j = 0; j < 8; ++j)
|
|
decay_accumulator *= recent_loop_decay[j];
|
|
|
|
// Special check for transition or high motion followed by a
|
|
// static scene.
|
|
if (detect_transition_to_still(twopass, i, cpi->oxcf.key_freq - i,
|
|
loop_decay_rate, decay_accumulator))
|
|
break;
|
|
|
|
// Step on to the next frame.
|
|
++rc->frames_to_key;
|
|
|
|
// If we don't have a real key frame within the next two
|
|
// key_freq intervals then break out of the loop.
|
|
if (rc->frames_to_key >= 2 * cpi->oxcf.key_freq)
|
|
break;
|
|
} else {
|
|
++rc->frames_to_key;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
// If there is a max kf interval set by the user we must obey it.
|
|
// We already breakout of the loop above at 2x max.
|
|
// This code centers the extra kf if the actual natural interval
|
|
// is between 1x and 2x.
|
|
if (cpi->oxcf.auto_key &&
|
|
rc->frames_to_key > cpi->oxcf.key_freq) {
|
|
FIRSTPASS_STATS tmp_frame = first_frame;
|
|
|
|
rc->frames_to_key /= 2;
|
|
|
|
// Reset to the start of the group.
|
|
reset_fpf_position(twopass, start_position);
|
|
|
|
kf_group_err = 0;
|
|
|
|
// Rescan to get the correct error data for the forced kf group.
|
|
for (i = 0; i < rc->frames_to_key; ++i) {
|
|
kf_group_err += calculate_modified_err(twopass, oxcf, &tmp_frame);
|
|
input_stats(twopass, &tmp_frame);
|
|
}
|
|
rc->next_key_frame_forced = 1;
|
|
} else if (twopass->stats_in == twopass->stats_in_end ||
|
|
rc->frames_to_key >= cpi->oxcf.key_freq) {
|
|
rc->next_key_frame_forced = 1;
|
|
} else {
|
|
rc->next_key_frame_forced = 0;
|
|
}
|
|
|
|
// Special case for the last key frame of the file.
|
|
if (twopass->stats_in >= twopass->stats_in_end) {
|
|
// Accumulate kf group error.
|
|
kf_group_err += calculate_modified_err(twopass, oxcf, this_frame);
|
|
}
|
|
|
|
// Calculate the number of bits that should be assigned to the kf group.
|
|
if (twopass->bits_left > 0 && twopass->modified_error_left > 0.0) {
|
|
// Maximum number of bits for a single normal frame (not key frame).
|
|
const int max_bits = frame_max_bits(rc, &cpi->oxcf);
|
|
|
|
// Maximum number of bits allocated to the key frame group.
|
|
int64_t max_grp_bits;
|
|
|
|
// Default allocation based on bits left and relative
|
|
// complexity of the section.
|
|
twopass->kf_group_bits = (int64_t)(twopass->bits_left *
|
|
(kf_group_err / twopass->modified_error_left));
|
|
|
|
// Clip based on maximum per frame rate defined by the user.
|
|
max_grp_bits = (int64_t)max_bits * (int64_t)rc->frames_to_key;
|
|
if (twopass->kf_group_bits > max_grp_bits)
|
|
twopass->kf_group_bits = max_grp_bits;
|
|
} else {
|
|
twopass->kf_group_bits = 0;
|
|
}
|
|
twopass->kf_group_bits = MAX(0, twopass->kf_group_bits);
|
|
|
|
// Reset the first pass file position.
|
|
reset_fpf_position(twopass, start_position);
|
|
|
|
// Scan through the kf group collating various stats used to deteermine
|
|
// how many bits to spend on it.
|
|
decay_accumulator = 1.0;
|
|
boost_score = 0.0;
|
|
for (i = 0; i < rc->frames_to_key; ++i) {
|
|
if (EOF == input_stats(twopass, &next_frame))
|
|
break;
|
|
|
|
// Monitor for static sections.
|
|
if ((next_frame.pcnt_inter - next_frame.pcnt_motion) <
|
|
zero_motion_accumulator) {
|
|
zero_motion_accumulator = (next_frame.pcnt_inter -
|
|
next_frame.pcnt_motion);
|
|
}
|
|
|
|
// For the first few frames collect data to decide kf boost.
|
|
if (i <= (rc->max_gf_interval * 2)) {
|
|
double r;
|
|
if (next_frame.intra_error > twopass->kf_intra_err_min)
|
|
r = (IIKFACTOR2 * next_frame.intra_error /
|
|
DOUBLE_DIVIDE_CHECK(next_frame.coded_error));
|
|
else
|
|
r = (IIKFACTOR2 * twopass->kf_intra_err_min /
|
|
DOUBLE_DIVIDE_CHECK(next_frame.coded_error));
|
|
|
|
if (r > RMAX)
|
|
r = RMAX;
|
|
|
|
// How fast is prediction quality decaying.
|
|
if (!detect_flash(twopass, 0)) {
|
|
const double loop_decay_rate = get_prediction_decay_rate(&cpi->common,
|
|
&next_frame);
|
|
decay_accumulator *= loop_decay_rate;
|
|
decay_accumulator = MAX(decay_accumulator, MIN_DECAY_FACTOR);
|
|
}
|
|
|
|
boost_score += (decay_accumulator * r);
|
|
}
|
|
}
|
|
|
|
reset_fpf_position(twopass, start_position);
|
|
|
|
// Store the zero motion percentage
|
|
twopass->kf_zeromotion_pct = (int)(zero_motion_accumulator * 100.0);
|
|
|
|
// Calculate a section intra ratio used in setting max loop filter.
|
|
twopass->section_intra_rating =
|
|
calculate_section_intra_ratio(start_position, twopass->stats_in_end,
|
|
rc->frames_to_key);
|
|
|
|
// Work out how many bits to allocate for the key frame itself.
|
|
rc->kf_boost = (int)boost_score;
|
|
|
|
if (rc->kf_boost < (rc->frames_to_key * 3))
|
|
rc->kf_boost = (rc->frames_to_key * 3);
|
|
if (rc->kf_boost < MIN_KF_BOOST)
|
|
rc->kf_boost = MIN_KF_BOOST;
|
|
|
|
kf_bits = calculate_boost_bits((rc->frames_to_key - 1),
|
|
rc->kf_boost, twopass->kf_group_bits);
|
|
|
|
twopass->kf_group_bits -= kf_bits;
|
|
|
|
// Save the bits to spend on the key frame.
|
|
twopass->gf_group.bit_allocation[0] = kf_bits;
|
|
twopass->gf_group.update_type[0] = KF_UPDATE;
|
|
twopass->gf_group.rf_level[0] = KF_STD;
|
|
|
|
// Note the total error score of the kf group minus the key frame itself.
|
|
twopass->kf_group_error_left = (int)(kf_group_err - kf_mod_err);
|
|
|
|
// Adjust the count of total modified error left.
|
|
// The count of bits left is adjusted elsewhere based on real coded frame
|
|
// sizes.
|
|
twopass->modified_error_left -= kf_group_err;
|
|
}
|
|
|
|
// For VBR...adjustment to the frame target based on error from previous frames
|
|
void vbr_rate_correction(int * this_frame_target,
|
|
const int64_t vbr_bits_off_target) {
|
|
int max_delta = (*this_frame_target * 15) / 100;
|
|
|
|
// vbr_bits_off_target > 0 means we have extra bits to spend
|
|
if (vbr_bits_off_target > 0) {
|
|
*this_frame_target +=
|
|
(vbr_bits_off_target > max_delta) ? max_delta
|
|
: (int)vbr_bits_off_target;
|
|
} else {
|
|
*this_frame_target -=
|
|
(vbr_bits_off_target < -max_delta) ? max_delta
|
|
: (int)-vbr_bits_off_target;
|
|
}
|
|
}
|
|
|
|
// Define the reference buffers that will be updated post encode.
|
|
void configure_buffer_updates(VP9_COMP *cpi) {
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
|
|
cpi->rc.is_src_frame_alt_ref = 0;
|
|
switch (twopass->gf_group.update_type[twopass->gf_group.index]) {
|
|
case KF_UPDATE:
|
|
cpi->refresh_last_frame = 1;
|
|
cpi->refresh_golden_frame = 1;
|
|
cpi->refresh_alt_ref_frame = 1;
|
|
break;
|
|
case LF_UPDATE:
|
|
cpi->refresh_last_frame = 1;
|
|
cpi->refresh_golden_frame = 0;
|
|
cpi->refresh_alt_ref_frame = 0;
|
|
break;
|
|
case GF_UPDATE:
|
|
cpi->refresh_last_frame = 1;
|
|
cpi->refresh_golden_frame = 1;
|
|
cpi->refresh_alt_ref_frame = 0;
|
|
break;
|
|
case OVERLAY_UPDATE:
|
|
cpi->refresh_last_frame = 0;
|
|
cpi->refresh_golden_frame = 1;
|
|
cpi->refresh_alt_ref_frame = 0;
|
|
cpi->rc.is_src_frame_alt_ref = 1;
|
|
break;
|
|
case ARF_UPDATE:
|
|
cpi->refresh_last_frame = 0;
|
|
cpi->refresh_golden_frame = 0;
|
|
cpi->refresh_alt_ref_frame = 1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
void vp9_rc_get_second_pass_params(VP9_COMP *cpi) {
|
|
VP9_COMMON *const cm = &cpi->common;
|
|
RATE_CONTROL *const rc = &cpi->rc;
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
int frames_left;
|
|
FIRSTPASS_STATS this_frame;
|
|
FIRSTPASS_STATS this_frame_copy;
|
|
|
|
int target_rate;
|
|
LAYER_CONTEXT *lc = NULL;
|
|
const int is_spatial_svc = (cpi->use_svc &&
|
|
cpi->svc.number_temporal_layers == 1);
|
|
if (is_spatial_svc) {
|
|
lc = &cpi->svc.layer_context[cpi->svc.spatial_layer_id];
|
|
frames_left = (int)(twopass->total_stats.count -
|
|
lc->current_video_frame_in_layer);
|
|
} else {
|
|
frames_left = (int)(twopass->total_stats.count -
|
|
cm->current_video_frame);
|
|
}
|
|
|
|
if (!twopass->stats_in)
|
|
return;
|
|
|
|
// If this is an arf frame then we dont want to read the stats file or
|
|
// advance the input pointer as we already have what we need.
|
|
if (twopass->gf_group.update_type[twopass->gf_group.index] == ARF_UPDATE) {
|
|
int target_rate;
|
|
configure_buffer_updates(cpi);
|
|
target_rate = twopass->gf_group.bit_allocation[twopass->gf_group.index];
|
|
target_rate = vp9_rc_clamp_pframe_target_size(cpi, target_rate);
|
|
rc->base_frame_target = target_rate;
|
|
#ifdef LONG_TERM_VBR_CORRECTION
|
|
// Correction to rate target based on prior over or under shoot.
|
|
if (cpi->oxcf.rc_mode == VPX_VBR)
|
|
vbr_rate_correction(&target_rate, rc->vbr_bits_off_target);
|
|
#endif
|
|
vp9_rc_set_frame_target(cpi, target_rate);
|
|
cm->frame_type = INTER_FRAME;
|
|
return;
|
|
}
|
|
|
|
vp9_clear_system_state();
|
|
|
|
if (is_spatial_svc && twopass->kf_intra_err_min == 0) {
|
|
twopass->kf_intra_err_min = KF_MB_INTRA_MIN * cpi->common.MBs;
|
|
twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs;
|
|
}
|
|
|
|
if (cpi->oxcf.rc_mode == VPX_Q) {
|
|
twopass->active_worst_quality = cpi->oxcf.cq_level;
|
|
} else if (cm->current_video_frame == 0 ||
|
|
(is_spatial_svc && lc->current_video_frame_in_layer == 0)) {
|
|
// Special case code for first frame.
|
|
const int section_target_bandwidth = (int)(twopass->bits_left /
|
|
frames_left);
|
|
const int tmp_q = get_twopass_worst_quality(cpi, &twopass->total_left_stats,
|
|
section_target_bandwidth);
|
|
twopass->active_worst_quality = tmp_q;
|
|
rc->ni_av_qi = tmp_q;
|
|
rc->avg_q = vp9_convert_qindex_to_q(tmp_q);
|
|
}
|
|
vp9_zero(this_frame);
|
|
if (EOF == input_stats(twopass, &this_frame))
|
|
return;
|
|
|
|
// Local copy of the current frame's first pass stats.
|
|
this_frame_copy = this_frame;
|
|
|
|
// Keyframe and section processing.
|
|
if (rc->frames_to_key == 0 ||
|
|
(cpi->frame_flags & FRAMEFLAGS_KEY)) {
|
|
// Define next KF group and assign bits to it.
|
|
find_next_key_frame(cpi, &this_frame_copy);
|
|
} else {
|
|
cm->frame_type = INTER_FRAME;
|
|
}
|
|
|
|
if (is_spatial_svc) {
|
|
if (cpi->svc.spatial_layer_id == 0) {
|
|
lc->is_key_frame = (cm->frame_type == KEY_FRAME);
|
|
} else {
|
|
cm->frame_type = INTER_FRAME;
|
|
lc->is_key_frame = cpi->svc.layer_context[0].is_key_frame;
|
|
|
|
if (lc->is_key_frame) {
|
|
cpi->ref_frame_flags &= (~VP9_LAST_FLAG);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Define a new GF/ARF group. (Should always enter here for key frames).
|
|
if (rc->frames_till_gf_update_due == 0) {
|
|
define_gf_group(cpi, &this_frame_copy);
|
|
|
|
if (twopass->gf_zeromotion_pct > 995) {
|
|
// As long as max_thresh for encode breakout is small enough, it is ok
|
|
// to enable it for show frame, i.e. set allow_encode_breakout to
|
|
// ENCODE_BREAKOUT_LIMITED.
|
|
if (!cm->show_frame)
|
|
cpi->allow_encode_breakout = ENCODE_BREAKOUT_DISABLED;
|
|
else
|
|
cpi->allow_encode_breakout = ENCODE_BREAKOUT_LIMITED;
|
|
}
|
|
|
|
rc->frames_till_gf_update_due = rc->baseline_gf_interval;
|
|
cpi->refresh_golden_frame = 1;
|
|
}
|
|
|
|
{
|
|
FIRSTPASS_STATS next_frame;
|
|
if (lookup_next_frame_stats(twopass, &next_frame) != EOF) {
|
|
twopass->next_iiratio = (int)(next_frame.intra_error /
|
|
DOUBLE_DIVIDE_CHECK(next_frame.coded_error));
|
|
}
|
|
}
|
|
|
|
configure_buffer_updates(cpi);
|
|
|
|
target_rate = twopass->gf_group.bit_allocation[twopass->gf_group.index];
|
|
if (cpi->common.frame_type == KEY_FRAME)
|
|
target_rate = vp9_rc_clamp_iframe_target_size(cpi, target_rate);
|
|
else
|
|
target_rate = vp9_rc_clamp_pframe_target_size(cpi, target_rate);
|
|
|
|
rc->base_frame_target = target_rate;
|
|
#ifdef LONG_TERM_VBR_CORRECTION
|
|
// Correction to rate target based on prior over or under shoot.
|
|
if (cpi->oxcf.rc_mode == VPX_VBR)
|
|
vbr_rate_correction(&target_rate, rc->vbr_bits_off_target);
|
|
#endif
|
|
vp9_rc_set_frame_target(cpi, target_rate);
|
|
|
|
// Update the total stats remaining structure.
|
|
subtract_stats(&twopass->total_left_stats, &this_frame);
|
|
}
|
|
|
|
void vp9_twopass_postencode_update(VP9_COMP *cpi) {
|
|
TWO_PASS *const twopass = &cpi->twopass;
|
|
RATE_CONTROL *const rc = &cpi->rc;
|
|
#ifdef LONG_TERM_VBR_CORRECTION
|
|
// In this experimental mode, the VBR correction is done exclusively through
|
|
// rc->vbr_bits_off_target. Based on the sign of this value, a limited %
|
|
// adjustment is made to the target rate of subsequent frames, to try and
|
|
// push it back towards 0. This mode is less likely to suffer from
|
|
// extreme behaviour at the end of a clip or group of frames.
|
|
const int bits_used = rc->base_frame_target;
|
|
rc->vbr_bits_off_target += rc->base_frame_target - rc->projected_frame_size;
|
|
#else
|
|
// In this mode, VBR correction is acheived by altering bits_left,
|
|
// kf_group_bits & gf_group_bits to reflect any deviation from the target
|
|
// rate in this frame. This alters the allocation of bits to the
|
|
// remaning frames in the group / clip.
|
|
//
|
|
// This method can give rise to unstable behaviour near the end of a clip
|
|
// or kf/gf group of frames where any accumulated error is corrected over an
|
|
// ever decreasing number of frames. Hence we change the balance of target
|
|
// vs. actual bitrate gradually as we progress towards the end of the
|
|
// sequence in order to mitigate this effect.
|
|
const double progress =
|
|
(double)(twopass->stats_in - twopass->stats_in_start) /
|
|
(twopass->stats_in_end - twopass->stats_in_start);
|
|
const int bits_used = (int)(progress * rc->this_frame_target +
|
|
(1.0 - progress) * rc->projected_frame_size);
|
|
#endif
|
|
|
|
twopass->bits_left = MAX(twopass->bits_left - bits_used, 0);
|
|
|
|
#ifdef LONG_TERM_VBR_CORRECTION
|
|
if (cpi->common.frame_type != KEY_FRAME &&
|
|
!vp9_is_upper_layer_key_frame(cpi)) {
|
|
#else
|
|
if (cpi->common.frame_type == KEY_FRAME ||
|
|
vp9_is_upper_layer_key_frame(cpi)) {
|
|
// For key frames kf_group_bits already had the target bits subtracted out.
|
|
// So now update to the correct value based on the actual bits used.
|
|
twopass->kf_group_bits += rc->this_frame_target - bits_used;
|
|
} else {
|
|
#endif
|
|
twopass->kf_group_bits -= bits_used;
|
|
}
|
|
twopass->kf_group_bits = MAX(twopass->kf_group_bits, 0);
|
|
|
|
// Increment the gf group index ready for the next frame.
|
|
++twopass->gf_group.index;
|
|
}
|