diff options
33 files changed, 1314 insertions, 541 deletions
diff --git a/build/make/Makefile b/build/make/Makefile index 0c5ff64f4..dd7fb4a21 100644 --- a/build/make/Makefile +++ b/build/make/Makefile @@ -147,15 +147,6 @@ $(BUILD_PFX)%.cc.o: %.cc $(if $(quiet),@echo " [CXX] $@") $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -c -o $@ $< -$(BUILD_PFX)%.cpp.d: %.cpp - $(if $(quiet),@echo " [DEP] $@") - $(qexec)mkdir -p $(dir $@) - $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -M $< | $(fmt_deps) > $@ - -$(BUILD_PFX)%.cpp.o: %.cpp - $(if $(quiet),@echo " [CXX] $@") - $(qexec)$(CXX) $(INTERNAL_CFLAGS) $(CXXFLAGS) -c -o $@ $< - $(BUILD_PFX)%.asm.d: %.asm $(if $(quiet),@echo " [DEP] $@") $(qexec)mkdir -p $(dir $@) @@ -227,7 +218,7 @@ cond_enabled=$(if $(filter yes,$($(1))), $(call enabled,$(2))) find_file1=$(word 1,$(wildcard $(subst //,/,$(addsuffix /$(1),$(2))))) find_file=$(foreach f,$(1),$(call find_file1,$(strip $(f)),$(strip $(2))) ) -obj_pats=.c=.c.o $(AS_SFX)=$(AS_SFX).o .cc=.cc.o .cpp=.cpp.o +obj_pats=.c=.c.o $(AS_SFX)=$(AS_SFX).o .cc=.cc.o objs=$(addprefix $(BUILD_PFX),$(foreach p,$(obj_pats),$(filter %.o,$(1:$(p))) )) install_map_templates=$(eval $(call install_map_template,$(1),$(2))) diff --git a/build/make/gen_msvs_proj.sh b/build/make/gen_msvs_proj.sh index 5936370a7..df9143595 100755 --- a/build/make/gen_msvs_proj.sh +++ b/build/make/gen_msvs_proj.sh @@ -162,7 +162,8 @@ generate_filter() { done done fi - if [ "$pat" == "c" ] || [ "$pat" == "cc" ] ; then + if [ "$pat" == "c" ] || \ + [ "$pat" == "cc" ] || [ "$pat" == "cpp" ]; then for plat in "${platforms[@]}"; do for cfg in Debug Release; do open_tag FileConfiguration \ @@ -561,7 +562,7 @@ generate_vcproj() { close_tag Configurations open_tag Files - generate_filter srcs "Source Files" "c;cc;def;odl;idl;hpj;bat;asm;asmx" + generate_filter srcs "Source Files" "c;cc;cpp;def;odl;idl;hpj;bat;asm;asmx" generate_filter hdrs "Header Files" "h;hm;inl;inc;xsd" generate_filter resrcs "Resource Files" "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" generate_filter resrcs "Build Files" "mk" diff --git a/build/make/gen_msvs_vcxproj.sh b/build/make/gen_msvs_vcxproj.sh index 7c8871ba2..23990a413 100755 --- a/build/make/gen_msvs_vcxproj.sh +++ b/build/make/gen_msvs_vcxproj.sh @@ -174,7 +174,8 @@ generate_filter() { done done close_tag CustomBuild - elif [ "$pat" == "c" ] || [ "$pat" == "cc" ] ; then + elif [ "$pat" == "c" ] || \ + [ "$pat" == "cc" ] || [ "$pat" == "cpp" ]; then open_tag ClCompile \ Include=".\\$f" # Separate file names with Condition? @@ -524,7 +525,7 @@ generate_vcxproj() { done open_tag ItemGroup - generate_filter "Source Files" "c;cc;def;odl;idl;hpj;bat;asm;asmx;s" + generate_filter "Source Files" "c;cc;cpp;def;odl;idl;hpj;bat;asm;asmx;s" close_tag ItemGroup open_tag ItemGroup generate_filter "Header Files" "h;hm;inl;inc;xsd" @@ -704,11 +704,13 @@ process_toolchain() { enabled postproc || die "postproc_visualizer requires postproc to be enabled" fi + # Enable WebM IO by default. + soft_enable webm_io + # Enable unit tests by default if we have a working C++ compiler. case "$toolchain" in *-vs*) soft_enable unit_tests - soft_enable webm_io ;; *-android-*) # GTestLog must be modified to use Android logging utilities. @@ -724,21 +726,13 @@ process_toolchain() { check_cxx "$@" <<EOF && soft_enable unit_tests int z; EOF - check_cxx "$@" <<EOF && soft_enable webm_io -int z; -EOF ;; *) enabled pthread_h && check_cxx "$@" <<EOF && soft_enable unit_tests int z; EOF - check_cxx "$@" <<EOF && soft_enable webm_io -int z; -EOF ;; esac - # libwebm needs to be linked with C++ standard library - enabled webm_io && LD=${CXX} } diff --git a/examples.mk b/examples.mk index f091c2de4..fa5d66cda 100644 --- a/examples.mk +++ b/examples.mk @@ -15,16 +15,6 @@ LIBYUV_SRCS += third_party/libyuv/include/libyuv/basic_types.h \ third_party/libyuv/source/scale.c \ third_party/libyuv/source/cpu_id.c -LIBWEBM_MUXER_SRCS += third_party/libwebm/mkvmuxer.cpp \ - third_party/libwebm/mkvmuxerutil.cpp \ - third_party/libwebm/mkvwriter.cpp \ - third_party/libwebm/mkvmuxer.hpp \ - third_party/libwebm/mkvmuxertypes.hpp \ - third_party/libwebm/mkvmuxerutil.hpp \ - third_party/libwebm/mkvparser.hpp \ - third_party/libwebm/mkvwriter.hpp \ - third_party/libwebm/webmids.hpp - # List of examples to build. UTILS are tools meant for distribution # while EXAMPLES demonstrate specific portions of the API. UTILS-$(CONFIG_DECODERS) += vpxdec.c @@ -63,8 +53,10 @@ vpxenc.SRCS += vpx_ports/vpx_timer.h vpxenc.SRCS += vpxstats.c vpxstats.h vpxenc.SRCS += $(LIBYUV_SRCS) ifeq ($(CONFIG_WEBM_IO),yes) - vpxenc.SRCS += $(LIBWEBM_MUXER_SRCS) - vpxenc.SRCS += webmenc.cc webmenc.h + vpxenc.SRCS += third_party/libmkv/EbmlIDs.h + vpxenc.SRCS += third_party/libmkv/EbmlWriter.c + vpxenc.SRCS += third_party/libmkv/EbmlWriter.h + vpxenc.SRCS += webmenc.c webmenc.h endif vpxenc.GUID = 548DEC74-7A15-4B2B-AFC3-AA102E7C25C1 vpxenc.DESCRIPTION = Full featured encoder @@ -78,7 +70,7 @@ vp9_spatial_scalable_encoder.SRCS += vpxstats.c vpxstats.h vp9_spatial_scalable_encoder.GUID = 4A38598D-627D-4505-9C7B-D4020C84100D vp9_spatial_scalable_encoder.DESCRIPTION = Spatial Scalable Encoder -ifeq ($(CONFIG_SHARED),no) +ifneq ($(CONFIG_SHARED),yes) EXAMPLES-$(CONFIG_VP9_ENCODER) += resize_util.c endif diff --git a/examples/vpx_temporal_scalable_patterns.c b/examples/vpx_temporal_scalable_patterns.c index 3a4f05b92..5cb4ee9cf 100644 --- a/examples/vpx_temporal_scalable_patterns.c +++ b/examples/vpx_temporal_scalable_patterns.c @@ -575,6 +575,7 @@ int main(int argc, char **argv) { } else if (strncmp(encoder->name, "vp9", 3) == 0) { vpx_codec_control(&codec, VP8E_SET_CPUUSED, speed); vpx_codec_control(&codec, VP9E_SET_AQ_MODE, 3); + vpx_codec_control(&codec, VP9E_SET_FRAME_PERIODIC_BOOST, 0); vpx_codec_control(&codec, VP8E_SET_NOISE_SENSITIVITY, 0); if (vpx_codec_control(&codec, VP9E_SET_SVC, 1)) { die_codec(&codec, "Failed to set SVC"); diff --git a/test/tools_common.sh b/test/tools_common.sh new file mode 100755 index 000000000..cd7977156 --- /dev/null +++ b/test/tools_common.sh @@ -0,0 +1,437 @@ +#!/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 file contains shell code shared by test scripts for libvpx tools. +set -e + +# Sets $VPX_TOOL_TEST to the name specified by positional parameter one. +test_begin() { + VPX_TOOL_TEST="${1}" +} + +# Clears the VPX_TOOL_TEST variable after confirming that $VPX_TOOL_TEST matches +# positional parameter one. +test_end() { + if [ "$1" != "${VPX_TOOL_TEST}" ]; then + echo "FAIL completed test mismatch!." + echo " completed test: ${1}" + echo " active test: ${VPX_TOOL_TEST}." + return 1 + fi + VPX_TOOL_TEST='<unset>' +} + +# Echoes the target configuration being tested. +test_configuration_target() { + vpx_config_mk="${LIBVPX_CONFIG_PATH}/config.mk" + # Find the TOOLCHAIN line, split it using ':=' as the field separator, and + # print the last field to get the value. Then pipe the value to tr to consume + # any leading/trailing spaces while allowing tr to echo the output to stdout. + awk -F ':=' '/TOOLCHAIN/ { print $NF }' "${vpx_config_mk}" | tr -d ' ' +} + +# Trap function used for failure reports and tool output directory removal. +# When the contents of $VPX_TOOL_TEST do not match the string '<unset>', reports +# failure of test stored in $VPX_TOOL_TEST. +cleanup() { + if [ -n "${VPX_TOOL_TEST}" ] && [ "${VPX_TOOL_TEST}" != '<unset>' ]; then + echo "FAIL: $VPX_TOOL_TEST" + fi + if [ -n "${VPX_TEST_OUTPUT_DIR}" ] && [ -d "${VPX_TEST_OUTPUT_DIR}" ]; then + rm -rf "${VPX_TEST_OUTPUT_DIR}" + fi +} + +# Echoes the git hash portion of the VERSION_STRING variable defined in +# $LIBVPX_CONFIG_PATH/config.mk to stdout, or the version number string when +# no git hash is contained in VERSION_STRING. +config_hash() { + vpx_config_mk="${LIBVPX_CONFIG_PATH}/config.mk" + # Find VERSION_STRING line, split it with "-g" and print the last field to + # output the git hash to stdout. + vpx_version=$(awk -F -g '/VERSION_STRING/ {print $NF}' "${vpx_config_mk}") + # Handle two situations here: + # 1. The default case: $vpx_version is a git hash, so echo it unchanged. + # 2. When being run a non-dev tree, the -g portion is not present in the + # version string: It's only the version number. + # In this case $vpx_version is something like 'VERSION_STRING=v1.3.0', so + # we echo only what is after the '='. + echo "${vpx_version##*=}" +} + +# Echoes the short form of the current git hash. +current_hash() { + if git --version > /dev/null 2>&1; then + (cd "$(dirname "${0}")" + git rev-parse --short HEAD) + else + # Return the config hash if git is unavailable: Fail silently, git hashes + # are used only for warnings. + config_hash + fi +} + +# Echoes warnings to stdout when git hash in vpx_config.h does not match the +# current git hash. +check_git_hashes() { + hash_at_configure_time=$(config_hash) + hash_now=$(current_hash) + + if [ "${hash_at_configure_time}" != "${hash_now}" ]; then + echo "Warning: git hash has changed since last configure." + fi +} + +# This script requires that the LIBVPX_BIN_PATH, LIBVPX_CONFIG_PATH, and +# LIBVPX_TEST_DATA_PATH variables are in the environment: Confirm that +# the variables are set and that they all evaluate to directory paths. +verify_vpx_test_environment() { + if [ ! -d "${LIBVPX_BIN_PATH}" ]; then + echo "The LIBVPX_BIN_PATH environment variable must be set." + return 1 + fi + if [ ! -d "${LIBVPX_CONFIG_PATH}" ]; then + echo "The LIBVPX_CONFIG_PATH environment variable must be set." + return 1 + fi + if [ ! -d "${LIBVPX_TEST_DATA_PATH}" ]; then + echo "The LIBVPX_TEST_DATA_PATH environment variable must be set." + return 1 + fi +} + +# Greps vpx_config.h in LIBVPX_CONFIG_PATH for positional parameter one, which +# should be a LIBVPX preprocessor flag. Echoes yes to stdout when the feature +# is available. +vpx_config_option_enabled() { + vpx_config_option="${1}" + vpx_config_file="${LIBVPX_CONFIG_PATH}/vpx_config.h" + config_line=$(grep "${vpx_config_option}" "${vpx_config_file}") + if echo "${config_line}" | egrep -q '1$'; then + echo yes + fi +} + +# Echoes yes when output of test_configuration_target() contains win32 or win64. +is_windows_target() { + if test_configuration_target \ + | grep -q -e win32 -e win64 > /dev/null 2>&1; then + echo yes + fi +} + +# Echoes yes to stdout when the file named by positional parameter one exists +# in LIBVPX_BIN_PATH, and is executable. +vpx_tool_available() { + tool_name="${1}" + if [ "$(is_windows_target)" = "yes" ]; then + tool_name="${tool_name}.exe" + fi + [ -x "${LIBVPX_BIN_PATH}/${1}" ] && echo yes +} + +# Echoes yes to stdout when vpx_config_option_enabled() reports yes for +# CONFIG_VP8_DECODER. +vp8_decode_available() { + [ "$(vpx_config_option_enabled CONFIG_VP8_DECODER)" = "yes" ] && echo yes +} + +# Echoes yes to stdout when vpx_config_option_enabled() reports yes for +# CONFIG_VP8_ENCODER. +vp8_encode_available() { + [ "$(vpx_config_option_enabled CONFIG_VP8_ENCODER)" = "yes" ] && echo yes +} + +# Echoes yes to stdout when vpx_config_option_enabled() reports yes for +# CONFIG_VP9_DECODER. +vp9_decode_available() { + [ "$(vpx_config_option_enabled CONFIG_VP9_DECODER)" = "yes" ] && echo yes +} + +# Echoes yes to stdout when vpx_config_option_enabled() reports yes for +# CONFIG_VP9_ENCODER. +vp9_encode_available() { + [ "$(vpx_config_option_enabled CONFIG_VP9_ENCODER)" = "yes" ] && echo yes +} + +# Echoes yes to stdout when vpx_config_option_enabled() reports yes for +# CONFIG_WEBM_IO. +webm_io_available() { + [ "$(vpx_config_option_enabled CONFIG_WEBM_IO)" = "yes" ] && echo yes +} + +# Echoes yes to stdout when vpxdec exists according to vpx_tool_available(). +vpxdec_available() { + [ -n $(vpx_tool_available vpxdec) ] && echo yes +} + +# Wrapper function for running vpxdec in noblit mode. Requires that +# LIBVPX_BIN_PATH points to the directory containing vpxdec. Positional +# parameter one is used as the input file path. Positional parameter two, when +# present, is interpreted as a boolean flag that means the input should be sent +# to vpxdec via pipe from cat instead of directly. +vpxdec() { + input="${1}" + pipe_input=${2} + + if [ $# -gt 2 ]; then + # shift away $1 and $2 so the remaining arguments can be passed to vpxdec + # via $@. + shift 2 + fi + + decoder="${LIBVPX_BIN_PATH}/vpxdec" + + if [ "$(is_windows_target)" = "yes" ]; then + decoder="${decoder}.exe" + fi + + if [ -z "${pipe_input}" ]; then + "${decoder}" "$input" --summary --noblit "$@" > /dev/null 2>&1 + else + cat "${input}" | "${decoder}" - --summary --noblit "$@" > /dev/null 2>&1 + fi +} + +# Echoes yes to stdout when vpxenc exists according to vpx_tool_available(). +vpxenc_available() { + [ -n $(vpx_tool_available vpxenc) ] && echo yes +} + +# Wrapper function for running vpxenc. Positional parameters are interpreted as +# follows: +# 1 - codec name +# 2 - input width +# 3 - input height +# 4 - number of frames to encode +# 5 - path to input file +# 6 - path to output file +# Note: The output file path must end in .ivf to output an IVF file. +# 7 - extra flags +# Note: Extra flags currently supports a special case: when set to "-" +# input is piped to vpxenc via cat. +vpxenc() { + encoder="${LIBVPX_BIN_PATH}/vpxenc" + codec="${1}" + width=${2} + height=${3} + frames=${4} + input=${5} + output="${VPX_TEST_OUTPUT_DIR}/${6}" + extra_flags=${7} + + if [ "$(is_windows_target)" = "yes" ]; then + encoder="${encoder}.exe" + fi + + # Because --ivf must be within the command line to get IVF from vpxenc. + if echo "${output}" | egrep -q 'ivf$'; then + use_ivf=--ivf + else + unset use_ivf + fi + + if [ "${extra_flags}" = "-" ]; then + pipe_input=yes + extra_flags=${8} + else + unset pipe_input + fi + + if [ -z "${pipe_input}" ]; then + "${encoder}" --codec=${codec} --width=${width} --height=${height} \ + --limit=${frames} ${use_ivf} ${extra_flags} --output="${output}" \ + "${input}" > /dev/null 2>&1 + else + cat "${input}" \ + | "${encoder}" --codec=${codec} --width=${width} --height=${height} \ + --limit=${frames} ${use_ivf} ${extra_flags} --output="${output}" - \ + > /dev/null 2>&1 + fi + + if [ ! -e "${output}" ]; then + # Return non-zero exit status: output file doesn't exist, so something + # definitely went wrong. + return 1 + fi +} + +# Filters strings from positional parameter one using the filter specified by +# positional parameter two. Filter behavior depends on the presence of a third +# positional parameter. When parameter three is present, strings that match the +# filter are excluded. When omitted, strings matching the filter are included. +# The filtered string is echoed to stdout. +filter_strings() { + strings=${1} + filter=${2} + exclude=${3} + + if [ -n "${exclude}" ]; then + # When positional parameter three exists the caller wants to remove strings. + # Tell grep to invert matches using the -v argument. + exclude='-v' + else + unset exclude + fi + + if [ -n "${filter}" ]; then + for s in ${strings}; do + if echo "${s}" | egrep -q ${exclude} "${filter}" > /dev/null 2>&1; then + filtered_strings="${filtered_strings} ${s}" + fi + done + else + filtered_strings="${strings}" + fi + echo "${filtered_strings}" +} + +# Runs user test functions passed via positional parameters one and two. +# Functions in positional parameter one are treated as environment verification +# functions and are run unconditionally. Functions in positional parameter two +# are run according to the rules specified in vpx_test_usage(). +run_tests() { + env_tests="verify_vpx_test_environment ${1}" + tests_to_filter="${2}" + + if [ "${VPX_TEST_RUN_DISABLED_TESTS}" != "yes" ]; then + # Filter out DISABLED tests. + tests_to_filter=$(filter_strings "${tests_to_filter}" ^DISABLED exclude) + fi + + if [ -n "${VPX_TEST_FILTER}" ]; then + # Remove tests not matching the user's filter. + tests_to_filter=$(filter_strings "${tests_to_filter}" ${VPX_TEST_FILTER}) + fi + + tests_to_run="${env_tests} ${tests_to_filter}" + + check_git_hashes + + # Run tests. + for test in ${tests_to_run}; do + test_begin "${test}" + "${test}" + [ "${VPX_TEST_VERBOSE_OUTPUT}" = "yes" ] && echo " PASS ${test}" + test_end "${test}" + done + + tested_config="$(test_configuration_target) @ $(current_hash)" + echo $(basename "${0%.*}"): Done, all tests pass for ${tested_config}. +} + +vpx_test_usage() { +cat << EOF + Usage: ${0##*/} [arguments] + --bin-path <path to libvpx binaries directory> + --config-path <path to libvpx config directory> + --filter <filter>: User test filter. Only tests matching filter are run. + --run-disabled-tests: Run disabled tests. + --help: Display this message and exit. + --test-data-path <path to libvpx test data directory> + --verbose: Verbose output. + + When the --bin-path option is not specified the script attempts to use + \$LIBVPX_BIN_PATH and then the current directory. + + When the --config-path option is not specified the script attempts to use + \$LIBVPX_CONFIG_PATH and then the current directory. + + When the -test-data-path option is not specified the script attempts to use + \$LIBVPX_TEST_DATA_PATH and then the current directory. +EOF +} + +# Returns non-zero (failure) when required environment variables are empty +# strings. +vpx_test_check_environment() { + if [ -z "${LIBVPX_BIN_PATH}" ] || \ + [ -z "${LIBVPX_CONFIG_PATH}" ] || \ + [ -z "${LIBVPX_TEST_DATA_PATH}" ]; then + return 1 + fi +} + +# Parse the command line. +while [ -n "$1" ]; do + case "$1" in + --bin-path) + LIBVPX_BIN_PATH="$2" + shift + ;; + --config-path) + LIBVPX_CONFIG_PATH="$2" + shift + ;; + --filter) + VPX_TEST_FILTER="$2" + shift + ;; + --run-disabled-tests) + VPX_TEST_RUN_DISABLED_TESTS=yes + ;; + --help) + vpx_test_usage + exit + ;; + --test-data-path) + LIBVPX_TEST_DATA_PATH="$2" + shift + ;; + --verbose) + VPX_TEST_VERBOSE_OUTPUT=yes + ;; + *) + vpx_test_usage + exit 1 + ;; + esac + shift +done + +# Handle running the tests from a build directory without arguments when running +# the tests on *nix/macosx. +LIBVPX_BIN_PATH="${LIBVPX_BIN_PATH:-.}" +LIBVPX_CONFIG_PATH="${LIBVPX_CONFIG_PATH:-.}" +LIBVPX_TEST_DATA_PATH="${LIBVPX_TEST_DATA_PATH:-.}" + +# Create a temporary directory for output files, and a trap to clean it up. +if [ -n "${TMPDIR}" ]; then + VPX_TEST_TEMP_ROOT="${TMPDIR}" +elif [ -n "${TEMPDIR}" ]; then + VPX_TEST_TEMP_ROOT="${TEMPDIR}" +else + VPX_TEST_TEMP_ROOT=/tmp +fi + +VPX_TEST_RAND=$(awk 'BEGIN { srand(); printf "%d\n",(rand() * 32768)}') +VPX_TEST_OUTPUT_DIR="${VPX_TEST_TEMP_ROOT}/vpx_test_${VPX_TEST_RAND}" + +if ! mkdir -p "${VPX_TEST_OUTPUT_DIR}" || \ + [ ! -d "${VPX_TEST_OUTPUT_DIR}" ]; then + echo "${0##*/}: Cannot create output directory, giving up." + echo "${0##*/}: VPX_TEST_OUTPUT_DIR=${VPX_TEST_OUTPUT_DIR}" + exit 1 +fi + +trap cleanup EXIT + +if [ "${VPX_TEST_VERBOSE_OUTPUT}" = "yes" ]; then +cat << EOF +$(basename "${0%.*}") test configuration: + LIBVPX_BIN_PATH=${LIBVPX_BIN_PATH} + LIBVPX_CONFIG_PATH=${LIBVPX_CONFIG_PATH} + LIBVPX_TEST_DATA_PATH=${LIBVPX_TEST_DATA_PATH} + VPX_TEST_OUTPUT_DIR=${VPX_TEST_OUTPUT_DIR} + VPX_TEST_VERBOSE_OUTPUT=${VPX_TEST_VERBOSE_OUTPUT} + VPX_TEST_FILTER=${VPX_TEST_FILTER} + VPX_TEST_RUN_DISABLED_TESTS=${VPX_TEST_RUN_DISABLED_TESTS} +EOF +fi diff --git a/test/vpxdec.sh b/test/vpxdec.sh new file mode 100755 index 000000000..d236f973b --- /dev/null +++ b/test/vpxdec.sh @@ -0,0 +1,65 @@ +#!/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 file tests vpxdec. To add new tests to this file, do the following: +## 1. Write a shell function (this is your test). +## 2. Add the function to vpxdec_tests (on a new line). +## +. $(dirname $0)/tools_common.sh + +VP8_IVF_FILE="${LIBVPX_TEST_DATA_PATH}/vp80-00-comprehensive-001.ivf" +VP9_WEBM_FILE="${LIBVPX_TEST_DATA_PATH}/vp90-2-00-quantizer-00.webm" + +# Environment check: Make sure input is available. +vpxdec_verify_environment() { + if [ ! -e "${VP8_IVF_FILE}" ] || [ ! -e "${VP9_WEBM_FILE}" ]; then + echo "Libvpx test data must exist in LIBVPX_TEST_DATA_PATH." + return 1 + fi +} + +vpxdec_can_decode_vp8() { + if [ "$(vpxdec_available)" = "yes" ] && \ + [ "$(vp8_decode_available)" = "yes" ]; then + echo yes + fi +} + +vpxdec_can_decode_vp9() { + if [ "$(vpxdec_available)" = "yes" ] && \ + [ "$(vp9_decode_available)" = "yes" ]; then + echo yes + fi +} + +vpxdec_vp8_ivf() { + if [ "$(vpxdec_can_decode_vp8)" = "yes" ]; then + vpxdec "${VP8_IVF_FILE}" + fi +} + +vpxdec_vp8_ivf_pipe_input() { + if [ "$(vpxdec_can_decode_vp8)" = "yes" ]; then + vpxdec "${VP8_IVF_FILE}" - + fi +} + +vpxdec_vp9_webm() { + if [ "$(vpxdec_can_decode_vp9)" = "yes" ] && \ + [ "$(webm_io_available)" = "yes" ]; then + vpxdec "${VP9_WEBM_FILE}" + fi +} + +vpxdec_tests="vpxdec_vp8_ivf + vpxdec_vp8_ivf_pipe_input + vpxdec_vp9_webm" + +run_tests vpxdec_verify_environment "${vpxdec_tests}" diff --git a/test/vpxenc.sh b/test/vpxenc.sh new file mode 100755 index 000000000..89e4eb39c --- /dev/null +++ b/test/vpxenc.sh @@ -0,0 +1,96 @@ +#!/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 file tests vpxenc using hantro_collage_w352h288.yuv as input. To add +## new tests to this file, do the following: +## 1. Write a shell function (this is your test). +## 2. Add the function to vpxenc_tests (on a new line). +## +. $(dirname $0)/tools_common.sh + +YUV_RAW_INPUT="${LIBVPX_TEST_DATA_PATH}/hantro_collage_w352h288.yuv" +YUV_RAW_INPUT_WIDTH=352 +YUV_RAW_INPUT_HEIGHT=288 +TEST_FRAMES=10 + +# Environment check: Make sure input is available. +vpxenc_verify_environment() { + if [ ! -e "${YUV_RAW_INPUT}" ]; then + echo "The file ${YUV_RAW_INPUT##*/} must exist in LIBVPX_TEST_DATA_PATH." + return 1 + fi +} + +vpxenc_can_encode_vp8() { + if [ "$(vpxenc_available)" = "yes" ] && \ + [ "$(vp8_encode_available)" = "yes" ]; then + echo yes + fi +} + +vpxenc_can_encode_vp9() { + if [ "$(vpxenc_available)" = "yes" ] && \ + [ "$(vp9_encode_available)" = "yes" ]; then + echo yes + fi +} + +vpxenc_vp8_ivf() { + if [ "$(vpxenc_can_encode_vp8)" = "yes" ]; then + vpxenc vp8 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp8.ivf + fi +} + +vpxenc_vp8_ivf_pipe_input() { + if [ "$(vpxenc_can_encode_vp8)" = "yes" ]; then + vpxenc vp8 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp8.ivf - + fi +} + +vpxenc_vp8_webm() { + if [ "$(vpxenc_can_encode_vp8)" = "yes" ] && + [ "$(webm_io_available)" = "yes" ] ; then + vpxenc vp8 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp8.webm + fi +} + +vpxenc_vp9_ivf() { + if [ "$(vpxenc_can_encode_vp9)" = "yes" ]; then + vpxenc vp9 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp9.ivf + fi +} + +vpxenc_vp9_webm() { + if [ "$(vpxenc_can_encode_vp9)" = "yes" ] && + [ "$(webm_io_available)" = "yes" ] ; then + vpxenc vp9 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp9.webm + fi +} + +DISABLED_vpxenc_vp9_ivf_lossless() { + if [ "$(vpxenc_can_encode_vp9)" = "yes" ]; then + vpxenc vp9 ${YUV_RAW_INPUT_WIDTH} ${YUV_RAW_INPUT_HEIGHT} ${TEST_FRAMES} \ + "${YUV_RAW_INPUT}" vp9_lossless.ivf --lossless + fi +} + +vpxenc_tests="vpxenc_vp8_ivf + vpxenc_vp8_webm + vpxenc_vp8_ivf_pipe_input + vpxenc_vp9_ivf + vpxenc_vp9_webm + DISABLED_vpxenc_vp9_ivf_lossless" + +run_tests vpxenc_verify_environment "${vpxenc_tests}" diff --git a/tools_common.h b/tools_common.h index 58894def0..549e895ec 100644 --- a/tools_common.h +++ b/tools_common.h @@ -22,10 +22,12 @@ #endif #if defined(_MSC_VER) -/* MSVS doesn't define off_t, and uses _f{seek,tell}i64. */ -typedef __int64 off_t; +/* MSVS uses _f{seek,tell}i64. */ #define fseeko _fseeki64 #define ftello _ftelli64 +typedef long _off_t; // NOLINT - MSVS compatible type +typedef __int64 off_t; // fseeki64 compatible type +#define _OFF_T_DEFINED #elif defined(_WIN32) /* MinGW defines off_t as long and uses f{seek,tell}o64/off64_t for large * files. */ diff --git a/vp9/common/vp9_blockd.h b/vp9/common/vp9_blockd.h index 1f7e6329d..55320a6a4 100644 --- a/vp9/common/vp9_blockd.h +++ b/vp9/common/vp9_blockd.h @@ -231,8 +231,6 @@ typedef struct macroblockd { /* Inverse transform function pointers. */ void (*itxm_add)(const int16_t *input, uint8_t *dest, int stride, int eob); - const InterpKernel *interp_kernel; - int corrupted; DECLARE_ALIGNED(16, int16_t, dqcoeff[MAX_MB_PLANE][64 * 64]); diff --git a/vp9/common/vp9_reconinter.c b/vp9/common/vp9_reconinter.c index 0ab50d456..e722d6a3e 100644 --- a/vp9/common/vp9_reconinter.c +++ b/vp9/common/vp9_reconinter.c @@ -146,6 +146,7 @@ static void build_inter_predictors(MACROBLOCKD *xd, int plane, int block, struct macroblockd_plane *const pd = &xd->plane[plane]; const MODE_INFO *mi = xd->mi[0]; const int is_compound = has_second_ref(&mi->mbmi); + const InterpKernel *kernel = vp9_get_interp_kernel(mi->mbmi.interp_filter); int ref; for (ref = 0; ref < 1 + is_compound; ++ref) { @@ -193,8 +194,7 @@ static void build_inter_predictors(MACROBLOCKD *xd, int plane, int block, + (scaled_mv.col >> SUBPEL_BITS); inter_predictor(pre, pre_buf->stride, dst, dst_buf->stride, - subpel_x, subpel_y, sf, w, h, ref, xd->interp_kernel, - xs, ys); + subpel_x, subpel_y, sf, w, h, ref, kernel, xs, ys); } } @@ -250,6 +250,7 @@ static void dec_build_inter_predictors(MACROBLOCKD *xd, int plane, int block, struct macroblockd_plane *const pd = &xd->plane[plane]; const MODE_INFO *mi = xd->mi[0]; const int is_compound = has_second_ref(&mi->mbmi); + const InterpKernel *kernel = vp9_get_interp_kernel(mi->mbmi.interp_filter); int ref; for (ref = 0; ref < 1 + is_compound; ++ref) { @@ -377,7 +378,7 @@ static void dec_build_inter_predictors(MACROBLOCKD *xd, int plane, int block, } inter_predictor(buf_ptr, buf_stride, dst, dst_buf->stride, subpel_x, - subpel_y, sf, w, h, ref, xd->interp_kernel, xs, ys); + subpel_y, sf, w, h, ref, kernel, xs, ys); } } diff --git a/vp9/decoder/vp9_decodeframe.c b/vp9/decoder/vp9_decodeframe.c index 53f94f848..5a2e6f881 100644 --- a/vp9/decoder/vp9_decodeframe.c +++ b/vp9/decoder/vp9_decodeframe.c @@ -360,8 +360,6 @@ static void decode_block(VP9_COMMON *const cm, MACROBLOCKD *const xd, if (has_second_ref(mbmi)) set_ref(cm, xd, 1, mi_row, mi_col); - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); - // Prediction vp9_dec_build_inter_predictors_sb(xd, mi_row, mi_col, bsize); diff --git a/vp9/encoder/vp9_aq_cyclicrefresh.c b/vp9/encoder/vp9_aq_cyclicrefresh.c index 1d272e1d9..2e1b4ef5f 100644 --- a/vp9/encoder/vp9_aq_cyclicrefresh.c +++ b/vp9/encoder/vp9_aq_cyclicrefresh.c @@ -204,7 +204,7 @@ void vp9_cyclic_refresh_setup(VP9_COMP *const cpi) { // Some of these parameters may be set via codec-control function later. cr->max_sbs_perframe = 10; cr->max_qdelta_perc = 50; - cr->min_block_size = BLOCK_16X16; + cr->min_block_size = BLOCK_8X8; cr->time_for_refresh = 1; // Set rate threshold to some fraction of target (and scaled by 256). cr->thresh_rate_sb = (rc->sb64_target_rate * 256) >> 2; diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c index 7c811e2a3..dbefe1ecf 100644 --- a/vp9/encoder/vp9_bitstream.c +++ b/vp9/encoder/vp9_bitstream.c @@ -484,12 +484,12 @@ static void write_modes(VP9_COMP *cpi, } } -static void build_tree_distribution(VP9_COMP *cpi, TX_SIZE tx_size) { +static void build_tree_distribution(VP9_COMP *cpi, TX_SIZE tx_size, + vp9_coeff_stats *coef_branch_ct) { vp9_coeff_probs_model *coef_probs = cpi->frame_coef_probs[tx_size]; vp9_coeff_count *coef_counts = cpi->coef_counts[tx_size]; unsigned int (*eob_branch_ct)[REF_TYPES][COEF_BANDS][COEFF_CONTEXTS] = cpi->common.counts.eob_branch[tx_size]; - vp9_coeff_stats *coef_branch_ct = cpi->frame_branch_ct[tx_size]; int i, j, k, l, m; for (i = 0; i < PLANE_TYPES; ++i) { @@ -512,11 +512,11 @@ static void build_tree_distribution(VP9_COMP *cpi, TX_SIZE tx_size) { } static void update_coef_probs_common(vp9_writer* const bc, VP9_COMP *cpi, - TX_SIZE tx_size) { + TX_SIZE tx_size, + vp9_coeff_stats *frame_branch_ct) { vp9_coeff_probs_model *new_frame_coef_probs = cpi->frame_coef_probs[tx_size]; vp9_coeff_probs_model *old_frame_coef_probs = cpi->common.fc.coef_probs[tx_size]; - vp9_coeff_stats *frame_branch_ct = cpi->frame_branch_ct[tx_size]; const vp9_prob upd = DIFF_UPDATE_PROB; const int entropy_nodes_update = UNCONSTRAINED_NODES; int i, j, k, l, t; @@ -669,13 +669,15 @@ static void update_coef_probs(VP9_COMP *cpi, vp9_writer* w) { const TX_MODE tx_mode = cpi->common.tx_mode; const TX_SIZE max_tx_size = tx_mode_to_biggest_tx_size[tx_mode]; TX_SIZE tx_size; + vp9_coeff_stats frame_branch_ct[TX_SIZES][PLANE_TYPES]; + vp9_clear_system_state(); for (tx_size = TX_4X4; tx_size <= TX_32X32; ++tx_size) - build_tree_distribution(cpi, tx_size); + build_tree_distribution(cpi, tx_size, frame_branch_ct[tx_size]); for (tx_size = TX_4X4; tx_size <= max_tx_size; ++tx_size) - update_coef_probs_common(w, cpi, tx_size); + update_coef_probs_common(w, cpi, tx_size, frame_branch_ct[tx_size]); } static void encode_loopfilter(struct loopfilter *lf, diff --git a/vp9/encoder/vp9_encodeframe.c b/vp9/encoder/vp9_encodeframe.c index 42e1ac88f..67b8b0292 100644 --- a/vp9/encoder/vp9_encodeframe.c +++ b/vp9/encoder/vp9_encodeframe.c @@ -856,13 +856,22 @@ static void update_state(VP9_COMP *cpi, PICK_MODE_CONTEXT *ctx, *mi_addr = *mi; - // For in frame adaptive Q, check for reseting the segment_id and updating - // the cyclic refresh map. - if ((cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) && seg->enabled && - output_enabled) { - vp9_cyclic_refresh_update_segment(cpi, &xd->mi[0]->mbmi, - mi_row, mi_col, bsize, 1); - vp9_init_plane_quantizers(cpi, x); + // If segmentation in use + if (seg->enabled && output_enabled) { + // For in frame complexity AQ copy the segment id from the segment map. + if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) { + const uint8_t *const map = seg->update_map ? cpi->segmentation_map + : cm->last_frame_seg_map; + mi_addr->mbmi.segment_id = + vp9_get_segment_id(cm, map, bsize, mi_row, mi_col); + } + // Else for cyclic refresh mode update the segment map, set the segment id + // and then update the quantizer. + else if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) { + vp9_cyclic_refresh_update_segment(cpi, &xd->mi[0]->mbmi, + mi_row, mi_col, bsize, 1); + vp9_init_plane_quantizers(cpi, x); + } } max_plane = is_inter_block(mbmi) ? MAX_MB_PLANE : 1; @@ -3430,7 +3439,6 @@ static void encode_superblock(VP9_COMP *cpi, TOKENEXTRA **t, int output_enabled, } } else { set_ref_ptrs(cm, xd, mbmi->ref_frame[0], mbmi->ref_frame[1]); - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); if (cpi->oxcf.tuning == VP8_TUNE_SSIM) { // Adjust the zbin based on this MB rate. diff --git a/vp9/encoder/vp9_mcomp.c b/vp9/encoder/vp9_mcomp.c index e0299f07f..d40701046 100644 --- a/vp9/encoder/vp9_mcomp.c +++ b/vp9/encoder/vp9_mcomp.c @@ -500,8 +500,7 @@ static int vp9_pattern_search(const MACROBLOCK *x, MV *ref_mv, int search_param, int sad_per_bit, - int do_init_search, - int do_refine, + int do_init_search, int do_refine, const vp9_variance_fn_ptr_t *vfp, int use_mvcost, const MV *center_mv, MV *best_mv, @@ -513,20 +512,15 @@ static int vp9_pattern_search(const MACROBLOCK *x, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }; int i, j, s, t; - const uint8_t *what = x->plane[0].src.buf; - const int what_stride = x->plane[0].src.stride; - const int in_what_stride = xd->plane[0].pre[0].stride; + const struct buf_2d *const what = &x->plane[0].src; + const struct buf_2d *const in_what = &xd->plane[0].pre[0]; int br, bc; - MV this_mv; int bestsad = INT_MAX; int thissad; - const uint8_t *base_offset; - const uint8_t *this_offset; int k = -1; - int best_site = -1; const MV fcenter_mv = {center_mv->row >> 3, center_mv->col >> 3}; int best_init_s = search_param_to_steps[search_param]; - const int *mvjsadcost = x->nmvjointsadcost; + const int *const mvjsadcost = x->nmvjointsadcost; int *mvsadcost[2] = {x->nmvsadcost[0], x->nmvsadcost[1]}; // adjust ref_mv to make sure it is within MV range @@ -535,13 +529,10 @@ static int vp9_pattern_search(const MACROBLOCK *x, bc = ref_mv->col; // Work out the start point for the search - base_offset = xd->plane[0].pre[0].buf; - this_offset = base_offset + (br * in_what_stride) + bc; - this_mv.row = br; - this_mv.col = bc; - bestsad = vfp->sdf(what, what_stride, this_offset, in_what_stride, 0x7fffffff) - + mvsad_err_cost(&this_mv, &fcenter_mv, - mvjsadcost, mvsadcost, sad_per_bit); + bestsad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, ref_mv), in_what->stride, + 0x7fffffff) + mvsad_err_cost(ref_mv, &fcenter_mv, + mvjsadcost, mvsadcost, sad_per_bit); // Search all possible scales upto the search param around the center point // pick the scale of the point that is best as the starting scale of @@ -550,27 +541,25 @@ static int vp9_pattern_search(const MACROBLOCK *x, s = best_init_s; best_init_s = -1; for (t = 0; t <= s; ++t) { - best_site = -1; + int best_site = -1; if (check_bounds(x, br, bc, 1 << t)) { for (i = 0; i < num_candidates[t]; i++) { - this_mv.row = br + candidates[t][i].row; - this_mv.col = bc + candidates[t][i].col; - this_offset = base_offset + (this_mv.row * in_what_stride) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + const MV this_mv = {br + candidates[t][i].row, + bc + candidates[t][i].col}; + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } else { for (i = 0; i < num_candidates[t]; i++) { - this_mv.row = br + candidates[t][i].row; - this_mv.col = bc + candidates[t][i].col; + const MV this_mv = {br + candidates[t][i].row, + bc + candidates[t][i].col}; if (!is_mv_in(x, &this_mv)) continue; - this_offset = base_offset + (this_mv.row * in_what_stride) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } @@ -590,31 +579,30 @@ static int vp9_pattern_search(const MACROBLOCK *x, // If the center point is still the best, just skip this and move to // the refinement step. if (best_init_s != -1) { + int best_site = -1; s = best_init_s; - best_site = -1; + do { // No need to search all 6 points the 1st time if initial search was used if (!do_init_search || s != best_init_s) { if (check_bounds(x, br, bc, 1 << s)) { for (i = 0; i < num_candidates[s]; i++) { - this_mv.row = br + candidates[s][i].row; - this_mv.col = bc + candidates[s][i].col; - this_offset = base_offset + (this_mv.row * in_what_stride) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + const MV this_mv = {br + candidates[s][i].row, + bc + candidates[s][i].col}; + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } else { for (i = 0; i < num_candidates[s]; i++) { - this_mv.row = br + candidates[s][i].row; - this_mv.col = bc + candidates[s][i].col; + const MV this_mv = {br + candidates[s][i].row, + bc + candidates[s][i].col}; if (!is_mv_in(x, &this_mv)) continue; - this_offset = base_offset + (this_mv.row * in_what_stride) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } @@ -637,24 +625,22 @@ static int vp9_pattern_search(const MACROBLOCK *x, if (check_bounds(x, br, bc, 1 << s)) { for (i = 0; i < PATTERN_CANDIDATES_REF; i++) { - this_mv.row = br + candidates[s][next_chkpts_indices[i]].row; - this_mv.col = bc + candidates[s][next_chkpts_indices[i]].col; - this_offset = base_offset + (this_mv.row * (in_what_stride)) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + const MV this_mv = {br + candidates[s][next_chkpts_indices[i]].row, + bc + candidates[s][next_chkpts_indices[i]].col}; + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } else { for (i = 0; i < PATTERN_CANDIDATES_REF; i++) { - this_mv.row = br + candidates[s][next_chkpts_indices[i]].row; - this_mv.col = bc + candidates[s][next_chkpts_indices[i]].col; + const MV this_mv = {br + candidates[s][next_chkpts_indices[i]].row, + bc + candidates[s][next_chkpts_indices[i]].col}; if (!is_mv_in(x, &this_mv)) continue; - this_offset = base_offset + (this_mv.row * (in_what_stride)) + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } @@ -671,29 +657,28 @@ static int vp9_pattern_search(const MACROBLOCK *x, // Check 4 1-away neighbors if do_refine is true. // For most well-designed schemes do_refine will not be necessary. if (do_refine) { - static const MV neighbors[4] = { {0, -1}, { -1, 0}, {1, 0}, {0, 1} }; + static const MV neighbors[4] = {{0, -1}, { -1, 0}, {1, 0}, {0, 1}}; + for (j = 0; j < 16; j++) { - best_site = -1; + int best_site = -1; if (check_bounds(x, br, bc, 1)) { for (i = 0; i < 4; i++) { - this_mv.row = br + neighbors[i].row; - this_mv.col = bc + neighbors[i].col; - this_offset = base_offset + this_mv.row * in_what_stride + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + const MV this_mv = {br + neighbors[i].row, + bc + neighbors[i].col}; + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } else { for (i = 0; i < 4; i++) { - this_mv.row = br + neighbors[i].row; - this_mv.col = bc + neighbors[i].col; + const MV this_mv = {br + neighbors[i].row, + bc + neighbors[i].col}; if (!is_mv_in(x, &this_mv)) continue; - this_offset = base_offset + this_mv.row * in_what_stride + - this_mv.col; - thissad = vfp->sdf(what, what_stride, this_offset, in_what_stride, - bestsad); + thissad = vfp->sdf(what->buf, what->stride, + get_buf_from_mv(in_what, &this_mv), + in_what->stride, bestsad); CHECK_BETTER } } @@ -710,8 +695,6 @@ static int vp9_pattern_search(const MACROBLOCK *x, best_mv->row = br; best_mv->col = bc; - this_mv.row = best_mv->row * 8; - this_mv.col = best_mv->col * 8; return bestsad; } @@ -719,39 +702,32 @@ int vp9_get_mvpred_var(const MACROBLOCK *x, const MV *best_mv, const MV *center_mv, const vp9_variance_fn_ptr_t *vfp, int use_mvcost) { - unsigned int unused; - const MACROBLOCKD *const xd = &x->e_mbd; - const uint8_t *what = x->plane[0].src.buf; - const int what_stride = x->plane[0].src.stride; - const int in_what_stride = xd->plane[0].pre[0].stride; - const uint8_t *base_offset = xd->plane[0].pre[0].buf; - const uint8_t *this_offset = &base_offset[best_mv->row * in_what_stride + - best_mv->col]; + const struct buf_2d *const what = &x->plane[0].src; + const struct buf_2d *const in_what = &xd->plane[0].pre[0]; const MV mv = {best_mv->row * 8, best_mv->col * 8}; - return vfp->vf(what, what_stride, this_offset, in_what_stride, &unused) + + unsigned int unused; + + return vfp->vf(what->buf, what->stride, + get_buf_from_mv(in_what, best_mv), in_what->stride, &unused) + (use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost, x->errorperbit) : 0); } int vp9_get_mvpred_av_var(const MACROBLOCK *x, - MV *best_mv, - const MV *center_mv, + const MV *best_mv, const MV *center_mv, const uint8_t *second_pred, const vp9_variance_fn_ptr_t *vfp, int use_mvcost) { - unsigned int bestsad; const MACROBLOCKD *const xd = &x->e_mbd; - const uint8_t *what = x->plane[0].src.buf; - const int what_stride = x->plane[0].src.stride; - const int in_what_stride = xd->plane[0].pre[0].stride; - const uint8_t *base_offset = xd->plane[0].pre[0].buf; - const uint8_t *this_offset = base_offset + (best_mv->row * in_what_stride) + - best_mv->col; - const MV this_mv = {best_mv->row * 8, best_mv->col * 8}; - return vfp->svaf(this_offset, in_what_stride, 0, 0, what, what_stride, - &bestsad, second_pred) + - (use_mvcost ? mv_err_cost(&this_mv, center_mv, x->nmvjointcost, + const struct buf_2d *const what = &x->plane[0].src; + const struct buf_2d *const in_what = &xd->plane[0].pre[0]; + const MV mv = {best_mv->row * 8, best_mv->col * 8}; + unsigned int unused; + + return vfp->svaf(get_buf_from_mv(in_what, best_mv), in_what->stride, 0, 0, + what->buf, what->stride, &unused, second_pred) + + (use_mvcost ? mv_err_cost(&mv, center_mv, x->nmvjointcost, x->mvcost, x->errorperbit) : 0); } diff --git a/vp9/encoder/vp9_mcomp.h b/vp9/encoder/vp9_mcomp.h index 917de75e6..f7b7c5e49 100644 --- a/vp9/encoder/vp9_mcomp.h +++ b/vp9/encoder/vp9_mcomp.h @@ -42,8 +42,7 @@ int vp9_get_mvpred_var(const MACROBLOCK *x, const vp9_variance_fn_ptr_t *vfp, int use_mvcost); int vp9_get_mvpred_av_var(const MACROBLOCK *x, - MV *best_mv, - const MV *center_mv, + const MV *best_mv, const MV *center_mv, const uint8_t *second_pred, const vp9_variance_fn_ptr_t *vfp, int use_mvcost); diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index f8427cffd..9ecab5770 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -1002,7 +1002,7 @@ static void cal_nmvjointsadcost(int *mvjointsadcost) { mvjointsadcost[0] = 600; mvjointsadcost[1] = 300; mvjointsadcost[2] = 300; - mvjointsadcost[0] = 300; + mvjointsadcost[3] = 300; } static void cal_nmvsadcosts(int *mvsadcost[2]) { @@ -3198,8 +3198,6 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, } set_ref_ptrs(cm, xd, LAST_FRAME, LAST_FRAME); - xd->interp_kernel = vp9_get_interp_kernel( - DEFAULT_INTERP_FILTER == SWITCHABLE ? EIGHTTAP : DEFAULT_INTERP_FILTER); if (cpi->oxcf.aq_mode == VARIANCE_AQ) { vp9_vaq_init(); diff --git a/vp9/encoder/vp9_onyx_int.h b/vp9/encoder/vp9_onyx_int.h index 535bdaacb..d9dbd53da 100644 --- a/vp9/encoder/vp9_onyx_int.h +++ b/vp9/encoder/vp9_onyx_int.h @@ -232,6 +232,9 @@ typedef struct { int lossless; AQ_MODE aq_mode; // Adaptive Quantization mode + // Enable feature to reduce the frame quantization every x frames. + int frame_periodic_boost; + // two pass datarate control int two_pass_vbrbias; // two pass datarate control tweaks int two_pass_vbrmin_section; @@ -385,7 +388,6 @@ typedef struct VP9_COMP { vp9_coeff_count coef_counts[TX_SIZES][PLANE_TYPES]; vp9_coeff_probs_model frame_coef_probs[TX_SIZES][PLANE_TYPES]; - vp9_coeff_stats frame_branch_ct[TX_SIZES][PLANE_TYPES]; struct vpx_codec_pkt_list *output_pkt_list; @@ -587,6 +589,13 @@ static INLINE YV12_BUFFER_CONFIG *get_ref_frame_buffer( .buf; } +// Intra only frames, golden frames (except alt ref overlays) and +// alt ref frames tend to be coded at a higher than ambient quality +static INLINE int vp9_frame_is_boosted(const VP9_COMP *cpi) { + return frame_is_intra_only(&cpi->common) || cpi->refresh_alt_ref_frame || + (cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref); +} + static INLINE int get_token_alloc(int mb_rows, int mb_cols) { // TODO(JBB): make this work for alpha channel and double check we can't // exceed this token count if we have a 32x32 transform crossing a boundary diff --git a/vp9/encoder/vp9_pickmode.c b/vp9/encoder/vp9_pickmode.c index b517d6cfe..f987de512 100644 --- a/vp9/encoder/vp9_pickmode.c +++ b/vp9/encoder/vp9_pickmode.c @@ -188,14 +188,12 @@ static void model_rd_for_sb_y(VP9_COMP *cpi, BLOCK_SIZE bsize, struct macroblock_plane *const p = &x->plane[0]; struct macroblockd_plane *const pd = &xd->plane[0]; - const BLOCK_SIZE bs = get_plane_block_size(bsize, pd); - int var = cpi->fn_ptr[bs].vf(p->src.buf, p->src.stride, - pd->dst.buf, pd->dst.stride, &sse); + int var = cpi->fn_ptr[bsize].vf(p->src.buf, p->src.stride, + pd->dst.buf, pd->dst.stride, &sse); - vp9_model_rd_from_var_lapndz(var + sse, 1 << num_pels_log2_lookup[bs], + vp9_model_rd_from_var_lapndz(sse + var, 1 << num_pels_log2_lookup[bsize], pd->dequant[1] >> 3, &rate, &dist); - *out_rate_sum = rate; *out_dist_sum = dist << 3; } @@ -212,7 +210,6 @@ int64_t vp9_pick_inter_mode(VP9_COMP *cpi, MACROBLOCK *x, MB_MODE_INFO *mbmi = &xd->mi[0]->mbmi; struct macroblock_plane *const p = &x->plane[0]; struct macroblockd_plane *const pd = &xd->plane[0]; - const BLOCK_SIZE block_size = get_plane_block_size(bsize, &xd->plane[0]); MB_PREDICTION_MODE this_mode, best_mode = ZEROMV; MV_REFERENCE_FRAME ref_frame, best_ref_frame = LAST_FRAME; int_mv frame_mv[MB_MODE_COUNT][MAX_REF_FRAMES]; @@ -256,13 +253,12 @@ int64_t vp9_pick_inter_mode(VP9_COMP *cpi, MACROBLOCK *x, EIGHTTAP : cpi->common.interp_filter; mbmi->skip = 0; mbmi->segment_id = segment_id; - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); for (ref_frame = LAST_FRAME; ref_frame <= LAST_FRAME ; ++ref_frame) { x->pred_mv_sad[ref_frame] = INT_MAX; if (cpi->ref_frame_flags & flag_list[ref_frame]) { vp9_setup_buffer_inter(cpi, x, tile, - ref_frame, block_size, mi_row, mi_col, + ref_frame, bsize, mi_row, mi_col, frame_mv[NEARESTMV], frame_mv[NEARMV], yv12_mb); } frame_mv[NEWMV][ref_frame].as_int = INVALID_MV; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 016901456..eb4db1a33 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -982,8 +982,8 @@ int vp9_rc_pick_q_and_bounds(const VP9_COMP *cpi, if (cpi->sf.use_nonrd_pick_mode) { if (q == 0) q++; - if (cpi->sf.partition_check == 1) - q -= 10; + if (cpi->sf.force_frame_boost == 1) + q -= cpi->sf.max_delta_qindex; if (q < *bottom_index) *bottom_index = q; @@ -1002,28 +1002,14 @@ void vp9_rc_compute_frame_size_bounds(const VP9_COMP *cpi, *frame_under_shoot_limit = 0; *frame_over_shoot_limit = INT_MAX; } else { - if (cpi->common.frame_type == KEY_FRAME) { - *frame_over_shoot_limit = this_frame_target * 9 / 8; - *frame_under_shoot_limit = this_frame_target * 7 / 8; - } else { - if (cpi->refresh_alt_ref_frame || cpi->refresh_golden_frame) { - *frame_over_shoot_limit = this_frame_target * 9 / 8; - *frame_under_shoot_limit = this_frame_target * 7 / 8; - } else { - // Strong overshoot limit for constrained quality - if (cpi->oxcf.end_usage == USAGE_CONSTRAINED_QUALITY) { - *frame_over_shoot_limit = this_frame_target * 11 / 8; - *frame_under_shoot_limit = this_frame_target * 2 / 8; - } else { - *frame_over_shoot_limit = this_frame_target * 11 / 8; - *frame_under_shoot_limit = this_frame_target * 5 / 8; - } - } - } + int recode_tolerance = + (cpi->sf.recode_tolerance * this_frame_target) / 100; + + *frame_over_shoot_limit = this_frame_target + recode_tolerance; + *frame_under_shoot_limit = this_frame_target - recode_tolerance; // For very small rate targets where the fractional adjustment - // (eg * 7/8) may be tiny make sure there is at least a minimum - // range. + // may be tiny make sure there is at least a minimum range. *frame_over_shoot_limit += 200; *frame_under_shoot_limit -= 200; if (*frame_under_shoot_limit < 0) diff --git a/vp9/encoder/vp9_rdopt.c b/vp9/encoder/vp9_rdopt.c index d758d3bf5..a3e513277 100644 --- a/vp9/encoder/vp9_rdopt.c +++ b/vp9/encoder/vp9_rdopt.c @@ -1559,6 +1559,8 @@ static int64_t encode_inter_mb_segment(VP9_COMP *cpi, int thisrate = 0, ref; const scan_order *so = &vp9_default_scan_orders[TX_4X4]; const int is_compound = has_second_ref(&mi->mbmi); + const InterpKernel *kernel = vp9_get_interp_kernel(mi->mbmi.interp_filter); + for (ref = 0; ref < 1 + is_compound; ++ref) { const uint8_t *pre = &pd->pre[ref].buf[raster_block_offset(BLOCK_8X8, i, pd->pre[ref].stride)]; @@ -1566,7 +1568,7 @@ static int64_t encode_inter_mb_segment(VP9_COMP *cpi, dst, pd->dst.stride, &mi->bmi[i].as_mv[ref].as_mv, &xd->block_refs[ref]->sf, width, height, ref, - xd->interp_kernel, MV_PRECISION_Q3, + kernel, MV_PRECISION_Q3, mi_col * MI_SIZE + 4 * (i % 2), mi_row * MI_SIZE + 4 * (i / 2)); } @@ -2544,6 +2546,7 @@ static void joint_motion_search(VP9_COMP *cpi, MACROBLOCK *x, int ite, ref; // Prediction buffer from second frame. uint8_t *second_pred = vpx_memalign(16, pw * ph * sizeof(uint8_t)); + const InterpKernel *kernel = vp9_get_interp_kernel(mbmi->interp_filter); // Do joint motion search in compound mode to get more accurate mv. struct buf_2d backup_yv12[2][MAX_MB_PLANE]; @@ -2597,7 +2600,7 @@ static void joint_motion_search(VP9_COMP *cpi, MACROBLOCK *x, &frame_mv[refs[!id]].as_mv, &xd->block_refs[!id]->sf, pw, ph, 0, - xd->interp_kernel, MV_PRECISION_Q3, + kernel, MV_PRECISION_Q3, mi_col * MI_SIZE, mi_row * MI_SIZE); // Compound motion search on first ref frame. @@ -2812,7 +2815,6 @@ static int64_t handle_inter_mode(VP9_COMP *cpi, MACROBLOCK *x, int j; int64_t rs_rd; mbmi->interp_filter = i; - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); rs = get_switchable_rate(x); rs_rd = RDCOST(x->rdmult, x->rddiv, rs, 0); @@ -2883,7 +2885,6 @@ static int64_t handle_inter_mode(VP9_COMP *cpi, MACROBLOCK *x, // Set the appropriate filter mbmi->interp_filter = cm->interp_filter != SWITCHABLE ? cm->interp_filter : *best_filter; - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); rs = cm->interp_filter == SWITCHABLE ? get_switchable_rate(x) : 0; if (pred_exists) { @@ -3394,7 +3395,6 @@ int64_t vp9_rd_pick_inter_mode_sb(VP9_COMP *cpi, MACROBLOCK *x, : cm->interp_filter; x->skip = 0; set_ref_ptrs(cm, xd, ref_frame, second_ref_frame); - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); // Select prediction reference frames. for (i = 0; i < MAX_MB_PLANE; i++) { @@ -3938,7 +3938,6 @@ int64_t vp9_rd_pick_inter_mode_sub8x8(VP9_COMP *cpi, MACROBLOCK *x, // them for this frame. mbmi->interp_filter = cm->interp_filter == SWITCHABLE ? EIGHTTAP : cm->interp_filter; - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); if (comp_pred) { if (!(cpi->ref_frame_flags & flag_list[second_ref_frame])) @@ -4061,7 +4060,6 @@ int64_t vp9_rd_pick_inter_mode_sub8x8(VP9_COMP *cpi, MACROBLOCK *x, int newbest, rs; int64_t rs_rd; mbmi->interp_filter = switchable_filter_index; - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); tmp_rd = rd_pick_best_mbsegmentation(cpi, x, tile, &mbmi->ref_mvs[ref_frame][0], second_ref, @@ -4126,7 +4124,6 @@ int64_t vp9_rd_pick_inter_mode_sub8x8(VP9_COMP *cpi, MACROBLOCK *x, mbmi->interp_filter = (cm->interp_filter == SWITCHABLE ? tmp_best_filter : cm->interp_filter); - xd->interp_kernel = vp9_get_interp_kernel(mbmi->interp_filter); if (!pred_exists) { // Handles the special case when a filter that is not in the // switchable list (bilinear, 6-tap) is indicated at the frame level diff --git a/vp9/encoder/vp9_speed_features.c b/vp9/encoder/vp9_speed_features.c index f09035077..7b983f992 100644 --- a/vp9/encoder/vp9_speed_features.c +++ b/vp9/encoder/vp9_speed_features.c @@ -13,24 +13,32 @@ #include "vp9/encoder/vp9_onyx_int.h" #include "vp9/encoder/vp9_speed_features.h" -#define ALL_INTRA_MODES 0x3FF -#define INTRA_DC_ONLY 0x01 -#define INTRA_DC_TM ((1 << TM_PRED) | (1 << DC_PRED)) -#define INTRA_DC_H_V ((1 << DC_PRED) | (1 << V_PRED) | (1 << H_PRED)) +#define ALL_INTRA_MODES ((1 << DC_PRED) | \ + (1 << V_PRED) | (1 << H_PRED) | \ + (1 << D45_PRED) | (1 << D135_PRED) | \ + (1 << D117_PRED) | (1 << D153_PRED) | \ + (1 << D207_PRED) | (1 << D63_PRED) | \ + (1 << TM_PRED)) +#define INTRA_DC_ONLY (1 << DC_PRED) +#define INTRA_DC_TM ((1 << TM_PRED) | (1 << DC_PRED)) +#define INTRA_DC_H_V ((1 << DC_PRED) | (1 << V_PRED) | (1 << H_PRED)) #define INTRA_DC_TM_H_V (INTRA_DC_TM | (1 << V_PRED) | (1 << H_PRED)) // Masks for partially or completely disabling split mode -#define DISABLE_ALL_SPLIT 0x3F -#define DISABLE_ALL_INTER_SPLIT 0x1F -#define DISABLE_COMPOUND_SPLIT 0x18 -#define LAST_AND_INTRA_SPLIT_ONLY 0x1E - -// Intra only frames, golden frames (except alt ref overlays) and -// alt ref frames tend to be coded at a higher than ambient quality -static INLINE int frame_is_boosted(const VP9_COMP *cpi) { - return frame_is_intra_only(&cpi->common) || cpi->refresh_alt_ref_frame || - (cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref); -} +#define DISABLE_ALL_INTER_SPLIT ((1 << THR_COMP_GA) | \ + (1 << THR_COMP_LA) | \ + (1 << THR_ALTR) | \ + (1 << THR_GOLD) | \ + (1 << THR_LAST)) + +#define DISABLE_ALL_SPLIT ((1 << THR_INTRA) | DISABLE_ALL_INTER_SPLIT) + +#define DISABLE_COMPOUND_SPLIT ((1 << THR_COMP_GA) | (1 << THR_COMP_LA)) + +#define LAST_AND_INTRA_SPLIT_ONLY ((1 << THR_COMP_GA) | \ + (1 << THR_COMP_LA) | \ + (1 << THR_ALTR) | \ + (1 << THR_GOLD)) static void set_good_speed_feature(VP9_COMP *cpi, VP9_COMMON *cm, @@ -44,7 +52,7 @@ static void set_good_speed_feature(VP9_COMP *cpi, if (speed >= 1) { sf->use_square_partition_only = !frame_is_intra_only(cm); sf->less_rectangular_check = 1; - sf->tx_size_search_method = frame_is_boosted(cpi) + sf->tx_size_search_method = vp9_frame_is_boosted(cpi) ? USE_FULL_RD : USE_LARGESTALL; if (MIN(cm->width, cm->height) >= 720) @@ -68,7 +76,7 @@ static void set_good_speed_feature(VP9_COMP *cpi, } // Additions or changes from speed 1 for speed >= 2. if (speed >= 2) { - sf->tx_size_search_method = frame_is_boosted(cpi) + sf->tx_size_search_method = vp9_frame_is_boosted(cpi) ? USE_FULL_RD : USE_LARGESTALL; if (MIN(cm->width, cm->height) >= 720) @@ -251,6 +259,10 @@ static void set_rt_speed_feature(VP9_COMMON *cm, sf->min_partition_size = BLOCK_8X8; sf->partition_check = (cm->current_video_frame % sf->last_partitioning_redo_frequency == 1); + sf->force_frame_boost = cm->frame_type == KEY_FRAME || + (cm->current_video_frame % + (sf->last_partitioning_redo_frequency << 1) == 1); + sf->max_delta_qindex = (cm->frame_type == KEY_FRAME) ? 20 : 15; sf->partition_search_type = REFERENCE_PARTITION; sf->use_nonrd_pick_mode = 1; sf->search_method = FAST_DIAMOND; @@ -303,6 +315,8 @@ void vp9_set_speed_features(VP9_COMP *cpi) { sf->last_partitioning_redo_frequency = 4; sf->disable_split_mask = 0; sf->mode_search_skip_flags = 0; + sf->force_frame_boost = 0; + sf->max_delta_qindex = 0; sf->disable_split_var_thresh = 0; sf->disable_filter_search_var_thresh = 0; for (i = 0; i < TX_SIZES; i++) { @@ -326,6 +340,9 @@ void vp9_set_speed_features(VP9_COMP *cpi) { // to FIXED_PARTITION. sf->always_this_block_size = BLOCK_16X16; + // Recode loop tolerence %. + sf->recode_tolerance = 25; + switch (cpi->oxcf.mode) { case MODE_BESTQUALITY: case MODE_SECONDPASS_BEST: // This is the best quality mode. @@ -366,4 +383,8 @@ void vp9_set_speed_features(VP9_COMP *cpi) { if (sf->disable_split_mask == DISABLE_ALL_SPLIT) sf->adaptive_pred_interp_filter = 0; + + if (!cpi->oxcf.frame_periodic_boost) { + sf->max_delta_qindex = 0; + } } diff --git a/vp9/encoder/vp9_speed_features.h b/vp9/encoder/vp9_speed_features.h index 5091e2b06..826043910 100644 --- a/vp9/encoder/vp9_speed_features.h +++ b/vp9/encoder/vp9_speed_features.h @@ -252,6 +252,13 @@ typedef struct { // encoding process for RTC. int partition_check; + // Use finer quantizer in every other few frames that run variable block + // partition type search. + int force_frame_boost; + + // Maximally allowed base quantization index fluctuation. + int max_delta_qindex; + // Implements various heuristics to skip searching modes // The heuristics selected are based on flags // defined in the MODE_SEARCH_SKIP_HEURISTICS enum @@ -301,6 +308,10 @@ typedef struct { // calculation in the rd coefficient costing loop. int use_fast_coef_costing; + // This feature controls the tolerence vs target used in deciding whether to + // recode a frame. It has no meaning if recode is disabled. + int recode_tolerance; + // This variable controls the maximum block size where intra blocks can be // used in inter frames. // TODO(aconverse): Fold this into one of the other many mode skips diff --git a/vp9/encoder/vp9_temporal_filter.c b/vp9/encoder/vp9_temporal_filter.c index f8a641513..041027354 100644 --- a/vp9/encoder/vp9_temporal_filter.c +++ b/vp9/encoder/vp9_temporal_filter.c @@ -41,7 +41,10 @@ static void temporal_filter_predictors_mb_c(MACROBLOCKD *xd, struct scale_factors *scale, int x, int y) { const int which_mv = 0; - MV mv = { mv_row, mv_col }; + const MV mv = { mv_row, mv_col }; + const InterpKernel *const kernel = + vp9_get_interp_kernel(xd->mi[0]->mbmi.interp_filter); + enum mv_precision mv_precision_uv; int uv_stride; if (uv_block_size == 8) { @@ -58,7 +61,7 @@ static void temporal_filter_predictors_mb_c(MACROBLOCKD *xd, scale, 16, 16, which_mv, - xd->interp_kernel, MV_PRECISION_Q3, x, y); + kernel, MV_PRECISION_Q3, x, y); vp9_build_inter_predictor(u_mb_ptr, uv_stride, &pred[256], uv_block_size, @@ -66,7 +69,7 @@ static void temporal_filter_predictors_mb_c(MACROBLOCKD *xd, scale, uv_block_size, uv_block_size, which_mv, - xd->interp_kernel, mv_precision_uv, x, y); + kernel, mv_precision_uv, x, y); vp9_build_inter_predictor(v_mb_ptr, uv_stride, &pred[512], uv_block_size, @@ -74,7 +77,7 @@ static void temporal_filter_predictors_mb_c(MACROBLOCKD *xd, scale, uv_block_size, uv_block_size, which_mv, - xd->interp_kernel, mv_precision_uv, x, y); + kernel, mv_precision_uv, x, y); } void vp9_temporal_filter_apply_c(uint8_t *frame1, diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index a9fd8c11e..37a214e05 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -37,6 +37,7 @@ struct vp9_extracfg { unsigned int lossless; unsigned int frame_parallel_decoding_mode; AQ_MODE aq_mode; + unsigned int frame_periodic_boost; }; struct extraconfig_map { @@ -65,6 +66,7 @@ static const struct extraconfig_map extracfg_map[] = { 0, // lossless 0, // frame_parallel_decoding_mode NO_AQ, // aq_mode + 0, // frame_periodic_delta_q } } }; @@ -150,7 +152,7 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, RANGE_CHECK_HI(cfg, rc_min_quantizer, cfg->rc_max_quantizer); RANGE_CHECK_BOOL(extra_cfg, lossless); RANGE_CHECK(extra_cfg, aq_mode, 0, AQ_MODE_COUNT - 1); - + RANGE_CHECK(extra_cfg, frame_periodic_boost, 0, 1); RANGE_CHECK_HI(cfg, g_threads, 64); RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_LAG_BUFFERS); RANGE_CHECK(cfg, rc_end_usage, VPX_VBR, VPX_Q); @@ -360,6 +362,8 @@ static vpx_codec_err_t set_vp9e_config(VP9_CONFIG *oxcf, oxcf->aq_mode = extra_cfg->aq_mode; + oxcf->frame_periodic_boost = extra_cfg->frame_periodic_boost; + oxcf->ss_number_layers = cfg->ss_number_layers; if (oxcf->ss_number_layers > 1) { @@ -484,6 +488,7 @@ static vpx_codec_err_t set_param(vpx_codec_alg_priv_t *ctx, int ctrl_id, MAP(VP9E_SET_FRAME_PARALLEL_DECODING, extra_cfg.frame_parallel_decoding_mode); MAP(VP9E_SET_AQ_MODE, extra_cfg.aq_mode); + MAP(VP9E_SET_FRAME_PERIODIC_BOOST, extra_cfg.frame_periodic_boost); } res = validate_config(ctx, &ctx->cfg, &extra_cfg); @@ -1094,6 +1099,7 @@ static vpx_codec_ctrl_fn_map_t vp9e_ctf_maps[] = { {VP9E_SET_LOSSLESS, set_param}, {VP9E_SET_FRAME_PARALLEL_DECODING, set_param}, {VP9E_SET_AQ_MODE, set_param}, + {VP9E_SET_FRAME_PERIODIC_BOOST, set_param}, {VP9_GET_REFERENCE, get_reference}, {VP9E_SET_SVC, vp9e_set_svc}, {VP9E_SET_SVC_PARAMETERS, vp9e_set_svc_parameters}, diff --git a/vp9/vp9_dx_iface.c b/vp9/vp9_dx_iface.c index 271589c6b..3be945480 100644 --- a/vp9/vp9_dx_iface.c +++ b/vp9/vp9_dx_iface.c @@ -23,27 +23,10 @@ #define VP9_CAP_POSTPROC (CONFIG_VP9_POSTPROC ? VPX_CODEC_CAP_POSTPROC : 0) typedef vpx_codec_stream_info_t vp9_stream_info_t; -/* Structures for handling memory allocations */ -typedef enum { - VP9_SEG_ALG_PRIV = 256, - VP9_SEG_MAX -} mem_seg_id_t; -#define NELEMENTS(x) ((int)(sizeof(x)/sizeof(x[0]))) - -static unsigned long priv_sz(const vpx_codec_dec_cfg_t *si, - vpx_codec_flags_t flags); - -static const mem_req_t vp9_mem_req_segs[] = { - {VP9_SEG_ALG_PRIV, 0, 8, VPX_CODEC_MEM_ZERO, priv_sz}, - {VP9_SEG_MAX, 0, 0, 0, NULL} -}; - struct vpx_codec_alg_priv { vpx_codec_priv_t base; - vpx_codec_mmap_t mmaps[NELEMENTS(vp9_mem_req_segs) - 1]; vpx_codec_dec_cfg_t cfg; vp9_stream_info_t si; - int defer_alloc; int decoder_init; struct VP9Decompressor *pbi; int postproc_cfg_set; @@ -66,80 +49,38 @@ struct vpx_codec_alg_priv { vpx_release_frame_buffer_cb_fn_t release_ext_fb_cb; }; -static unsigned long priv_sz(const vpx_codec_dec_cfg_t *si, - vpx_codec_flags_t flags) { - /* Although this declaration is constant, we can't use it in the requested - * segments list because we want to define the requested segments list - * before defining the private type (so that the number of memory maps is - * known) - */ - (void)si; - return sizeof(vpx_codec_alg_priv_t); -} - -static void vp9_init_ctx(vpx_codec_ctx_t *ctx, const vpx_codec_mmap_t *mmap) { - int i; - - ctx->priv = (vpx_codec_priv_t *)mmap->base; - ctx->priv->sz = sizeof(*ctx->priv); - ctx->priv->iface = ctx->iface; - ctx->priv->alg_priv = (struct vpx_codec_alg_priv *)mmap->base; - - for (i = 0; i < NELEMENTS(ctx->priv->alg_priv->mmaps); i++) - ctx->priv->alg_priv->mmaps[i].id = vp9_mem_req_segs[i].id; - - ctx->priv->alg_priv->mmaps[0] = *mmap; - ctx->priv->alg_priv->si.sz = sizeof(ctx->priv->alg_priv->si); - ctx->priv->init_flags = ctx->init_flags; - - if (ctx->config.dec) { - /* Update the reference to the config structure to an internal copy. */ - ctx->priv->alg_priv->cfg = *ctx->config.dec; - ctx->config.dec = &ctx->priv->alg_priv->cfg; - } -} - -static void vp9_finalize_mmaps(vpx_codec_alg_priv_t *ctx) { - /* nothing to clean up */ -} - static vpx_codec_err_t vp9_init(vpx_codec_ctx_t *ctx, vpx_codec_priv_enc_mr_cfg_t *data) { - vpx_codec_err_t res = VPX_CODEC_OK; - // This function only allocates space for the vpx_codec_alg_priv_t // structure. More memory may be required at the time the stream // information becomes known. if (!ctx->priv) { - vpx_codec_mmap_t mmap; - - mmap.id = vp9_mem_req_segs[0].id; - mmap.sz = sizeof(vpx_codec_alg_priv_t); - mmap.align = vp9_mem_req_segs[0].align; - mmap.flags = vp9_mem_req_segs[0].flags; - - res = vpx_mmap_alloc(&mmap); - if (!res) { - vp9_init_ctx(ctx, &mmap); - - ctx->priv->alg_priv->defer_alloc = 1; + void *base = vpx_memalign(32, sizeof(vpx_codec_alg_priv_t)); + if (base == NULL) + return VPX_CODEC_MEM_ERROR; + + memset(base, 0, sizeof(vpx_codec_alg_priv_t)); + ctx->priv = (vpx_codec_priv_t *)base; + ctx->priv->sz = sizeof(*ctx->priv); + ctx->priv->iface = ctx->iface; + ctx->priv->alg_priv = (vpx_codec_alg_priv_t *)base; + ctx->priv->alg_priv->si.sz = sizeof(ctx->priv->alg_priv->si); + ctx->priv->init_flags = ctx->init_flags; + + if (ctx->config.dec) { + // Update the reference to the config structure to an internal copy. + ctx->priv->alg_priv->cfg = *ctx->config.dec; + ctx->config.dec = &ctx->priv->alg_priv->cfg; } } - return res; + return VPX_CODEC_OK; } static vpx_codec_err_t vp9_destroy(vpx_codec_alg_priv_t *ctx) { - int i; - if (ctx->pbi) vp9_remove_decompressor(ctx->pbi); - for (i = NELEMENTS(ctx->mmaps) - 1; i >= 0; i--) { - if (ctx->mmaps[i].dtor) - ctx->mmaps[i].dtor(&ctx->mmaps[i]); - } - return VPX_CODEC_OK; } @@ -231,94 +172,59 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx, ctx->img_avail = 0; - /* Determine the stream parameters. Note that we rely on peek_si to - * validate that we have a buffer that does not wrap around the top - * of the heap. - */ + // Determine the stream parameters. Note that we rely on peek_si to + // validate that we have a buffer that does not wrap around the top + // of the heap. if (!ctx->si.h) res = ctx->base.iface->dec.peek_si(*data, data_sz, &ctx->si); - - /* Perform deferred allocations, if required */ - if (!res && ctx->defer_alloc) { - int i; - - for (i = 1; !res && i < NELEMENTS(ctx->mmaps); i++) { - vpx_codec_dec_cfg_t cfg; - - cfg.w = ctx->si.w; - cfg.h = ctx->si.h; - ctx->mmaps[i].id = vp9_mem_req_segs[i].id; - ctx->mmaps[i].sz = vp9_mem_req_segs[i].sz; - ctx->mmaps[i].align = vp9_mem_req_segs[i].align; - ctx->mmaps[i].flags = vp9_mem_req_segs[i].flags; - - if (!ctx->mmaps[i].sz) - ctx->mmaps[i].sz = vp9_mem_req_segs[i].calc_sz(&cfg, - ctx->base.init_flags); - - res = vpx_mmap_alloc(&ctx->mmaps[i]); - } - - if (!res) - vp9_finalize_mmaps(ctx); - - ctx->defer_alloc = 0; - } - /* Initialize the decoder instance on the first frame*/ if (!res && !ctx->decoder_init) { - res = vpx_validate_mmaps(&ctx->si, ctx->mmaps, - vp9_mem_req_segs, NELEMENTS(vp9_mem_req_segs), - ctx->base.init_flags); - - if (!res) { - VP9D_CONFIG oxcf; - struct VP9Decompressor *optr; - - vp9_initialize_dec(); - - oxcf.width = ctx->si.w; - oxcf.height = ctx->si.h; - oxcf.version = 9; - oxcf.max_threads = ctx->cfg.threads; - oxcf.inv_tile_order = ctx->invert_tile_order; - optr = vp9_create_decompressor(&oxcf); - - // If postprocessing was enabled by the application and a - // configuration has not been provided, default it. - if (!ctx->postproc_cfg_set && - (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC)) { - ctx->postproc_cfg.post_proc_flag = VP8_DEBLOCK | VP8_DEMACROBLOCK; - ctx->postproc_cfg.deblocking_level = 4; - ctx->postproc_cfg.noise_level = 0; - } - - if (!optr) { - res = VPX_CODEC_ERROR; - } else { - VP9D_COMP *const pbi = (VP9D_COMP*)optr; - VP9_COMMON *const cm = &pbi->common; + VP9D_CONFIG oxcf; + struct VP9Decompressor *optr; + + vp9_initialize_dec(); + + oxcf.width = ctx->si.w; + oxcf.height = ctx->si.h; + oxcf.version = 9; + oxcf.max_threads = ctx->cfg.threads; + oxcf.inv_tile_order = ctx->invert_tile_order; + optr = vp9_create_decompressor(&oxcf); + + // If postprocessing was enabled by the application and a + // configuration has not been provided, default it. + if (!ctx->postproc_cfg_set && + (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC)) { + ctx->postproc_cfg.post_proc_flag = VP8_DEBLOCK | VP8_DEMACROBLOCK; + ctx->postproc_cfg.deblocking_level = 4; + ctx->postproc_cfg.noise_level = 0; + } - // Set index to not initialized. - cm->new_fb_idx = -1; + if (!optr) { + res = VPX_CODEC_ERROR; + } else { + VP9D_COMP *const pbi = (VP9D_COMP*)optr; + VP9_COMMON *const cm = &pbi->common; - if (ctx->get_ext_fb_cb != NULL && ctx->release_ext_fb_cb != NULL) { - cm->get_fb_cb = ctx->get_ext_fb_cb; - cm->release_fb_cb = ctx->release_ext_fb_cb; - cm->cb_priv = ctx->ext_priv; - } else { - cm->get_fb_cb = vp9_get_frame_buffer; - cm->release_fb_cb = vp9_release_frame_buffer; + // Set index to not initialized. + cm->new_fb_idx = -1; - if (vp9_alloc_internal_frame_buffers(&cm->int_frame_buffers)) - vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, - "Failed to initialize internal frame buffers"); - cm->cb_priv = &cm->int_frame_buffers; - } + if (ctx->get_ext_fb_cb != NULL && ctx->release_ext_fb_cb != NULL) { + cm->get_fb_cb = ctx->get_ext_fb_cb; + cm->release_fb_cb = ctx->release_ext_fb_cb; + cm->cb_priv = ctx->ext_priv; + } else { + cm->get_fb_cb = vp9_get_frame_buffer; + cm->release_fb_cb = vp9_release_frame_buffer; - ctx->pbi = optr; + if (vp9_alloc_internal_frame_buffers(&cm->int_frame_buffers)) + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to initialize internal frame buffers"); + cm->cb_priv = &cm->int_frame_buffers; } + + ctx->pbi = optr; } ctx->decoder_init = 1; @@ -496,75 +402,6 @@ static vpx_codec_err_t vp9_set_fb_fn( return VPX_CODEC_ERROR; } -static vpx_codec_err_t vp9_xma_get_mmap(const vpx_codec_ctx_t *ctx, - vpx_codec_mmap_t *mmap, - vpx_codec_iter_t *iter) { - vpx_codec_err_t res; - const mem_req_t *seg_iter = (const mem_req_t *)*iter; - - /* Get address of next segment request */ - do { - if (!seg_iter) - seg_iter = vp9_mem_req_segs; - else if (seg_iter->id != VP9_SEG_MAX) - seg_iter++; - - *iter = (vpx_codec_iter_t)seg_iter; - - if (seg_iter->id != VP9_SEG_MAX) { - mmap->id = seg_iter->id; - mmap->sz = seg_iter->sz; - mmap->align = seg_iter->align; - mmap->flags = seg_iter->flags; - - if (!seg_iter->sz) - mmap->sz = seg_iter->calc_sz(ctx->config.dec, ctx->init_flags); - - res = VPX_CODEC_OK; - } else { - res = VPX_CODEC_LIST_END; - } - } while (!mmap->sz && res != VPX_CODEC_LIST_END); - - return res; -} - -static vpx_codec_err_t vp9_xma_set_mmap(vpx_codec_ctx_t *ctx, - const vpx_codec_mmap_t *mmap) { - vpx_codec_err_t res = VPX_CODEC_MEM_ERROR; - int i, done; - - if (!ctx->priv) { - if (mmap->id == VP9_SEG_ALG_PRIV) { - if (!ctx->priv) { - vp9_init_ctx(ctx, mmap); - res = VPX_CODEC_OK; - } - } - } - - done = 1; - - if (!res && ctx->priv->alg_priv) { - for (i = 0; i < NELEMENTS(ctx->priv->alg_priv->mmaps); i++) { - if (ctx->priv->alg_priv->mmaps[i].id == mmap->id) - if (!ctx->priv->alg_priv->mmaps[i].base) { - ctx->priv->alg_priv->mmaps[i] = *mmap; - res = VPX_CODEC_OK; - } - - done &= (ctx->priv->alg_priv->mmaps[i].base != NULL); - } - } - - if (done && !res) { - vp9_finalize_mmaps(ctx->priv->alg_priv); - res = ctx->iface->init(ctx, NULL); - } - - return res; -} - static vpx_codec_err_t set_reference(vpx_codec_alg_priv_t *ctx, int ctr_id, va_list args) { vpx_ref_frame_t *const data = va_arg(args, vpx_ref_frame_t *); @@ -735,8 +572,8 @@ CODEC_INTERFACE(vpx_codec_vp9_dx) = { vp9_init, /* vpx_codec_init_fn_t init; */ vp9_destroy, /* vpx_codec_destroy_fn_t destroy; */ ctf_maps, /* vpx_codec_ctrl_fn_map_t *ctrl_maps; */ - vp9_xma_get_mmap, /* vpx_codec_get_mmap_fn_t get_mmap; */ - vp9_xma_set_mmap, /* vpx_codec_set_mmap_fn_t set_mmap; */ + NOT_IMPLEMENTED, /* vpx_codec_get_mmap_fn_t get_mmap; */ + NOT_IMPLEMENTED, /* vpx_codec_set_mmap_fn_t set_mmap; */ { // NOLINT vp9_peek_si, /* vpx_codec_peek_si_fn_t peek_si; */ vp9_get_si, /* vpx_codec_get_si_fn_t get_si; */ diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h index 0b637d42f..8944a2664 100644 --- a/vpx/vp8cx.h +++ b/vpx/vp8cx.h @@ -192,6 +192,7 @@ enum vp8e_enc_control_id { VP9E_SET_TILE_ROWS, VP9E_SET_FRAME_PARALLEL_DECODING, VP9E_SET_AQ_MODE, + VP9E_SET_FRAME_PERIODIC_BOOST, VP9E_SET_SVC, VP9E_SET_SVC_PARAMETERS, @@ -364,6 +365,8 @@ VPX_CTRL_USE_TYPE(VP9E_SET_FRAME_PARALLEL_DECODING, unsigned int) VPX_CTRL_USE_TYPE(VP9E_SET_AQ_MODE, unsigned int) +VPX_CTRL_USE_TYPE(VP9E_SET_FRAME_PERIODIC_BOOST, unsigned int) + /*! @} - end defgroup vp8_encoder */ #ifdef __cplusplus } // extern "C" @@ -123,6 +123,55 @@ int fourcc_is_ivf(const char detect[4]) { return 0; } +#if CONFIG_WEBM_IO +/* Murmur hash derived from public domain reference implementation at + * http:// sites.google.com/site/murmurhash/ + */ +static unsigned int murmur(const void *key, int len, unsigned int seed) { + const unsigned int m = 0x5bd1e995; + const int r = 24; + + unsigned int h = seed ^ len; + + const unsigned char *data = (const unsigned char *)key; + + while (len >= 4) { + unsigned int k; + + k = (unsigned int)data[0]; + k |= (unsigned int)data[1] << 8; + k |= (unsigned int)data[2] << 16; + k |= (unsigned int)data[3] << 24; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + switch (len) { + case 3: + h ^= data[2] << 16; + case 2: + h ^= data[1] << 8; + case 1: + h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} +#endif // CONFIG_WEBM_IO + static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, "Debug mode (makes output deterministic)"); static const arg_def_t outputfile = ARG_DEF("o", "output", 1, @@ -351,13 +400,17 @@ static const arg_def_t frame_parallel_decoding = ARG_DEF( NULL, "frame-parallel", 1, "Enable frame parallel decodability features"); static const arg_def_t aq_mode = ARG_DEF( NULL, "aq-mode", 1, - "Adaptive q mode (0: off (by default), 1: variance 2: complexity)"); + "Adaptive q mode (0: off (by default), 1: variance 2: complexity, " + "3: cyclic refresh)"); +static const arg_def_t frame_periodic_boost = ARG_DEF( + NULL, "frame_boost", 1, + "Enable frame periodic boost (0: off (by default), 1: on)"); static const arg_def_t *vp9_args[] = { &cpu_used, &auto_altref, &noise_sens, &sharpness, &static_thresh, &tile_cols, &tile_rows, &arnr_maxframes, &arnr_strength, &arnr_type, &tune_ssim, &cq_level, &max_intra_rate_pct, &lossless, - &frame_parallel_decoding, &aq_mode, + &frame_parallel_decoding, &aq_mode, &frame_periodic_boost, NULL }; static const int vp9_arg_ctrl_map[] = { @@ -367,6 +420,7 @@ static const int vp9_arg_ctrl_map[] = { VP8E_SET_ARNR_MAXFRAMES, VP8E_SET_ARNR_STRENGTH, VP8E_SET_ARNR_TYPE, VP8E_SET_TUNING, VP8E_SET_CQ_LEVEL, VP8E_SET_MAX_INTRA_BITRATE_PCT, VP9E_SET_LOSSLESS, VP9E_SET_FRAME_PARALLEL_DECODING, VP9E_SET_AQ_MODE, + VP9E_SET_FRAME_PERIODIC_BOOST, 0 }; #endif @@ -565,6 +619,7 @@ struct stream_state { FILE *file; struct rate_hist *rate_hist; struct EbmlGlobal ebml; + uint32_t hash; uint64_t psnr_sse_total; uint64_t psnr_samples_total; double psnr_totals[4]; @@ -786,9 +841,7 @@ static struct stream_state *new_stream(struct VpxEncoderConfig *global, stream->config.stereo_fmt = STEREO_FORMAT_MONO; stream->config.write_webm = 1; #if CONFIG_WEBM_IO - stream->ebml.last_pts_ns = -1; - stream->ebml.writer = NULL; - stream->ebml.segment = NULL; + stream->ebml.last_pts_ms = -1; #endif /* Allows removal of the application version from the EBML tags */ @@ -1123,7 +1176,9 @@ static void close_output_file(struct stream_state *stream, #if CONFIG_WEBM_IO if (stream->config.write_webm) { - write_webm_file_footer(&stream->ebml); + write_webm_file_footer(&stream->ebml, stream->hash); + free(stream->ebml.cue_list); + stream->ebml.cue_list = NULL; } #endif @@ -1279,6 +1334,12 @@ static void get_cx_data(struct stream_state *stream, update_rate_histogram(stream->rate_hist, cfg, pkt); #if CONFIG_WEBM_IO if (stream->config.write_webm) { + /* Update the hash */ + if (!stream->ebml.debug) + stream->hash = murmur(pkt->data.frame.buf, + (int)pkt->data.frame.sz, + stream->hash); + write_webm_block(&stream->ebml, cfg, pkt); } #endif diff --git a/webmenc.c b/webmenc.c new file mode 100644 index 000000000..17bbeec78 --- /dev/null +++ b/webmenc.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2013 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 "webmenc.h" + +#include <limits.h> +#include <string.h> + +#include "third_party/libmkv/EbmlWriter.h" +#include "third_party/libmkv/EbmlIDs.h" + +void Ebml_Write(struct EbmlGlobal *glob, + const void *buffer_in, + unsigned long len) { + (void) fwrite(buffer_in, 1, len, glob->stream); +} + +#define WRITE_BUFFER(s) \ +for (i = len - 1; i >= 0; i--) { \ + x = (char)(*(const s *)buffer_in >> (i * CHAR_BIT)); \ + Ebml_Write(glob, &x, 1); \ +} + +void Ebml_Serialize(struct EbmlGlobal *glob, + const void *buffer_in, + int buffer_size, + unsigned long len) { + char x; + int i; + + /* buffer_size: + * 1 - int8_t; + * 2 - int16_t; + * 3 - int32_t; + * 4 - int64_t; + */ + switch (buffer_size) { + case 1: + WRITE_BUFFER(int8_t) + break; + case 2: + WRITE_BUFFER(int16_t) + break; + case 4: + WRITE_BUFFER(int32_t) + break; + case 8: + WRITE_BUFFER(int64_t) + break; + default: + break; + } +} +#undef WRITE_BUFFER + +/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit + * one, but not a 32 bit one. + */ +static void Ebml_SerializeUnsigned32(struct EbmlGlobal *glob, + unsigned int class_id, + uint64_t ui) { + const unsigned char sizeSerialized = 4 | 0x80; + Ebml_WriteID(glob, class_id); + Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1); + Ebml_Serialize(glob, &ui, sizeof(ui), 4); +} + +static void Ebml_StartSubElement(struct EbmlGlobal *glob, + EbmlLoc *ebmlLoc, + unsigned int class_id) { + const uint64_t kEbmlUnknownLength = LITERALU64(0x01FFFFFF, 0xFFFFFFFF); + Ebml_WriteID(glob, class_id); + *ebmlLoc = ftello(glob->stream); + Ebml_Serialize(glob, &kEbmlUnknownLength, sizeof(kEbmlUnknownLength), 8); +} + +static void Ebml_EndSubElement(struct EbmlGlobal *glob, EbmlLoc *ebmlLoc) { + off_t pos; + uint64_t size; + + /* Save the current stream pointer. */ + pos = ftello(glob->stream); + + /* Calculate the size of this element. */ + size = pos - *ebmlLoc - 8; + size |= LITERALU64(0x01000000, 0x00000000); + + /* Seek back to the beginning of the element and write the new size. */ + fseeko(glob->stream, *ebmlLoc, SEEK_SET); + Ebml_Serialize(glob, &size, sizeof(size), 8); + + /* Reset the stream pointer. */ + fseeko(glob->stream, pos, SEEK_SET); +} + +void write_webm_seek_element(struct EbmlGlobal *ebml, + unsigned int id, + off_t pos) { + uint64_t offset = pos - ebml->position_reference; + EbmlLoc start; + Ebml_StartSubElement(ebml, &start, Seek); + Ebml_SerializeBinary(ebml, SeekID, id); + Ebml_SerializeUnsigned64(ebml, SeekPosition, offset); + Ebml_EndSubElement(ebml, &start); +} + +void write_webm_seek_info(struct EbmlGlobal *ebml) { + off_t pos; + EbmlLoc start; + EbmlLoc startInfo; + uint64_t frame_time; + char version_string[64]; + + /* Save the current stream pointer. */ + pos = ftello(ebml->stream); + + if (ebml->seek_info_pos) + fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET); + else + ebml->seek_info_pos = pos; + + Ebml_StartSubElement(ebml, &start, SeekHead); + write_webm_seek_element(ebml, Tracks, ebml->track_pos); + write_webm_seek_element(ebml, Cues, ebml->cue_pos); + write_webm_seek_element(ebml, Info, ebml->segment_info_pos); + Ebml_EndSubElement(ebml, &start); + + /* Create and write the Segment Info. */ + if (ebml->debug) { + strcpy(version_string, "vpxenc"); + } else { + strcpy(version_string, "vpxenc "); + strncat(version_string, + vpx_codec_version_str(), + sizeof(version_string) - 1 - strlen(version_string)); + } + + frame_time = (uint64_t)1000 * ebml->framerate.den + / ebml->framerate.num; + ebml->segment_info_pos = ftello(ebml->stream); + Ebml_StartSubElement(ebml, &startInfo, Info); + Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000); + Ebml_SerializeFloat(ebml, Segment_Duration, + (double)(ebml->last_pts_ms + frame_time)); + Ebml_SerializeString(ebml, 0x4D80, version_string); + Ebml_SerializeString(ebml, 0x5741, version_string); + Ebml_EndSubElement(ebml, &startInfo); +} + +void write_webm_file_header(struct EbmlGlobal *glob, + const vpx_codec_enc_cfg_t *cfg, + const struct vpx_rational *fps, + stereo_format_t stereo_fmt, + unsigned int fourcc) { + EbmlLoc start; + EbmlLoc trackStart; + EbmlLoc videoStart; + unsigned int trackNumber = 1; + uint64_t trackID = 0; + unsigned int pixelWidth = cfg->g_w; + unsigned int pixelHeight = cfg->g_h; + + /* Write the EBML header. */ + Ebml_StartSubElement(glob, &start, EBML); + Ebml_SerializeUnsigned(glob, EBMLVersion, 1); + Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1); + Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4); + Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8); + Ebml_SerializeString(glob, DocType, "webm"); + Ebml_SerializeUnsigned(glob, DocTypeVersion, 2); + Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2); + Ebml_EndSubElement(glob, &start); + + /* Open and begin writing the segment element. */ + Ebml_StartSubElement(glob, &glob->startSegment, Segment); + glob->position_reference = ftello(glob->stream); + glob->framerate = *fps; + write_webm_seek_info(glob); + + /* Open and write the Tracks element. */ + glob->track_pos = ftello(glob->stream); + Ebml_StartSubElement(glob, &trackStart, Tracks); + + /* Open and write the Track entry. */ + Ebml_StartSubElement(glob, &start, TrackEntry); + Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber); + glob->track_id_pos = ftello(glob->stream); + Ebml_SerializeUnsigned32(glob, TrackUID, trackID); + Ebml_SerializeUnsigned(glob, TrackType, 1); + Ebml_SerializeString(glob, CodecID, + fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9"); + Ebml_StartSubElement(glob, &videoStart, Video); + Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth); + Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight); + Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt); + Ebml_EndSubElement(glob, &videoStart); + + /* Close Track entry. */ + Ebml_EndSubElement(glob, &start); + + /* Close Tracks element. */ + Ebml_EndSubElement(glob, &trackStart); + + /* Segment element remains open. */ +} + +void write_webm_block(struct EbmlGlobal *glob, + const vpx_codec_enc_cfg_t *cfg, + const vpx_codec_cx_pkt_t *pkt) { + unsigned int block_length; + unsigned char track_number; + uint16_t block_timecode = 0; + unsigned char flags; + int64_t pts_ms; + int start_cluster = 0, is_keyframe; + + /* Calculate the PTS of this frame in milliseconds. */ + pts_ms = pkt->data.frame.pts * 1000 + * (uint64_t)cfg->g_timebase.num / (uint64_t)cfg->g_timebase.den; + + if (pts_ms <= glob->last_pts_ms) + pts_ms = glob->last_pts_ms + 1; + + glob->last_pts_ms = pts_ms; + + /* Calculate the relative time of this block. */ + if (pts_ms - glob->cluster_timecode > SHRT_MAX) + start_cluster = 1; + else + block_timecode = (uint16_t)pts_ms - glob->cluster_timecode; + + is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); + if (start_cluster || is_keyframe) { + if (glob->cluster_open) + Ebml_EndSubElement(glob, &glob->startCluster); + + /* Open the new cluster. */ + block_timecode = 0; + glob->cluster_open = 1; + glob->cluster_timecode = (uint32_t)pts_ms; + glob->cluster_pos = ftello(glob->stream); + Ebml_StartSubElement(glob, &glob->startCluster, Cluster); + Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode); + + /* Save a cue point if this is a keyframe. */ + if (is_keyframe) { + struct cue_entry *cue, *new_cue_list; + + new_cue_list = realloc(glob->cue_list, + (glob->cues + 1) * sizeof(struct cue_entry)); + if (new_cue_list) + glob->cue_list = new_cue_list; + else + fatal("Failed to realloc cue list."); + + cue = &glob->cue_list[glob->cues]; + cue->time = glob->cluster_timecode; + cue->loc = glob->cluster_pos; + glob->cues++; + } + } + + /* Write the Simple Block. */ + Ebml_WriteID(glob, SimpleBlock); + + block_length = (unsigned int)pkt->data.frame.sz + 4; + block_length |= 0x10000000; + Ebml_Serialize(glob, &block_length, sizeof(block_length), 4); + + track_number = 1; + track_number |= 0x80; + Ebml_Write(glob, &track_number, 1); + + Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2); + + flags = 0; + if (is_keyframe) + flags |= 0x80; + if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) + flags |= 0x08; + Ebml_Write(glob, &flags, 1); + + Ebml_Write(glob, pkt->data.frame.buf, (unsigned int)pkt->data.frame.sz); +} + +void write_webm_file_footer(struct EbmlGlobal *glob, int hash) { + EbmlLoc start_cues; + EbmlLoc start_cue_point; + EbmlLoc start_cue_tracks; + unsigned int i; + + if (glob->cluster_open) + Ebml_EndSubElement(glob, &glob->startCluster); + + glob->cue_pos = ftello(glob->stream); + Ebml_StartSubElement(glob, &start_cues, Cues); + + for (i = 0; i < glob->cues; i++) { + struct cue_entry *cue = &glob->cue_list[i]; + Ebml_StartSubElement(glob, &start_cue_point, CuePoint); + Ebml_SerializeUnsigned(glob, CueTime, cue->time); + + Ebml_StartSubElement(glob, &start_cue_tracks, CueTrackPositions); + Ebml_SerializeUnsigned(glob, CueTrack, 1); + Ebml_SerializeUnsigned64(glob, CueClusterPosition, + cue->loc - glob->position_reference); + Ebml_EndSubElement(glob, &start_cue_tracks); + + Ebml_EndSubElement(glob, &start_cue_point); + } + + Ebml_EndSubElement(glob, &start_cues); + + /* Close the Segment. */ + Ebml_EndSubElement(glob, &glob->startSegment); + + /* Patch up the seek info block. */ + write_webm_seek_info(glob); + + /* Patch up the track id. */ + fseeko(glob->stream, glob->track_id_pos, SEEK_SET); + Ebml_SerializeUnsigned32(glob, TrackUID, glob->debug ? 0xDEADBEEF : hash); + + fseeko(glob->stream, 0, SEEK_END); +} diff --git a/webmenc.cc b/webmenc.cc deleted file mode 100644 index 6a3374d02..000000000 --- a/webmenc.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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. - */ -#include "./webmenc.h" - -#include <string> - -#include "third_party/libwebm/mkvmuxer.hpp" -#include "third_party/libwebm/mkvmuxerutil.hpp" -#include "third_party/libwebm/mkvwriter.hpp" - -namespace { -const uint64_t kDebugTrackUid = 0xDEADBEEF; -const int kVideoTrackNumber = 1; -} // namespace - -void write_webm_file_header(struct EbmlGlobal *glob, - const vpx_codec_enc_cfg_t *cfg, - const struct vpx_rational *fps, - stereo_format_t stereo_fmt, - unsigned int fourcc) { - mkvmuxer::MkvWriter *const writer = new mkvmuxer::MkvWriter(glob->stream); - mkvmuxer::Segment *const segment = new mkvmuxer::Segment(); - segment->Init(writer); - segment->set_mode(mkvmuxer::Segment::kFile); - segment->OutputCues(true); - - mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo(); - const uint64_t kTimecodeScale = 1000000; - info->set_timecode_scale(kTimecodeScale); - std::string version = "vpxenc"; - if (!glob->debug) { - version.append(std::string(" ") + vpx_codec_version_str()); - } - info->set_writing_app(version.c_str()); - - const int video_track_id = segment->AddVideoTrack(static_cast<int>(cfg->g_w), - static_cast<int>(cfg->g_h), - kVideoTrackNumber); - mkvmuxer::VideoTrack* const video_track = - static_cast<mkvmuxer::VideoTrack*>( - segment->GetTrackByNumber(video_track_id)); - video_track->SetStereoMode(stereo_fmt); - video_track->set_codec_id(fourcc == VP8_FOURCC ? "V_VP8" : "V_VP9"); - if (glob->debug) { - video_track->set_uid(kDebugTrackUid); - } - glob->writer = writer; - glob->segment = segment; -} - -void write_webm_block(struct EbmlGlobal *glob, - const vpx_codec_enc_cfg_t *cfg, - const vpx_codec_cx_pkt_t *pkt) { - mkvmuxer::Segment *const segment = - reinterpret_cast<mkvmuxer::Segment*>(glob->segment); - int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * - cfg->g_timebase.num / cfg->g_timebase.den; - if (pts_ns <= glob->last_pts_ns) - pts_ns = glob->last_pts_ns + 1000000; - glob->last_pts_ns = pts_ns; - - segment->AddFrame(static_cast<uint8_t*>(pkt->data.frame.buf), - pkt->data.frame.sz, - kVideoTrackNumber, - pts_ns, - pkt->data.frame.flags & VPX_FRAME_IS_KEY); -} - -void write_webm_file_footer(struct EbmlGlobal *glob) { - mkvmuxer::MkvWriter *const writer = - reinterpret_cast<mkvmuxer::MkvWriter*>(glob->writer); - mkvmuxer::Segment *const segment = - reinterpret_cast<mkvmuxer::Segment*>(glob->segment); - segment->Finalize(); - delete segment; - delete writer; - glob->writer = NULL; - glob->segment = NULL; -} @@ -13,6 +13,13 @@ #include <stdio.h> #include <stdlib.h> +#if defined(_MSC_VER) +/* MSVS doesn't define off_t */ +typedef __int64 off_t; +#else +#include <stdint.h> +#endif + #include "tools_common.h" #include "vpx/vpx_encoder.h" @@ -20,13 +27,40 @@ extern "C" { #endif -/* TODO(vigneshv): Rename this struct */ +typedef off_t EbmlLoc; + +struct cue_entry { + unsigned int time; + uint64_t loc; +}; + struct EbmlGlobal { int debug; + FILE *stream; - int64_t last_pts_ns; - void *writer; - void *segment; + int64_t last_pts_ms; + vpx_rational_t framerate; + + /* These pointers are to the start of an element */ + off_t position_reference; + off_t seek_info_pos; + off_t segment_info_pos; + off_t track_pos; + off_t cue_pos; + off_t cluster_pos; + + /* This pointer is to a specific element to be serialized */ + off_t track_id_pos; + + /* These pointers are to the size field of the element */ + EbmlLoc startSegment; + EbmlLoc startCluster; + + uint32_t cluster_timecode; + int cluster_open; + + struct cue_entry *cue_list; + unsigned int cues; }; /* Stereo 3D packed frame format */ @@ -38,6 +72,10 @@ typedef enum stereo_format { STEREO_FORMAT_RIGHT_LEFT = 11 } stereo_format_t; +void write_webm_seek_element(struct EbmlGlobal *ebml, + unsigned int id, + off_t pos); + void write_webm_file_header(struct EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const struct vpx_rational *fps, @@ -48,7 +86,7 @@ void write_webm_block(struct EbmlGlobal *glob, const vpx_codec_enc_cfg_t *cfg, const vpx_codec_cx_pkt_t *pkt); -void write_webm_file_footer(struct EbmlGlobal *glob); +void write_webm_file_footer(struct EbmlGlobal *glob, int hash); #ifdef __cplusplus } // extern "C" |