Merge "Revert "skip un-neccessary motion search in the first pass""
diff --git a/configure b/configure
index 800553e..9a7de73 100755
--- a/configure
+++ b/configure
@@ -272,6 +272,7 @@
alpha
multiple_arf
spatial_svc
+ denoising
"
CONFIG_LIST="
external_build
diff --git a/iosbuild.sh b/iosbuild.sh
new file mode 100755
index 0000000..0092bf2
--- /dev/null
+++ b/iosbuild.sh
@@ -0,0 +1,165 @@
+#!/bin/sh
+##
+## Copyright (c) 2014 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.
+##
+##
+## This script generates 'VPX.framework'. An iOS app can encode and decode VPx
+## video by including 'VPX.framework'.
+##
+## Run iosbuild.sh to create 'VPX.framework' in the current directory.
+##
+set -e
+devnull='> /dev/null 2>&1'
+
+BUILD_ROOT="_iosbuild"
+DIST_DIR="_dist"
+FRAMEWORK_DIR="VPX.framework"
+HEADER_DIR="${FRAMEWORK_DIR}/Headers/vpx"
+MAKE_JOBS=1
+LIBVPX_SOURCE_DIR=$(dirname "$0")
+LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
+ORIG_PWD="$(pwd)"
+TARGETS="armv6-darwin-gcc
+ armv7-darwin-gcc
+ armv7s-darwin-gcc
+ x86-iphonesimulator-gcc
+ x86_64-iphonesimulator-gcc"
+
+# This variable is set to the last dist dir used with make dist, and reused when
+# populating the framework directory to get the path to the most recent
+# includes.
+TARGET_DIST_DIR=""
+
+# List of library files passed to lipo.
+LIBS=""
+
+build_target() {
+ local target="$1"
+ local old_pwd="$(pwd)"
+
+ vlog "***Building target: ${target}***"
+
+ mkdir "${target}"
+ cd "${target}"
+ eval "../../${LIBVPX_SOURCE_DIR}/configure" --target="${target}" \
+ --disable-docs ${devnull}
+ export DIST_DIR
+ eval make -j ${MAKE_JOBS} dist ${devnull}
+ cd "${old_pwd}"
+
+ vlog "***Done building target: ${target}***"
+}
+
+build_targets() {
+ local targets="$1"
+ local target
+
+ # Clean up from previous build(s).
+ rm -rf "${BUILD_ROOT}" "${FRAMEWORK_DIR}"
+
+ # Create output dirs.
+ mkdir -p "${BUILD_ROOT}"
+ mkdir -p "${HEADER_DIR}"
+
+ cd "${BUILD_ROOT}"
+
+ for target in ${targets}; do
+ build_target "${target}"
+ TARGET_DIST_DIR="${BUILD_ROOT}/${target}/${DIST_DIR}"
+ LIBS="${LIBS} ${TARGET_DIST_DIR}/lib/libvpx.a"
+ done
+
+ cd "${ORIG_PWD}"
+}
+
+cleanup() {
+ cd "${ORIG_PWD}"
+
+ if [ "${PRESERVE_BUILD_OUTPUT}" != "yes" ]; then
+ rm -rf "${BUILD_ROOT}"
+ fi
+}
+
+iosbuild_usage() {
+cat << EOF
+ Usage: ${0##*/} [arguments]
+ --help: Display this message and exit.
+ --jobs: Number of make jobs.
+ --preserve-build-output: Do not delete the build directory.
+ --show-build-output: Show output from each library build.
+ --verbose: Output information about the environment and each stage of the
+ build.
+EOF
+}
+
+vlog() {
+ if [ "${VERBOSE}" = "yes" ]; then
+ echo "$@"
+ fi
+}
+
+trap cleanup EXIT
+
+# Parse the command line.
+while [ -n "$1" ]; do
+ case "$1" in
+ --help)
+ iosbuild_usage
+ exit
+ ;;
+ --jobs)
+ MAKE_JOBS="$2"
+ shift
+ ;;
+ --preserve-build-output)
+ PRESERVE_BUILD_OUTPUT=yes
+ ;;
+ --show-build-output)
+ devnull=
+ ;;
+ --verbose)
+ VERBOSE=yes
+ ;;
+ *)
+ iosbuild_usage
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+if [ "${VERBOSE}" = "yes" ]; then
+cat << EOF
+ BUILD_ROOT=${BUILD_ROOT}
+ DIST_DIR=${DIST_DIR}
+ FRAMEWORK_DIR=${FRAMEWORK_DIR}
+ HEADER_DIR=${HEADER_DIR}
+ MAKE_JOBS=${MAKE_JOBS}
+ PRESERVE_BUILD_OUTPUT=${PRESERVE_BUILD_OUTPUT}
+ LIBVPX_SOURCE_DIR=${LIBVPX_SOURCE_DIR}
+ LIPO=${LIPO}
+ ORIG_PWD=${ORIG_PWD}
+ TARGETS="${TARGETS}"
+EOF
+fi
+
+build_targets "${TARGETS}"
+
+# Includes are identical for all platforms, and according to dist target
+# behavior vpx_config.h and vpx_version.h aren't actually necessary for user
+# apps built with libvpx. So, just copy the includes from the last target built.
+# TODO(tomfinegan): The above is a lame excuse. Build common config/version
+# includes that use the preprocessor to include the correct file.
+cp -p "${TARGET_DIST_DIR}"/include/vpx/* "${HEADER_DIR}"
+${LIPO} -create ${LIBS} -output ${FRAMEWORK_DIR}/VPX
+
+vlog "Created fat library ${FRAMEWORK_DIR}/VPX containing:"
+for lib in ${LIBS}; do
+ vlog " $(echo ${lib} | awk -F / '{print $2, $NF}')"
+done
diff --git a/vp8/common/x86/postproc_mmx.asm b/vp8/common/x86/postproc_mmx.asm
index 8be3431..a2b1632 100644
--- a/vp8/common/x86/postproc_mmx.asm
+++ b/vp8/common/x86/postproc_mmx.asm
@@ -246,7 +246,6 @@
; unsigned char whiteclamp[16],
; unsigned char bothclamp[16],
; unsigned int Width, unsigned int Height, int Pitch)
-extern sym(rand)
global sym(vp8_plane_add_noise_mmx) PRIVATE
sym(vp8_plane_add_noise_mmx):
push rbp
@@ -258,7 +257,7 @@
; end prolog
.addnoise_loop:
- call sym(rand) WRT_PLT
+ call sym(LIBVPX_RAND) WRT_PLT
mov rcx, arg(1) ;noise
and rax, 0xff
add rcx, rax
diff --git a/vp8/common/x86/postproc_sse2.asm b/vp8/common/x86/postproc_sse2.asm
index f53daa7..fed4ee5 100644
--- a/vp8/common/x86/postproc_sse2.asm
+++ b/vp8/common/x86/postproc_sse2.asm
@@ -660,7 +660,6 @@
; unsigned char whiteclamp[16],
; unsigned char bothclamp[16],
; unsigned int Width, unsigned int Height, int Pitch)
-extern sym(rand)
global sym(vp8_plane_add_noise_wmt) PRIVATE
sym(vp8_plane_add_noise_wmt):
push rbp
@@ -672,7 +671,7 @@
; end prolog
.addnoise_loop:
- call sym(rand) WRT_PLT
+ call sym(LIBVPX_RAND) WRT_PLT
mov rcx, arg(1) ;noise
and rax, 0xff
add rcx, rax
diff --git a/vp8/common/x86/postproc_x86.c b/vp8/common/x86/postproc_x86.c
deleted file mode 100644
index 3ec0106..0000000
--- a/vp8/common/x86/postproc_x86.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2012 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.
- */
-
-/* On Android NDK, rand is inlined function, but postproc needs rand symbol */
-#if defined(__ANDROID__)
-#define rand __rand
-#include <stdlib.h>
-#undef rand
-
-extern int rand(void)
-{
- return __rand();
-}
-#else
-/* ISO C forbids an empty translation unit. */
-int vp8_unused;
-#endif
diff --git a/vp8/encoder/denoising.c b/vp8/encoder/denoising.c
index f26741f..5616bda 100644
--- a/vp8/encoder/denoising.c
+++ b/vp8/encoder/denoising.c
@@ -191,10 +191,12 @@
return FILTER_BLOCK;
}
-int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height)
+int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height,
+ int num_mb_rows, int num_mb_cols)
{
int i;
assert(denoiser);
+ denoiser->num_mb_cols = num_mb_cols;
for (i = 0; i < MAX_REF_FRAMES; i++)
{
@@ -222,6 +224,10 @@
vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0,
denoiser->yv12_mc_running_avg.frame_size);
+
+ denoiser->denoise_state = vpx_calloc((num_mb_rows * num_mb_cols), 1);
+ vpx_memset(denoiser->denoise_state, 0, (num_mb_rows * num_mb_cols));
+
return 0;
}
@@ -243,13 +249,20 @@
unsigned int best_sse,
unsigned int zero_mv_sse,
int recon_yoffset,
- int recon_uvoffset)
+ int recon_uvoffset,
+ loop_filter_info_n *lfi_n,
+ int mb_row,
+ int mb_col,
+ int block_index)
{
int mv_row;
int mv_col;
unsigned int motion_magnitude2;
unsigned int sse_thresh;
int sse_diff_thresh = 0;
+ // Spatial loop filter: only applied selectively based on
+ // temporal filter state of block relative to top/left neighbors.
+ int apply_spatial_loop_filter = 1;
MV_REFERENCE_FRAME frame = x->best_reference_frame;
MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame;
@@ -362,6 +375,8 @@
running_avg_y, avg_y_stride,
x->thismb, 16, motion_magnitude2,
x->increase_denoising);
+ denoiser->denoise_state[block_index] = motion_magnitude2 > 0 ?
+ kFilterNonZeroMV : kFilterZeroMV;
}
if (decision == COPY_BLOCK)
{
@@ -372,5 +387,59 @@
x->thismb, 16,
denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
denoiser->yv12_running_avg[INTRA_FRAME].y_stride);
+ denoiser->denoise_state[block_index] = kNoFilter;
+ }
+ // Option to selectively deblock the denoised signal.
+ if (apply_spatial_loop_filter) {
+ loop_filter_info lfi;
+ int apply_filter_col = 0;
+ int apply_filter_row = 0;
+ int apply_filter = 0;
+ int y_stride = denoiser->yv12_running_avg[INTRA_FRAME].y_stride;
+ int uv_stride =denoiser->yv12_running_avg[INTRA_FRAME].uv_stride;
+
+ // Fix filter level to some nominal value for now.
+ int filter_level = 32;
+
+ int hev_index = lfi_n->hev_thr_lut[INTER_FRAME][filter_level];
+ lfi.mblim = lfi_n->mblim[filter_level];
+ lfi.blim = lfi_n->blim[filter_level];
+ lfi.lim = lfi_n->lim[filter_level];
+ lfi.hev_thr = lfi_n->hev_thr[hev_index];
+
+ // Apply filter if there is a difference in the denoiser filter state
+ // between the current and left/top block, or if non-zero motion vector
+ // is used for the motion-compensated filtering.
+ if (mb_col > 0) {
+ apply_filter_col = !((denoiser->denoise_state[block_index] ==
+ denoiser->denoise_state[block_index - 1]) &&
+ denoiser->denoise_state[block_index] != kFilterNonZeroMV);
+ if (apply_filter_col) {
+ // Filter left vertical edge.
+ apply_filter = 1;
+ vp8_loop_filter_mbv(
+ denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
+ NULL, NULL, y_stride, uv_stride, &lfi);
+ }
+ }
+ if (mb_row > 0) {
+ apply_filter_row = !((denoiser->denoise_state[block_index] ==
+ denoiser->denoise_state[block_index - denoiser->num_mb_cols]) &&
+ denoiser->denoise_state[block_index] != kFilterNonZeroMV);
+ if (apply_filter_row) {
+ // Filter top horizontal edge.
+ apply_filter = 1;
+ vp8_loop_filter_mbh(
+ denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
+ NULL, NULL, y_stride, uv_stride, &lfi);
+ }
+ }
+ if (apply_filter) {
+ // Update the signal block |x|. Pixel changes are only to top and/or
+ // left boundary pixels: can we avoid full block copy here.
+ vp8_copy_mem16x16(
+ denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
+ y_stride, x->thismb, 16);
+ }
}
}
diff --git a/vp8/encoder/denoising.h b/vp8/encoder/denoising.h
index ae744d2..6db0785 100644
--- a/vp8/encoder/denoising.h
+++ b/vp8/encoder/denoising.h
@@ -12,6 +12,7 @@
#define VP8_ENCODER_DENOISING_H_
#include "block.h"
+#include "vp8/common/loopfilter.h"
#ifdef __cplusplus
extern "C" {
@@ -27,13 +28,22 @@
FILTER_BLOCK
};
+enum vp8_denoiser_filter_state {
+ kNoFilter,
+ kFilterZeroMV,
+ kFilterNonZeroMV
+};
+
typedef struct vp8_denoiser
{
YV12_BUFFER_CONFIG yv12_running_avg[MAX_REF_FRAMES];
YV12_BUFFER_CONFIG yv12_mc_running_avg;
+ unsigned char* denoise_state;
+ int num_mb_cols;
} VP8_DENOISER;
-int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height);
+int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height,
+ int num_mb_rows, int num_mb_cols);
void vp8_denoiser_free(VP8_DENOISER *denoiser);
@@ -42,7 +52,11 @@
unsigned int best_sse,
unsigned int zero_mv_sse,
int recon_yoffset,
- int recon_uvoffset);
+ int recon_uvoffset,
+ loop_filter_info_n *lfi_n,
+ int mb_row,
+ int mb_col,
+ int block_index);
#ifdef __cplusplus
} // extern "C"
diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c
index b550f6b..e6b0f9b 100644
--- a/vp8/encoder/encodeframe.c
+++ b/vp8/encoder/encodeframe.c
@@ -1246,7 +1246,7 @@
x->zbin_mode_boost_enabled = 0;
}
vp8_rd_pick_inter_mode(cpi, x, recon_yoffset, recon_uvoffset, &rate,
- &distortion, &intra_error);
+ &distortion, &intra_error, mb_row, mb_col);
/* switch back to the regular quantizer for the encode */
if (cpi->sf.improved_quant)
diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c
index 4d16a5f..09854a5 100644
--- a/vp8/encoder/onyx_if.c
+++ b/vp8/encoder/onyx_if.c
@@ -1751,7 +1751,8 @@
{
int width = (cpi->oxcf.Width + 15) & ~15;
int height = (cpi->oxcf.Height + 15) & ~15;
- vp8_denoiser_allocate(&cpi->denoiser, width, height);
+ vp8_denoiser_allocate(&cpi->denoiser, width, height,
+ cpi->common.mb_rows, cpi->common.mb_cols);
}
}
#endif
diff --git a/vp8/encoder/pickinter.c b/vp8/encoder/pickinter.c
index 817c9ef..3a78ee9 100644
--- a/vp8/encoder/pickinter.c
+++ b/vp8/encoder/pickinter.c
@@ -1168,6 +1168,7 @@
#if CONFIG_TEMPORAL_DENOISING
if (cpi->oxcf.noise_sensitivity)
{
+ int block_index = mb_row * cpi->common.mb_cols + mb_col;
if (x->best_sse_inter_mode == DC_PRED)
{
/* No best MV found. */
@@ -1179,7 +1180,9 @@
}
x->increase_denoising = 0;
vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse,
- recon_yoffset, recon_uvoffset);
+ recon_yoffset, recon_uvoffset,
+ &cpi->common.lf_info, mb_row, mb_col,
+ block_index);
/* Reevaluate ZEROMV after denoising. */
diff --git a/vp8/encoder/rdopt.c b/vp8/encoder/rdopt.c
index f145d09..4465b5e 100644
--- a/vp8/encoder/rdopt.c
+++ b/vp8/encoder/rdopt.c
@@ -1935,7 +1935,8 @@
void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset,
int recon_uvoffset, int *returnrate,
- int *returndistortion, int *returnintra)
+ int *returndistortion, int *returnintra,
+ int mb_row, int mb_col)
{
BLOCK *b = &x->block[0];
BLOCKD *d = &x->e_mbd.block[0];
@@ -2510,6 +2511,7 @@
#if CONFIG_TEMPORAL_DENOISING
if (cpi->oxcf.noise_sensitivity)
{
+ int block_index = mb_row * cpi->common.mb_cols + mb_col;
if (x->best_sse_inter_mode == DC_PRED)
{
/* No best MV found. */
@@ -2520,7 +2522,9 @@
best_sse = best_rd_sse;
}
vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse,
- recon_yoffset, recon_uvoffset);
+ recon_yoffset, recon_uvoffset,
+ &cpi->common.lf_info, mb_row, mb_col,
+ block_index);
/* Reevaluate ZEROMV after denoising. */
diff --git a/vp8/encoder/rdopt.h b/vp8/encoder/rdopt.h
index fe21b8e..e0da35e 100644
--- a/vp8/encoder/rdopt.h
+++ b/vp8/encoder/rdopt.h
@@ -70,7 +70,10 @@
}
extern void vp8_initialize_rd_consts(VP8_COMP *cpi, MACROBLOCK *x, int Qvalue);
-extern void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, int recon_uvoffset, int *returnrate, int *returndistortion, int *returnintra);
+extern void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x,
+ int recon_yoffset, int recon_uvoffset,
+ int *returnrate, int *returndistortion,
+ int *returnintra, int mb_row, int mb_col);
extern void vp8_rd_pick_intra_mode(MACROBLOCK *x, int *rate);
diff --git a/vp8/vp8_common.mk b/vp8/vp8_common.mk
index 0b1ac9e..6db031f 100644
--- a/vp8/vp8_common.mk
+++ b/vp8/vp8_common.mk
@@ -107,7 +107,6 @@
VP8_COMMON_SRCS-$(HAVE_SSE4_1) += common/x86/sad_sse4.asm
ifeq ($(CONFIG_POSTPROC),yes)
-VP8_COMMON_SRCS-$(ARCH_X86)$(ARCH_X86_64) += common/x86/postproc_x86.c
VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/postproc_mmx.asm
VP8_COMMON_SRCS-$(HAVE_SSE2) += common/x86/mfqe_sse2.asm
VP8_COMMON_SRCS-$(HAVE_SSE2) += common/x86/postproc_sse2.asm
diff --git a/vp9/common/x86/vp9_postproc_mmx.asm b/vp9/common/x86/vp9_postproc_mmx.asm
index c2118db..5b8deef 100644
--- a/vp9/common/x86/vp9_postproc_mmx.asm
+++ b/vp9/common/x86/vp9_postproc_mmx.asm
@@ -464,7 +464,6 @@
; unsigned char whiteclamp[16],
; unsigned char bothclamp[16],
; unsigned int width, unsigned int height, int pitch)
-extern sym(rand)
global sym(vp9_plane_add_noise_mmx) PRIVATE
sym(vp9_plane_add_noise_mmx):
push rbp
@@ -476,7 +475,7 @@
; end prolog
.addnoise_loop:
- call sym(rand) WRT_PLT
+ call sym(LIBVPX_RAND) WRT_PLT
mov rcx, arg(1) ;noise
and rax, 0xff
add rcx, rax
diff --git a/vp9/common/x86/vp9_postproc_sse2.asm b/vp9/common/x86/vp9_postproc_sse2.asm
index 858fc99..ec8bfdb 100644
--- a/vp9/common/x86/vp9_postproc_sse2.asm
+++ b/vp9/common/x86/vp9_postproc_sse2.asm
@@ -629,7 +629,6 @@
; unsigned char whiteclamp[16],
; unsigned char bothclamp[16],
; unsigned int width, unsigned int height, int pitch)
-extern sym(rand)
global sym(vp9_plane_add_noise_wmt) PRIVATE
sym(vp9_plane_add_noise_wmt):
push rbp
@@ -641,7 +640,7 @@
; end prolog
.addnoise_loop:
- call sym(rand) WRT_PLT
+ call sym(LIBVPX_RAND) WRT_PLT
mov rcx, arg(1) ;noise
and rax, 0xff
add rcx, rax
diff --git a/vp9/encoder/vp9_denoiser.c b/vp9/encoder/vp9_denoiser.c
new file mode 100644
index 0000000..687b4c2
--- /dev/null
+++ b/vp9/encoder/vp9_denoiser.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 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 <stdio.h>
+#include <stdint.h>
+#include "vp9/encoder/vp9_denoiser.h"
+#include "vpx_scale/yv12config.h"
+
+static const int widths[] = {4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64};
+static const int heights[] = {4, 8, 4, 8, 16, 8, 16, 32, 16, 32, 64, 32, 64};
+
+int vp9_denoiser_filter() {
+ return 0;
+}
+
+void vp9_denoiser_denoise(VP9_DENOISER *denoiser,
+ MACROBLOCK *mb, MODE_INFO **grid,
+ int mi_row, int mi_col, BLOCK_SIZE bs) {
+ return;
+}
+
+void vp9_denoiser_update_frame_info(VP9_DENOISER *denoiser,
+ FRAME_TYPE frame_type,
+ int refresh_alt_ref_frame,
+ int refresh_golden_frame,
+ int refresh_last_frame) {
+ return;
+}
+
+void vp9_denoiser_update_frame_stats() {
+ return;
+}
+
+int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height,
+ int border) {
+ return 0;
+}
+
+void vp9_denoiser_free(VP9_DENOISER *denoiser) {
+ return;
+}
+
diff --git a/vp9/encoder/vp9_denoiser.h b/vp9/encoder/vp9_denoiser.h
new file mode 100644
index 0000000..a7a8d93
--- /dev/null
+++ b/vp9/encoder/vp9_denoiser.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+#ifndef VP9_ENCODER_DENOISER_H_
+#define VP9_ENCODER_DENOISER_H_
+
+#include "vp9/encoder/vp9_block.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum vp9_denoiser_decision {
+ COPY_BLOCK,
+ FILTER_BLOCK
+};
+
+typedef struct vp9_denoiser {
+ struct buf_2d running_avg_y;
+ struct buf_2d mc_running_avg_y;
+} VP9_DENOISER;
+
+void vp9_denoiser_update_frame_info(VP9_DENOISER *denoiser,
+ FRAME_TYPE frame_type,
+ int refresh_alt_ref_frame,
+ int refresh_golden_frame,
+ int refresh_last_frame);
+
+void vp9_denoiser_denoise(VP9_DENOISER *denoiser,
+ MACROBLOCK *mb, MODE_INFO **grid,
+ int mi_row, int mi_col, BLOCK_SIZE bs);
+
+void vp9_denoiser_update_frame_stats();
+
+int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height,
+ int border);
+
+void vp9_denoiser_free(VP9_DENOISER *denoiser);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // VP9_ENCODER_DENOISER_H_
diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c
index 93081ad..525eccd 100644
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -538,7 +538,7 @@
cpi->svc.number_temporal_layers = oxcf->ts_number_layers;
if ((cpi->svc.number_temporal_layers > 1 &&
- cpi->oxcf.rc_mode == RC_MODE_CBR) ||
+ cpi->oxcf.rc_mode == VPX_CBR) ||
(cpi->svc.number_spatial_layers > 1 &&
cpi->oxcf.mode == TWO_PASS_SECOND_BEST)) {
vp9_init_layer_context(cpi);
@@ -609,7 +609,7 @@
cpi->encode_breakout = cpi->oxcf.encode_breakout;
// local file playback mode == really big buffer
- if (cpi->oxcf.rc_mode == RC_MODE_VBR) {
+ if (cpi->oxcf.rc_mode == VPX_VBR) {
cpi->oxcf.starting_buffer_level_ms = 60000;
cpi->oxcf.optimal_buffer_level_ms = 60000;
cpi->oxcf.maximum_buffer_size_ms = 240000;
@@ -657,7 +657,7 @@
update_frame_size(cpi);
if ((cpi->svc.number_temporal_layers > 1 &&
- cpi->oxcf.rc_mode == RC_MODE_CBR) ||
+ cpi->oxcf.rc_mode == VPX_CBR) ||
(cpi->svc.number_spatial_layers > 1 && cpi->pass == 2)) {
vp9_update_layer_context_change_config(cpi,
(int)cpi->oxcf.target_bandwidth);
@@ -680,6 +680,11 @@
cpi->ext_refresh_frame_flags_pending = 0;
cpi->ext_refresh_frame_context_pending = 0;
+
+#if CONFIG_DENOISING
+ vp9_denoiser_alloc(&(cpi->denoiser), cm->width, cm->height,
+ VP9_ENC_BORDER_IN_PIXELS);
+#endif
}
#ifndef M_LOG2_E
@@ -1085,6 +1090,10 @@
#endif
}
+#if CONFIG_DENOISING
+ vp9_denoiser_free(&(cpi->denoiser));
+#endif
+
dealloc_compressor_data(cpi);
vpx_free(cpi->tok);
@@ -1478,7 +1487,7 @@
if ((rc->projected_frame_size > high_limit && q < maxq) ||
(rc->projected_frame_size < low_limit && q > minq)) {
force_recode = 1;
- } else if (cpi->oxcf.rc_mode == RC_MODE_CONSTRAINED_QUALITY) {
+ } else if (cpi->oxcf.rc_mode == VPX_CQ) {
// Deal with frame undershoot and whether or not we are
// below the automatically set cq level.
if (q > oxcf->cq_level &&
@@ -1547,6 +1556,13 @@
ref_cnt_fb(cm->frame_bufs,
&cm->ref_frame_map[cpi->lst_fb_idx], cm->new_fb_idx);
}
+#if CONFIG_DENOISING
+ vp9_denoiser_update_frame_info(&cpi->denoiser,
+ cpi->common.frame_type,
+ cpi->refresh_alt_ref_frame,
+ cpi->refresh_golden_frame,
+ cpi->refresh_last_frame);
+#endif
}
static void loopfilter_frame(VP9_COMP *cpi, VP9_COMMON *cm) {
@@ -1773,7 +1789,7 @@
frame_over_shoot_limit = 1;
}
- if (cpi->oxcf.rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (cpi->oxcf.rc_mode == VPX_Q) {
loop = 0;
} else {
if ((cm->frame_type == KEY_FRAME) &&
@@ -1871,7 +1887,7 @@
// This should only trigger where there is very substantial
// undershoot on a frame and the auto cq level is above
// the user passsed in value.
- if (cpi->oxcf.rc_mode == RC_MODE_CONSTRAINED_QUALITY &&
+ if (cpi->oxcf.rc_mode == VPX_CQ &&
q < q_low) {
q_low = q;
}
@@ -2069,7 +2085,7 @@
// For 1 pass CBR, check if we are dropping this frame.
// Never drop on key frame.
if (cpi->pass == 0 &&
- cpi->oxcf.rc_mode == RC_MODE_CBR &&
+ cpi->oxcf.rc_mode == VPX_CBR &&
cm->frame_type != KEY_FRAME) {
if (vp9_rc_drop_frame(cpi)) {
vp9_rc_postencode_update_drop_frame(cpi);
@@ -2265,7 +2281,7 @@
static void Pass0Encode(VP9_COMP *cpi, size_t *size, uint8_t *dest,
unsigned int *frame_flags) {
- if (cpi->oxcf.rc_mode == RC_MODE_CBR) {
+ if (cpi->oxcf.rc_mode == VPX_CBR) {
vp9_rc_get_one_pass_cbr_params(cpi);
} else {
vp9_rc_get_one_pass_vbr_params(cpi);
@@ -2545,7 +2561,7 @@
}
if (cpi->svc.number_temporal_layers > 1 &&
- cpi->oxcf.rc_mode == RC_MODE_CBR) {
+ cpi->oxcf.rc_mode == VPX_CBR) {
vp9_update_temporal_layer_framerate(cpi);
vp9_restore_layer_context(cpi);
}
@@ -2578,7 +2594,7 @@
if (cpi->pass == 2 &&
cm->current_video_frame == 0 &&
cpi->oxcf.allow_spatial_resampling &&
- cpi->oxcf.rc_mode == RC_MODE_VBR) {
+ cpi->oxcf.rc_mode == VPX_VBR) {
// Internal scaling is triggered on the first frame.
vp9_set_size_literal(cpi, cpi->oxcf.scaled_frame_width,
cpi->oxcf.scaled_frame_height);
@@ -2640,7 +2656,7 @@
// Save layer specific state.
if ((cpi->svc.number_temporal_layers > 1 &&
- cpi->oxcf.rc_mode == RC_MODE_CBR) ||
+ cpi->oxcf.rc_mode == VPX_CBR) ||
(cpi->svc.number_spatial_layers > 1 && cpi->pass == 2)) {
vp9_save_layer_context(cpi);
}
diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h
index 6b0e228..a27868a 100644
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -36,6 +36,9 @@
#include "vp9/encoder/vp9_svc_layercontext.h"
#include "vp9/encoder/vp9_tokenize.h"
#include "vp9/encoder/vp9_variance.h"
+#if CONFIG_DENOISING
+#include "vp9/encoder/vp9_denoiser.h"
+#endif
#ifdef __cplusplus
extern "C" {
@@ -131,13 +134,6 @@
} VPX_SCALING;
typedef enum {
- RC_MODE_VBR = 0,
- RC_MODE_CBR = 1,
- RC_MODE_CONSTRAINED_QUALITY = 2,
- RC_MODE_CONSTANT_QUALITY = 3,
-} RC_MODE;
-
-typedef enum {
// Good Quality Fast Encoding. The encoder balances quality with the
// amount of time it takes to encode the output. (speed setting
// controls how fast)
@@ -209,7 +205,8 @@
// ----------------------------------------------------------------
// DATARATE CONTROL OPTIONS
- RC_MODE rc_mode; // vbr, cbr, constrained quality or constant quality
+ // vbr, cbr, constrained quality or constant quality
+ enum vpx_rc_mode rc_mode;
// buffer targeting aggressiveness
int under_shoot_pct;
@@ -526,6 +523,10 @@
int this_frame_weight;
int max_arf_level;
#endif
+
+#if CONFIG_DENOISING
+ VP9_DENOISER denoiser;
+#endif
} VP9_COMP;
void vp9_initialize_enc();
diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c
index dfe74b3..7cadb36 100644
--- a/vp9/encoder/vp9_firstpass.c
+++ b/vp9/encoder/vp9_firstpass.c
@@ -904,7 +904,7 @@
}
// Restriction on active max q for constrained quality mode.
- if (cpi->oxcf.rc_mode == RC_MODE_CONSTRAINED_QUALITY)
+ if (cpi->oxcf.rc_mode == VPX_CQ)
q = MAX(q, oxcf->cq_level);
return q;
}
@@ -2126,7 +2126,7 @@
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 == RC_MODE_VBR)
+ 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);
@@ -2141,7 +2141,7 @@
twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs;
}
- if (cpi->oxcf.rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ 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)) {
@@ -2226,7 +2226,7 @@
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 == RC_MODE_VBR)
+ 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);
diff --git a/vp9/encoder/vp9_pickmode.c b/vp9/encoder/vp9_pickmode.c
index cdfb31c..80966ad 100644
--- a/vp9/encoder/vp9_pickmode.c
+++ b/vp9/encoder/vp9_pickmode.c
@@ -438,6 +438,10 @@
}
}
+#if CONFIG_DENOISING
+ vp9_denoiser_update_frame_stats();
+#endif
+
if (this_rd < best_rd || x->skip) {
best_rd = this_rd;
*returnrate = rate;
@@ -453,6 +457,7 @@
}
}
+
mbmi->mode = best_mode;
mbmi->interp_filter = best_pred_filter;
mbmi->ref_frame[0] = best_ref_frame;
@@ -488,6 +493,10 @@
}
}
}
+#if CONFIG_DENOISING
+ vp9_denoiser_denoise(&cpi->denoiser, x, cpi->common.mi_grid_visible, mi_row,
+ mi_col, bsize);
+#endif
return INT64_MAX;
}
diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c
index 143c23b..f775003 100644
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -180,13 +180,13 @@
rc->bits_off_target = MIN(rc->bits_off_target, rc->maximum_buffer_size);
rc->buffer_level = rc->bits_off_target;
- if (cpi->use_svc && cpi->oxcf.rc_mode == RC_MODE_CBR) {
+ if (cpi->use_svc && cpi->oxcf.rc_mode == VPX_CBR) {
update_layer_buffer_level(&cpi->svc, encoded_frame_size);
}
}
void vp9_rc_init(const VP9EncoderConfig *oxcf, int pass, RATE_CONTROL *rc) {
- if (pass == 0 && oxcf->rc_mode == RC_MODE_CBR) {
+ if (pass == 0 && oxcf->rc_mode == VPX_CBR) {
rc->avg_frame_qindex[KEY_FRAME] = oxcf->worst_allowed_q;
rc->avg_frame_qindex[INTER_FRAME] = oxcf->worst_allowed_q;
} else {
@@ -276,7 +276,7 @@
} else {
if ((cpi->refresh_alt_ref_frame || cpi->refresh_golden_frame) &&
!cpi->rc.is_src_frame_alt_ref &&
- !(cpi->use_svc && cpi->oxcf.rc_mode == RC_MODE_CBR))
+ !(cpi->use_svc && cpi->oxcf.rc_mode == VPX_CBR))
return cpi->rc.gf_rate_correction_factor;
else
return cpi->rc.rate_correction_factor;
@@ -289,7 +289,7 @@
} else {
if ((cpi->refresh_alt_ref_frame || cpi->refresh_golden_frame) &&
!cpi->rc.is_src_frame_alt_ref &&
- !(cpi->use_svc && cpi->oxcf.rc_mode == RC_MODE_CBR))
+ !(cpi->use_svc && cpi->oxcf.rc_mode == VPX_CBR))
cpi->rc.gf_rate_correction_factor = factor;
else
cpi->rc.rate_correction_factor = factor;
@@ -605,7 +605,7 @@
const VP9EncoderConfig *const oxcf) {
static const double cq_adjust_threshold = 0.5;
int active_cq_level = oxcf->cq_level;
- if (oxcf->rc_mode == RC_MODE_CONSTRAINED_QUALITY &&
+ if (oxcf->rc_mode == VPX_CQ &&
rc->total_target_bits > 0) {
const double x = (double)rc->total_actual_bits / rc->total_target_bits;
if (x < cq_adjust_threshold) {
@@ -679,7 +679,7 @@
q = rc->avg_frame_qindex[KEY_FRAME];
}
// For constrained quality dont allow Q less than the cq level
- if (oxcf->rc_mode == RC_MODE_CONSTRAINED_QUALITY) {
+ if (oxcf->rc_mode == VPX_CQ) {
if (q < cq_level)
q = cq_level;
@@ -691,7 +691,7 @@
// Constrained quality use slightly lower active best.
active_best_quality = active_best_quality * 15 / 16;
- } else if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ } else if (oxcf->rc_mode == VPX_Q) {
if (!cpi->refresh_alt_ref_frame) {
active_best_quality = cq_level;
} else {
@@ -705,7 +705,7 @@
arfgf_low_motion_minq, arfgf_high_motion_minq);
}
} else {
- if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (oxcf->rc_mode == VPX_Q) {
active_best_quality = cq_level;
} else {
// Use the lower of active_worst_quality and recent/average Q.
@@ -715,7 +715,7 @@
active_best_quality = inter_minq[rc->avg_frame_qindex[KEY_FRAME]];
// For the constrained quality mode we don't want
// q to fall below the cq level.
- if ((oxcf->rc_mode == RC_MODE_CONSTRAINED_QUALITY) &&
+ if ((oxcf->rc_mode == VPX_CQ) &&
(active_best_quality < cq_level)) {
active_best_quality = cq_level;
}
@@ -752,7 +752,7 @@
}
#endif
- if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (oxcf->rc_mode == VPX_Q) {
q = active_best_quality;
// Special case code to try and match quality with forced key frames
} else if ((cm->frame_type == KEY_FRAME) && rc->this_key_frame_forced) {
@@ -771,7 +771,7 @@
#if CONFIG_MULTIPLE_ARF
// Force the quantizer determined by the coding order pattern.
if (cpi->multi_arf_enabled && (cm->frame_type != KEY_FRAME) &&
- cpi->oxcf.rc_mode != RC_MODE_CONSTANT_QUALITY) {
+ cpi->oxcf.rc_mode != VPX_Q) {
double new_q;
double current_q = vp9_convert_qindex_to_q(active_worst_quality);
int level = cpi->this_frame_weight;
@@ -859,7 +859,7 @@
q = active_worst_quality;
}
// For constrained quality dont allow Q less than the cq level
- if (oxcf->rc_mode == RC_MODE_CONSTRAINED_QUALITY) {
+ if (oxcf->rc_mode == VPX_CQ) {
if (q < cq_level)
q = cq_level;
@@ -871,7 +871,7 @@
// Constrained quality use slightly lower active best.
active_best_quality = active_best_quality * 15 / 16;
- } else if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ } else if (oxcf->rc_mode == VPX_Q) {
if (!cpi->refresh_alt_ref_frame) {
active_best_quality = cq_level;
} else {
@@ -885,14 +885,14 @@
arfgf_low_motion_minq, arfgf_high_motion_minq);
}
} else {
- if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (oxcf->rc_mode == VPX_Q) {
active_best_quality = cq_level;
} else {
active_best_quality = inter_minq[active_worst_quality];
// For the constrained quality mode we don't want
// q to fall below the cq level.
- if ((oxcf->rc_mode == RC_MODE_CONSTRAINED_QUALITY) &&
+ if ((oxcf->rc_mode == VPX_CQ) &&
(active_best_quality < cq_level)) {
active_best_quality = cq_level;
}
@@ -919,7 +919,7 @@
qdelta = vp9_compute_qdelta_by_rate(&cpi->rc, cm->frame_type,
active_worst_quality, 2.0);
} else if (!rc->is_src_frame_alt_ref &&
- (oxcf->rc_mode != RC_MODE_CBR) &&
+ (oxcf->rc_mode != VPX_CBR) &&
(cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame)) {
qdelta = vp9_compute_qdelta_by_rate(&cpi->rc, cm->frame_type,
active_worst_quality, 1.75);
@@ -929,7 +929,7 @@
}
#endif
- if (oxcf->rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (oxcf->rc_mode == VPX_Q) {
q = active_best_quality;
// Special case code to try and match quality with forced key frames.
} else if ((cm->frame_type == KEY_FRAME) && rc->this_key_frame_forced) {
@@ -948,7 +948,7 @@
#if CONFIG_MULTIPLE_ARF
// Force the quantizer determined by the coding order pattern.
if (cpi->multi_arf_enabled && (cm->frame_type != KEY_FRAME) &&
- cpi->oxcf.rc_mode != RC_MODE_CONSTANT_QUALITY) {
+ cpi->oxcf.rc_mode != VPX_Q) {
double new_q;
double current_q = vp9_convert_qindex_to_q(active_worst_quality);
int level = cpi->this_frame_weight;
@@ -974,7 +974,7 @@
int *bottom_index, int *top_index) {
int q;
if (cpi->pass == 0) {
- if (cpi->oxcf.rc_mode == RC_MODE_CBR)
+ if (cpi->oxcf.rc_mode == VPX_CBR)
q = rc_pick_q_and_bounds_one_pass_cbr(cpi, bottom_index, top_index);
else
q = rc_pick_q_and_bounds_one_pass_vbr(cpi, bottom_index, top_index);
@@ -997,7 +997,7 @@
int frame_target,
int *frame_under_shoot_limit,
int *frame_over_shoot_limit) {
- if (cpi->oxcf.rc_mode == RC_MODE_CONSTANT_QUALITY) {
+ if (cpi->oxcf.rc_mode == VPX_Q) {
*frame_under_shoot_limit = 0;
*frame_over_shoot_limit = INT_MAX;
} else {
@@ -1072,7 +1072,7 @@
// Post encode loop adjustment of Q prediction.
vp9_rc_update_rate_correction_factors(
cpi, (cpi->sf.recode_loop >= ALLOW_RECODE_KFARFGF ||
- oxcf->rc_mode == RC_MODE_CBR) ? 2 : 0);
+ oxcf->rc_mode == VPX_CBR) ? 2 : 0);
// Keep a record of last Q and ambient average Q.
if (cm->frame_type == KEY_FRAME) {
@@ -1082,7 +1082,7 @@
} else {
if (rc->is_src_frame_alt_ref ||
!(cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame) ||
- (cpi->use_svc && oxcf->rc_mode == RC_MODE_CBR)) {
+ (cpi->use_svc && oxcf->rc_mode == VPX_CBR)) {
rc->last_q[INTER_FRAME] = qindex;
rc->avg_frame_qindex[INTER_FRAME] =
ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[INTER_FRAME] + qindex, 2);
@@ -1225,7 +1225,7 @@
int min_frame_target = MAX(rc->avg_frame_bandwidth >> 4, FRAME_OVERHEAD_BITS);
int target = rc->avg_frame_bandwidth;
if (svc->number_temporal_layers > 1 &&
- oxcf->rc_mode == RC_MODE_CBR) {
+ oxcf->rc_mode == VPX_CBR) {
// Note that for layers, avg_frame_bandwidth is the cumulative
// per-frame-bandwidth. For the target size of this frame, use the
// layer average frame size (i.e., non-cumulative per-frame-bw).
@@ -1258,7 +1258,7 @@
int kf_boost = 32;
double framerate = oxcf->framerate;
if (svc->number_temporal_layers > 1 &&
- oxcf->rc_mode == RC_MODE_CBR) {
+ oxcf->rc_mode == VPX_CBR) {
// Use the layer framerate for temporal layers CBR mode.
const LAYER_CONTEXT *lc = &svc->layer_context[svc->temporal_layer_id];
framerate = lc->framerate;
@@ -1288,7 +1288,7 @@
cpi->svc.layer_context[cpi->svc.spatial_layer_id].is_key_frame = 1;
}
- if (cpi->pass == 0 && cpi->oxcf.rc_mode == RC_MODE_CBR) {
+ if (cpi->pass == 0 && cpi->oxcf.rc_mode == VPX_CBR) {
target = calc_iframe_target_size_one_pass_cbr(cpi);
}
} else {
@@ -1303,7 +1303,7 @@
}
}
- if (cpi->pass == 0 && cpi->oxcf.rc_mode == RC_MODE_CBR) {
+ if (cpi->pass == 0 && cpi->oxcf.rc_mode == VPX_CBR) {
target = calc_pframe_target_size_one_pass_cbr(cpi);
}
}
diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c
index 72768e1..edd59ab 100644
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -318,14 +318,7 @@
oxcf->lag_in_frames = cfg->g_pass == VPX_RC_FIRST_PASS ? 0
: cfg->g_lag_in_frames;
-
- oxcf->rc_mode = RC_MODE_VBR;
- if (cfg->rc_end_usage == VPX_CQ)
- oxcf->rc_mode = RC_MODE_CONSTRAINED_QUALITY;
- else if (cfg->rc_end_usage == VPX_Q)
- oxcf->rc_mode = RC_MODE_CONSTANT_QUALITY;
- else if (cfg->rc_end_usage == VPX_CBR)
- oxcf->rc_mode = RC_MODE_CBR;
+ oxcf->rc_mode = cfg->rc_end_usage;
// Convert target bandwidth from Kbit/s to Bit/s
oxcf->target_bandwidth = 1000 * cfg->rc_target_bitrate;
diff --git a/vp9/vp9cx.mk b/vp9/vp9cx.mk
index a44ffc1..9dbb678 100644
--- a/vp9/vp9cx.mk
+++ b/vp9/vp9cx.mk
@@ -23,6 +23,8 @@
VP9_CX_SRCS-yes += encoder/vp9_cost.h
VP9_CX_SRCS-yes += encoder/vp9_cost.c
VP9_CX_SRCS-yes += encoder/vp9_dct.c
+VP9_CX_SRCS-$(CONFIG_DENOISING) += encoder/vp9_denoiser.c
+VP9_CX_SRCS-$(CONFIG_DENOISING) += encoder/vp9_denoiser.h
VP9_CX_SRCS-yes += encoder/vp9_encodeframe.c
VP9_CX_SRCS-yes += encoder/vp9_encodeframe.h
VP9_CX_SRCS-yes += encoder/vp9_encodemb.c
diff --git a/vpx_ports/x86_abi_support.asm b/vpx_ports/x86_abi_support.asm
index eccbfa3..3814ef4 100644
--- a/vpx_ports/x86_abi_support.asm
+++ b/vpx_ports/x86_abi_support.asm
@@ -393,3 +393,14 @@
section .text
%endif
+; On Android platforms use lrand48 when building postproc routines. Prior to L
+; rand() was not available.
+%if CONFIG_POSTPROC=1
+%ifdef __ANDROID__
+extern sym(lrand48)
+%define LIBVPX_RAND lrand48
+%else
+extern sym(rand)
+%define LIBVPX_RAND rand
+%endif
+%endif ; CONFIG_POSTPROC