(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2019 FakeFish Games
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,16 @@
|
||||
## webm_mem_playback - simple webm video playback
|
||||
|
||||
webm_mem_playback (FakeFish Games) is available under the
|
||||
BSD 3-clause license - see LICENSE_webm_mem_playback.txt
|
||||
for more details.
|
||||
|
||||
libvpx (The WebM Project authors) is available under the
|
||||
BSD 3-clause license - see libvpx/LICENSE for more details.
|
||||
|
||||
libwebm (Google Inc) is available under the BSD 3-clause
|
||||
license - see libwebm/LICENSE for more details.
|
||||
|
||||
opus (Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin,
|
||||
Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding,
|
||||
Erik de Castro Lopo) is available under the BSD 3-clause
|
||||
license - see opus/COPYING for more details.
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p libvpx_x64_linux
|
||||
cp -rf libvpx/. libvpx_x64_linux/
|
||||
cd libvpx_x64_linux/
|
||||
./configure --target=x86_64-linux-gcc --enable-optimizations --enable-pic --disable-shared --disable-install-bins --disable-install-libs --disable-examples --disable-docs --enable-webm-io --disable-vp8-encoder --disable-vp9-encoder
|
||||
cd ..
|
||||
|
||||
mkdir -p libwebm_x64_linux
|
||||
cp -rf libwebm/. libwebm_x64_linux/
|
||||
cd libwebm_x64_linux/
|
||||
cmake -G "Unix Makefiles" -DCMAKE_CXX_FLAGS="-fPIC -O3" -DCMAKE_C_FLAGS="-fPIC -O3"
|
||||
cd ..
|
||||
|
||||
mkdir -p opus_x64_linux
|
||||
cp -rf opus/. opus_x64_linux/
|
||||
cd opus_x64_linux/
|
||||
./configure --enable-asm --enable-intrinsics --enable-float-approx --enable-static --disable-shared --with-pic
|
||||
cd ..
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "REQUIREMENTS:"
|
||||
echo " - NASM"
|
||||
echo " - You download that somewhere. If you have homebrew installed you can install it from there."
|
||||
echo " - https://cmake.org/download/"
|
||||
echo ""
|
||||
echo "IMPORTANT NOTE: libwebm_x64_macos will generate a file called \"Makefile.unix\", rename that to \"Makefile\" to call \"make\" on the folder."
|
||||
echo ""
|
||||
echo "ANOTHER IMPORTANT NOTE: The Opus makefile puts the .a file in a hidden folder called \".libs\""
|
||||
echo ""
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "Creating libvpx Makefile..."
|
||||
echo ""
|
||||
|
||||
mkdir -p libvpx_x64_macos
|
||||
cp -rf libvpx/. libvpx_x64_macos/
|
||||
cd libvpx_x64_macos/
|
||||
chmod +x ./configure
|
||||
./configure --target=x86_64-darwin17-gcc --enable-optimizations --enable-pic --disable-shared --disable-install-bins --disable-install-libs --disable-examples --disable-docs --enable-webm-io --disable-vp8-encoder --disable-vp9-encoder
|
||||
cd ..
|
||||
|
||||
echo "Creating libwebm Makefile..."
|
||||
echo ""
|
||||
|
||||
mkdir -p libwebm_x64_macos
|
||||
cp -rf libwebm/. libwebm_x64_macos/
|
||||
cd libwebm_x64_macos/
|
||||
cmake -G "Unix Makefiles" -DCMAKE_CXX_FLAGS="-fPIC -O3" -DCMAKE_C_FLAGS="-fPIC -O3"
|
||||
cd ..
|
||||
|
||||
echo "Creating opus Makefile..."
|
||||
echo ""
|
||||
|
||||
mkdir -p opus_x64_macos
|
||||
cp -rf opus/. opus_x64_macos/
|
||||
cd opus_x64_macos/
|
||||
chmod +x ./configure
|
||||
./configure --enable-asm --enable-intrinsics --enable-float-approx --enable-static --disable-shared --with-pic
|
||||
cd ..
|
||||
|
||||
echo "Done"
|
||||
echo ""
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "REQUIREMENTS:"
|
||||
echo " - https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/"
|
||||
echo " - You won't need MinGW itself, just get MSYS"
|
||||
echo " - Install msys-perl-bin afterwards"
|
||||
echo " - http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe"
|
||||
echo " - Drag into [VS2019 Install Dir]/Common7/Tools and rename to yasm.exe"
|
||||
echo " - https://www.microsoft.com/en-us/download/details.aspx?id=13523"
|
||||
echo " - The VS2010 C++ redist is required for yasm; output files will not require it"
|
||||
echo " - https://cmake.org/download/ (I'm so sorry)"
|
||||
echo ""
|
||||
|
||||
mkdir -p libvpx_x64_vs15
|
||||
cp -rf libvpx/. libvpx_x64_vs15/
|
||||
cd libvpx_x64_vs15/
|
||||
./configure --target=x86_64-win64-vs15 --enable-static-msvcrt --disable-static --disable-install-bins --disable-install-libs --disable-examples --disable-docs --enable-webm-io --disable-vp8-encoder --disable-vp9-encoder
|
||||
make
|
||||
cd ..
|
||||
|
||||
mkdir -p libvpx_x86_vs15
|
||||
cp -rf libvpx/. libvpx_x86_vs15/
|
||||
cd libvpx_x86_vs15/
|
||||
./configure --target=x86-win32-vs15 --enable-static-msvcrt --disable-static --disable-install-bins --disable-install-libs --disable-examples --disable-docs --enable-webm-io --disable-vp8-encoder --disable-vp9-encoder
|
||||
make
|
||||
cd ..
|
||||
|
||||
mkdir -p libwebm_x64_vs19
|
||||
cp -rf libwebm/. libwebm_x64_vs19/
|
||||
cd libwebm_x64_vs19/
|
||||
cmake -G "Visual Studio 16 2019" -A x64 ./
|
||||
cd ..
|
||||
|
||||
mkdir -p libwebm_x86_vs19
|
||||
cp -rf libwebm/. libwebm_x86_vs19/
|
||||
cd libwebm_x86_vs19/
|
||||
cmake -G "Visual Studio 16 2019" ./
|
||||
cd ..
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
# Generated with clang-format 7.0.1
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Right
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
configure eol=lf
|
||||
*.sh eol=lf
|
||||
@@ -0,0 +1,69 @@
|
||||
*.S
|
||||
*.a
|
||||
*.asm.s
|
||||
*.d
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.o
|
||||
*~
|
||||
.cproject
|
||||
.project
|
||||
.settings
|
||||
/*-*.mk
|
||||
/*.asm
|
||||
/*.doxy
|
||||
/*.ivf
|
||||
/*.ivf.md5
|
||||
/.bins
|
||||
/.deps
|
||||
/.docs
|
||||
/.install-*
|
||||
/.libs
|
||||
/Makefile
|
||||
/arm_neon.h
|
||||
/config.log
|
||||
/config.mk
|
||||
/docs/
|
||||
/doxyfile
|
||||
/examples/*.dox
|
||||
/examples/decode_to_md5
|
||||
/examples/decode_with_drops
|
||||
/examples/decode_with_partial_drops
|
||||
/examples/example_xma
|
||||
/examples/postproc
|
||||
/examples/resize_util
|
||||
/examples/set_maps
|
||||
/examples/simple_decoder
|
||||
/examples/simple_encoder
|
||||
/examples/twopass_encoder
|
||||
/examples/vp8_multi_resolution_encoder
|
||||
/examples/vp8cx_set_ref
|
||||
/examples/vp9cx_set_ref
|
||||
/examples/vp9_lossless_encoder
|
||||
/examples/vp9_spatial_svc_encoder
|
||||
/examples/vpx_temporal_svc_encoder
|
||||
/ivfdec
|
||||
/ivfdec.dox
|
||||
/ivfenc
|
||||
/ivfenc.dox
|
||||
/libvpx.so*
|
||||
/libvpx.ver
|
||||
/samples.dox
|
||||
/test_intra_pred_speed
|
||||
/test_libvpx
|
||||
/tools.dox
|
||||
/tools/*.dox
|
||||
/tools/tiny_ssim
|
||||
/vp8_api1_migration.dox
|
||||
/vp[89x]_rtcd.h
|
||||
/vpx.pc
|
||||
/vpx_config.c
|
||||
/vpx_config.h
|
||||
/vpx_dsp_rtcd.h
|
||||
/vpx_scale_rtcd.h
|
||||
/vpx_version.h
|
||||
/vpxdec
|
||||
/vpxdec.dox
|
||||
/vpxenc
|
||||
/vpxenc.dox
|
||||
TAGS
|
||||
@@ -0,0 +1,50 @@
|
||||
Adrian Grange <agrange@google.com>
|
||||
Aℓex Converse <aconverse@google.com>
|
||||
Aℓex Converse <aconverse@google.com> <alex.converse@gmail.com>
|
||||
Alexis Ballier <aballier@gentoo.org> <alexis.ballier@gmail.com>
|
||||
Alpha Lam <hclam@google.com> <hclam@chromium.org>
|
||||
Chris Cunningham <chcunningham@chromium.org>
|
||||
Chi Yo Tsai <chiyotsai@google.com>
|
||||
Daniele Castagna <dcastagna@chromium.org> <dcastagna@google.com>
|
||||
Deb Mukherjee <debargha@google.com>
|
||||
Elliott Karpilovsky <elliottk@google.com>
|
||||
Erik Niemeyer <erik.a.niemeyer@intel.com> <erik.a.niemeyer@gmail.com>
|
||||
Fyodor Kyslov <kyslov@google.com>
|
||||
Guillaume Martres <gmartres@google.com> <smarter3@gmail.com>
|
||||
Hangyu Kuang <hkuang@google.com>
|
||||
Hui Su <huisu@google.com>
|
||||
Jacky Chen <jackychen@google.com>
|
||||
Jim Bankoski <jimbankoski@google.com>
|
||||
Johann Koenig <johannkoenig@google.com>
|
||||
Johann Koenig <johannkoenig@google.com> <johann.koenig@duck.com>
|
||||
Johann Koenig <johannkoenig@google.com> <johann.koenig@gmail.com>
|
||||
Johann Koenig <johannkoenig@google.com> <johannkoenig@chromium.org>
|
||||
John Koleszar <jkoleszar@google.com>
|
||||
Joshua Litt <joshualitt@google.com> <joshualitt@chromium.org>
|
||||
Marco Paniconi <marpan@google.com>
|
||||
Marco Paniconi <marpan@google.com> <marpan@chromium.org>
|
||||
Martin Storsjö <martin@martin.st>
|
||||
Michael Horowitz <mhoro@webrtc.org> <mhoro@google.com>
|
||||
Pascal Massimino <pascal.massimino@gmail.com>
|
||||
Paul Wilkins <paulwilkins@google.com>
|
||||
Peter Boström <pbos@chromium.org> <pbos@google.com>
|
||||
Peter de Rivaz <peter.derivaz@gmail.com>
|
||||
Peter de Rivaz <peter.derivaz@gmail.com> <peter.derivaz@argondesign.com>
|
||||
Ralph Giles <giles@xiph.org> <giles@entropywave.com>
|
||||
Ralph Giles <giles@xiph.org> <giles@mozilla.com>
|
||||
Ronald S. Bultje <rsbultje@gmail.com> <rbultje@google.com>
|
||||
Sai Deng <sdeng@google.com>
|
||||
Sami Pietilä <samipietila@google.com>
|
||||
Shiyou Yin <yinshiyou-hf@loongson.cn>
|
||||
Tamar Levy <tamar.levy@intel.com>
|
||||
Tamar Levy <tamar.levy@intel.com> <levytamar82@gmail.com>
|
||||
Tero Rintaluoma <teror@google.com> <tero.rintaluoma@on2.com>
|
||||
Timothy B. Terriberry <tterribe@xiph.org> <tterriberry@mozilla.com>
|
||||
Tom Finegan <tomfinegan@google.com>
|
||||
Tom Finegan <tomfinegan@google.com> <tomfinegan@chromium.org>
|
||||
Urvang Joshi <urvang@google.com> <urvang@chromium.org>
|
||||
Yaowu Xu <yaowu@google.com> <adam@xuyaowu.com>
|
||||
Yaowu Xu <yaowu@google.com> <yaowu@xuyaowu.com>
|
||||
Yaowu Xu <yaowu@google.com> <Yaowu Xu>
|
||||
Venkatarama NG. Avadhani <venkatarama.avadhani@ittiam.com>
|
||||
xiwei gu <guxiwei-hf@loongson.cn>
|
||||
@@ -0,0 +1,195 @@
|
||||
# This file is automatically generated from the git commit history
|
||||
# by tools/gen_authors.sh.
|
||||
|
||||
Aaron Watry <awatry@gmail.com>
|
||||
Abo Talib Mahfoodh <ab.mahfoodh@gmail.com>
|
||||
Adrian Grange <agrange@google.com>
|
||||
Ahmad Sharif <asharif@google.com>
|
||||
Aidan Welch <aidansw@yahoo.com>
|
||||
Aleksey Vasenev <margtu-fivt@ya.ru>
|
||||
Alexander Potapenko <glider@google.com>
|
||||
Alexander Voronov <avoronov@graphics.cs.msu.ru>
|
||||
Alexandra Hájková <alexandra.khirnova@gmail.com>
|
||||
Aℓex Converse <aconverse@google.com>
|
||||
Alexis Ballier <aballier@gentoo.org>
|
||||
Alok Ahuja <waveletcoeff@gmail.com>
|
||||
Alpha Lam <hclam@google.com>
|
||||
A.Mahfoodh <ab.mahfoodh@gmail.com>
|
||||
Ami Fischman <fischman@chromium.org>
|
||||
Andoni Morales Alastruey <ylatuya@gmail.com>
|
||||
Andres Mejia <mcitadel@gmail.com>
|
||||
Andrew Lewis <andrewlewis@google.com>
|
||||
Andrew Russell <anrussell@google.com>
|
||||
Angie Chiang <angiebird@google.com>
|
||||
Aron Rosenberg <arosenberg@logitech.com>
|
||||
Attila Nagy <attilanagy@google.com>
|
||||
Brion Vibber <bvibber@wikimedia.org>
|
||||
changjun.yang <changjun.yang@intel.com>
|
||||
Charles 'Buck' Krasic <ckrasic@google.com>
|
||||
Cheng Chen <chengchen@google.com>
|
||||
Chi Yo Tsai <chiyotsai@google.com>
|
||||
chm <chm@rock-chips.com>
|
||||
Chris Cunningham <chcunningham@chromium.org>
|
||||
Christian Duvivier <cduvivier@google.com>
|
||||
Daniele Castagna <dcastagna@chromium.org>
|
||||
Daniel Kang <ddkang@google.com>
|
||||
Dan Zhu <zxdan@google.com>
|
||||
Deb Mukherjee <debargha@google.com>
|
||||
Deepa K G <deepa.kg@ittiam.com>
|
||||
Dim Temp <dimtemp0@gmail.com>
|
||||
Dmitry Kovalev <dkovalev@google.com>
|
||||
Dragan Mrdjan <dmrdjan@mips.com>
|
||||
Ed Baker <edward.baker@intel.com>
|
||||
Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
||||
Elliott Karpilovsky <elliottk@google.com>
|
||||
Erik Niemeyer <erik.a.niemeyer@intel.com>
|
||||
Fabio Pedretti <fabio.ped@libero.it>
|
||||
Frank Galligan <fgalligan@google.com>
|
||||
Fredrik Söderquist <fs@opera.com>
|
||||
Fritz Koenig <frkoenig@google.com>
|
||||
Fyodor Kyslov <kyslov@google.com>
|
||||
Gabriel Marin <gmx@chromium.org>
|
||||
Gaute Strokkenes <gaute.strokkenes@broadcom.com>
|
||||
Geza Lore <gezalore@gmail.com>
|
||||
Ghislain MARY <ghislainmary2@gmail.com>
|
||||
Giuseppe Scrivano <gscrivano@gnu.org>
|
||||
Gordana Cmiljanovic <gordana.cmiljanovic@imgtec.com>
|
||||
Gregor Jasny <gjasny@gmail.com>
|
||||
Guillaume Martres <gmartres@google.com>
|
||||
Guillermo Ballester Valor <gbvalor@gmail.com>
|
||||
Hangyu Kuang <hkuang@google.com>
|
||||
Hanno Böck <hanno@hboeck.de>
|
||||
Han Shen <shenhan@google.com>
|
||||
Harish Mahendrakar <harish.mahendrakar@ittiam.com>
|
||||
Henrik Lundin <hlundin@google.com>
|
||||
Hien Ho <hienho@google.com>
|
||||
Hui Su <huisu@google.com>
|
||||
Ivan Krasin <krasin@chromium.org>
|
||||
Ivan Maltz <ivanmaltz@google.com>
|
||||
Jacek Caban <cjacek@gmail.com>
|
||||
Jacky Chen <jackychen@google.com>
|
||||
James Berry <jamesberry@google.com>
|
||||
James Yu <james.yu@linaro.org>
|
||||
James Zern <jzern@google.com>
|
||||
Jan Gerber <j@mailb.org>
|
||||
Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
Janne Salonen <jsalonen@google.com>
|
||||
Jean-Yves Avenard <jyavenard@mozilla.com>
|
||||
Jeff Faust <jfaust@google.com>
|
||||
Jeff Muizelaar <jmuizelaar@mozilla.com>
|
||||
Jeff Petkau <jpet@chromium.org>
|
||||
Jerome Jiang <jianj@google.com>
|
||||
Jia Jia <jia.jia@linaro.org>
|
||||
Jian Zhou <zhoujian@google.com>
|
||||
Jim Bankoski <jimbankoski@google.com>
|
||||
Jingning Han <jingning@google.com>
|
||||
Joey Parrish <joeyparrish@google.com>
|
||||
Johann Koenig <johannkoenig@google.com>
|
||||
John Koleszar <jkoleszar@google.com>
|
||||
Johnny Klonaris <google@jawknee.com>
|
||||
John Stark <jhnstrk@gmail.com>
|
||||
Jon Kunkee <jkunkee@microsoft.com>
|
||||
Joshua Bleecher Snyder <josh@treelinelabs.com>
|
||||
Joshua Litt <joshualitt@google.com>
|
||||
Julia Robson <juliamrobson@gmail.com>
|
||||
Justin Clift <justin@salasaga.org>
|
||||
Justin Lebar <justin.lebar@gmail.com>
|
||||
Kaustubh Raste <kaustubh.raste@imgtec.com>
|
||||
KO Myung-Hun <komh@chollian.net>
|
||||
Kyle Siefring <kylesiefring@gmail.com>
|
||||
Lawrence Velázquez <larryv@macports.org>
|
||||
Linfeng Zhang <linfengz@google.com>
|
||||
Liu Peng <pengliu.mail@gmail.com>
|
||||
Lou Quillio <louquillio@google.com>
|
||||
Luca Barbato <lu_zero@gentoo.org>
|
||||
Luc Trudeau <luc@trud.ca>
|
||||
Makoto Kato <makoto.kt@gmail.com>
|
||||
Mans Rullgard <mans@mansr.com>
|
||||
Marco Paniconi <marpan@google.com>
|
||||
Mark Mentovai <mark@chromium.org>
|
||||
Martin Ettl <ettl.martin78@googlemail.com>
|
||||
Martin Storsjö <martin@martin.st>
|
||||
Matthew Heaney <matthewjheaney@chromium.org>
|
||||
Matthias Räncker <theonetruecamper@gmx.de>
|
||||
Michael Horowitz <mhoro@webrtc.org>
|
||||
Michael Kohler <michaelkohler@live.com>
|
||||
Mike Frysinger <vapier@chromium.org>
|
||||
Mike Hommey <mhommey@mozilla.com>
|
||||
Mikhal Shemer <mikhal@google.com>
|
||||
Min Chen <chenm003@gmail.com>
|
||||
Minghai Shang <minghai@google.com>
|
||||
Min Ye <yeemmi@google.com>
|
||||
Mirko Bonadei <mbonadei@google.com>
|
||||
Moriyoshi Koizumi <mozo@mozo.jp>
|
||||
Morton Jonuschat <yabawock@gmail.com>
|
||||
Nathan E. Egge <negge@mozilla.com>
|
||||
Nico Weber <thakis@chromium.org>
|
||||
Niveditha Rau <niveditha.rau@gmail.com>
|
||||
Parag Salasakar <img.mips1@gmail.com>
|
||||
Pascal Massimino <pascal.massimino@gmail.com>
|
||||
Patrik Westin <patrik.westin@gmail.com>
|
||||
Paul Wilkins <paulwilkins@google.com>
|
||||
Pavol Rusnak <stick@gk2.sk>
|
||||
Paweł Hajdan <phajdan@google.com>
|
||||
Pengchong Jin <pengchong@google.com>
|
||||
Peter Boström <pbos@chromium.org>
|
||||
Peter Collingbourne <pcc@chromium.org>
|
||||
Peter de Rivaz <peter.derivaz@gmail.com>
|
||||
Philip Jägenstedt <philipj@opera.com>
|
||||
Priit Laes <plaes@plaes.org>
|
||||
Rafael Ávila de Espíndola <rafael.espindola@gmail.com>
|
||||
Rafaël Carré <funman@videolan.org>
|
||||
Rafael de Lucena Valle <rafaeldelucena@gmail.com>
|
||||
Rahul Chaudhry <rahulchaudhry@google.com>
|
||||
Ralph Giles <giles@xiph.org>
|
||||
Ranjit Kumar Tulabandu <ranjit.tulabandu@ittiam.com>
|
||||
Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
|
||||
Ravi Chaudhary <ravi.chaudhary@ittiam.com>
|
||||
Ritu Baldwa <ritu.baldwa@ittiam.com>
|
||||
Rob Bradford <rob@linux.intel.com>
|
||||
Ronald S. Bultje <rsbultje@gmail.com>
|
||||
Rui Ueyama <ruiu@google.com>
|
||||
Sai Deng <sdeng@google.com>
|
||||
Sami Pietilä <samipietila@google.com>
|
||||
Sarah Parker <sarahparker@google.com>
|
||||
Sasi Inguva <isasi@google.com>
|
||||
Scott Graham <scottmg@chromium.org>
|
||||
Scott LaVarnway <slavarnway@google.com>
|
||||
Sean McGovern <gseanmcg@gmail.com>
|
||||
Sergey Kolomenkin <kolomenkin@gmail.com>
|
||||
Sergey Silkin <ssilkin@google.com>
|
||||
Sergey Ulanov <sergeyu@chromium.org>
|
||||
Shimon Doodkin <helpmepro1@gmail.com>
|
||||
Shiyou Yin <yinshiyou-hf@loongson.cn>
|
||||
Shubham Tandle <shubham.tandle@ittiam.com>
|
||||
Shunyao Li <shunyaoli@google.com>
|
||||
Stefan Holmer <holmer@google.com>
|
||||
Suman Sunkara <sunkaras@google.com>
|
||||
Supradeep T R <supradeep.tr@ittiam.com>
|
||||
Sylvestre Ledru <sylvestre@mozilla.com>
|
||||
Taekhyun Kim <takim@nvidia.com>
|
||||
Takanori MATSUURA <t.matsuu@gmail.com>
|
||||
Tamar Levy <tamar.levy@intel.com>
|
||||
Tao Bai <michaelbai@chromium.org>
|
||||
Tero Rintaluoma <teror@google.com>
|
||||
Thijs Vermeir <thijsvermeir@gmail.com>
|
||||
Tim Kopp <tkopp@google.com>
|
||||
Timothy B. Terriberry <tterribe@xiph.org>
|
||||
Tom Finegan <tomfinegan@google.com>
|
||||
Tristan Matthews <le.businessman@gmail.com>
|
||||
Urvang Joshi <urvang@google.com>
|
||||
Venkatarama NG. Avadhani <venkatarama.avadhani@ittiam.com>
|
||||
Vignesh Venkatasubramanian <vigneshv@google.com>
|
||||
Vlad Tsyrklevich <vtsyrklevich@chromium.org>
|
||||
Wan-Teh Chang <wtc@google.com>
|
||||
xiwei gu <guxiwei-hf@loongson.cn>
|
||||
Yaowu Xu <yaowu@google.com>
|
||||
Yi Luo <luoyi@google.com>
|
||||
Yongzhe Wang <yongzhe@google.com>
|
||||
Yue Chen <yuec@google.com>
|
||||
Yunqing Wang <yunqingwang@google.com>
|
||||
Yury Gitman <yuryg@google.com>
|
||||
Zoe Liu <zoeliu@google.com>
|
||||
Google Inc.
|
||||
The Mozilla Foundation
|
||||
The Xiph.Org Foundation
|
||||
@@ -0,0 +1,754 @@
|
||||
2019-07-15 v1.8.1 "Orpington Duck"
|
||||
This release collects incremental improvements to many aspects of the library.
|
||||
|
||||
- Upgrading:
|
||||
VP8E_SET_CPUUSED now accepts values up to 9 for vp9.
|
||||
VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT had a spelling fix (was VP8E).
|
||||
The --sdk-path option has been removed. If you were using it to build for
|
||||
Android please read build/make/Android.mk for alternatives.
|
||||
All PPC optimizations have been disabled:
|
||||
https://bugs.chromium.org/p/webm/issues/detail?id=1522.
|
||||
|
||||
- Enhancements:
|
||||
Various changes to improve encoder rate control, quality and speed
|
||||
for practically every use case.
|
||||
|
||||
- Bug fixes:
|
||||
vp9-rtc: Fix color artifacts for speed >= 8.
|
||||
|
||||
2019-01-31 v1.8.0 "Northern Shoveler Duck"
|
||||
This release focused on encoding performance for realtime and VOD use cases.
|
||||
|
||||
- Upgrading:
|
||||
This adds and improves several vp9 controls. Most are related to SVC:
|
||||
VP9E_SET_SVC_FRAME_DROP_LAYER:
|
||||
- Frame dropping in SVC.
|
||||
VP9E_SET_SVC_INTER_LAYER_PRED:
|
||||
- Inter-layer prediction in SVC.
|
||||
VP9E_SET_SVC_GF_TEMPORAL_REF:
|
||||
- Enable long term temporal reference in SVC.
|
||||
VP9E_SET_SVC_REF_FRAME_CONFIG/VP9E_GET_SVC_REF_FRAME_CONFIG:
|
||||
- Extend and improve this control for better flexibility in setting SVC
|
||||
pattern dynamically.
|
||||
VP9E_SET_POSTENCODE_DROP:
|
||||
- Allow for post-encode frame dropping (applies to non-SVC too).
|
||||
VP9E_SET_SVC_SPATIAL_LAYER_SYNC:
|
||||
- Enable spatial layer sync frames.
|
||||
VP9E_SET_SVC_LAYER_ID:
|
||||
- Extend api to specify temporal id for each spatial layers.
|
||||
VP9E_SET_ROI_MAP:
|
||||
- Extend Region of Interest functionality to VP9.
|
||||
|
||||
- Enhancements:
|
||||
2 pass vp9 encoding has improved substantially. When using --auto-alt-ref=6,
|
||||
we see approximately 8% for VBR and 10% for CQ. When using --auto-alt-ref=1,
|
||||
the gains are approximately 4% for VBR and 5% for CQ.
|
||||
|
||||
For real-time encoding, speed 7 has improved by ~5-10%. Encodes targeted at
|
||||
screen sharing have improved when the content changes significantly (slide
|
||||
sharing) or scrolls. There is a new speed 9 setting for mobile devices which
|
||||
is about 10-20% faster than speed 8.
|
||||
|
||||
- Bug fixes:
|
||||
VP9 denoiser issue.
|
||||
VP9 partition issue for 1080p.
|
||||
VP9 rate control improvments.
|
||||
Postprocessing Multi Frame Quality Enhancement (MFQE) issue.
|
||||
VP8 multithread decoder issues.
|
||||
A variety of fuzzing issues.
|
||||
|
||||
2018-01-04 v1.7.0 "Mandarin Duck"
|
||||
This release focused on high bit depth performance (10/12 bit) and vp9
|
||||
encoding improvements.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI incompatible due to new vp9 encoder features.
|
||||
|
||||
Frame parallel decoding for vp9 has been removed.
|
||||
|
||||
- Enhancements:
|
||||
vp9 encoding supports additional threads with --row-mt. This can be greater
|
||||
than the number of tiles.
|
||||
|
||||
Two new vp9 encoder options have been added:
|
||||
--corpus-complexity
|
||||
--tune-content=film
|
||||
|
||||
Additional tooling for respecting the vp9 "level" profiles has been added.
|
||||
|
||||
- Bug fixes:
|
||||
A variety of fuzzing issues.
|
||||
vp8 threading fix for ARM.
|
||||
Codec control VP9_SET_SKIP_LOOP_FILTER fixed.
|
||||
Reject invalid multi resolution configurations.
|
||||
|
||||
2017-01-09 v1.6.1 "Long Tailed Duck"
|
||||
This release improves upon the VP9 encoder and speeds up the encoding and
|
||||
decoding processes.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI compatible with 1.6.0.
|
||||
|
||||
- Enhancements:
|
||||
Faster VP9 encoding and decoding.
|
||||
High bit depth builds now provide similar speed for 8 bit encode and decode
|
||||
for x86 targets. Other platforms and higher bit depth improvements are in
|
||||
progress.
|
||||
|
||||
- Bug Fixes:
|
||||
A variety of fuzzing issues.
|
||||
|
||||
2016-07-20 v1.6.0 "Khaki Campbell Duck"
|
||||
This release improves upon the VP9 encoder and speeds up the encoding and
|
||||
decoding processes.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI incompatible with 1.5.0 due to a new 'color_range' enum
|
||||
in vpx_image and some minor changes to the VP8_COMP structure.
|
||||
|
||||
The default key frame interval for VP9 has changed from 128 to 9999.
|
||||
|
||||
- Enhancement:
|
||||
A core focus has been performance for low end Intel processors. SSSE3
|
||||
instructions such as 'pshufb' have been avoided and instructions have been
|
||||
reordered to better accommodate the more constrained pipelines.
|
||||
|
||||
As a result, devices based on Celeron processors have seen substantial
|
||||
decoding improvements. From Indian Runner Duck to Javan Whistling Duck,
|
||||
decoding speed improved between 10 and 30%. Between Javan Whistling Duck
|
||||
and Khaki Campbell Duck, it improved another 10 to 15%.
|
||||
|
||||
While Celeron benefited most, Core-i5 also improved 5% and 10% between the
|
||||
respective releases.
|
||||
|
||||
Realtime performance for WebRTC for both speed and quality has received a
|
||||
lot of attention.
|
||||
|
||||
- Bug Fixes:
|
||||
A number of fuzzing issues, found variously by Mozilla, Chromium and others,
|
||||
have been fixed and we strongly recommend updating.
|
||||
|
||||
2015-11-09 v1.5.0 "Javan Whistling Duck"
|
||||
This release improves upon the VP9 encoder and speeds up the encoding and
|
||||
decoding processes.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI incompatible with 1.4.0. It drops deprecated VP8
|
||||
controls and adds a variety of VP9 controls for testing.
|
||||
|
||||
The vpxenc utility now prefers VP9 by default.
|
||||
|
||||
- Enhancements:
|
||||
Faster VP9 encoding and decoding
|
||||
Smaller library size by combining functions used by VP8 and VP9
|
||||
|
||||
- Bug Fixes:
|
||||
A variety of fuzzing issues
|
||||
|
||||
2015-04-03 v1.4.0 "Indian Runner Duck"
|
||||
This release includes significant improvements to the VP9 codec.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI incompatible with 1.3.0. It drops the compatibility
|
||||
layer, requiring VPX_IMG_FMT_* instead of IMG_FMT_*, and adds several codec
|
||||
controls for VP9.
|
||||
|
||||
- Enhancements:
|
||||
Faster VP9 encoding and decoding
|
||||
Multithreaded VP9 decoding (tile and frame-based)
|
||||
Multithreaded VP9 encoding - on by default
|
||||
YUV 4:2:2 and 4:4:4 support in VP9
|
||||
10 and 12bit support in VP9
|
||||
64bit ARM support by replacing ARM assembly with intrinsics
|
||||
|
||||
- Bug Fixes:
|
||||
Fixes a VP9 bitstream issue in Profile 1. This only affected non-YUV 4:2:0
|
||||
files.
|
||||
|
||||
- Known Issues:
|
||||
Frame Parallel decoding fails for segmented and non-420 files.
|
||||
|
||||
2013-11-15 v1.3.0 "Forest"
|
||||
This release introduces the VP9 codec in a backward-compatible way.
|
||||
All existing users of VP8 can continue to use the library without
|
||||
modification. However, some VP8 options do not map to VP9 in the same manner.
|
||||
|
||||
The VP9 encoder in this release is not feature complete. Users interested in
|
||||
the encoder are advised to use the git master branch and discuss issues on
|
||||
libvpx mailing lists.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI and API compatible with Duclair (v1.0.0). Users
|
||||
of older releases should refer to the Upgrading notes in this document
|
||||
for that release.
|
||||
|
||||
- Enhancements:
|
||||
Get rid of bashisms in the main build scripts
|
||||
Added usage info on command line options
|
||||
Add lossless compression mode
|
||||
Dll build of libvpx
|
||||
Add additional Mac OS X targets: 10.7, 10.8 and 10.9 (darwin11-13)
|
||||
Add option to disable documentation
|
||||
configure: add --enable-external-build support
|
||||
make: support V=1 as short form of verbose=yes
|
||||
configure: support mingw-w64
|
||||
configure: support hardfloat armv7 CHOSTS
|
||||
configure: add support for android x86
|
||||
Add estimated completion time to vpxenc
|
||||
Don't exit on decode errors in vpxenc
|
||||
vpxenc: support scaling prior to encoding
|
||||
vpxdec: support scaling output
|
||||
vpxenc: improve progress indicators with --skip
|
||||
msvs: Don't link to winmm.lib
|
||||
Add a new script for producing vcxproj files
|
||||
Produce Visual Studio 10 and 11 project files
|
||||
Produce Windows Phone project files
|
||||
msvs-build: use msbuild for vs >= 2005
|
||||
configure: default configure log to config.log
|
||||
Add encoding option --static-thresh
|
||||
|
||||
- Speed:
|
||||
Miscellaneous speed optimizations for VP8 and VP9.
|
||||
|
||||
- Quality:
|
||||
In general, quality is consistent with the Eider release.
|
||||
|
||||
- Bug Fixes:
|
||||
This release represents approximately a year of engineering effort,
|
||||
and contains multiple bug fixes. Please refer to git history for details.
|
||||
|
||||
|
||||
2012-12-21 v1.2.0
|
||||
This release acts as a checkpoint for a large amount of internal refactoring
|
||||
and testing. It also contains a number of small bugfixes, so all users are
|
||||
encouraged to upgrade.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI and API compatible with Duclair (v1.0.0). Users
|
||||
of older releases should refer to the Upgrading notes in this
|
||||
document for that release.
|
||||
|
||||
- Enhancements:
|
||||
VP8 optimizations for MIPS dspr2
|
||||
vpxenc: add -quiet option
|
||||
|
||||
- Speed:
|
||||
Encoder and decoder speed is consistent with the Eider release.
|
||||
|
||||
- Quality:
|
||||
In general, quality is consistent with the Eider release.
|
||||
|
||||
Minor tweaks to ARNR filtering
|
||||
Minor improvements to real time encoding with multiple temporal layers
|
||||
|
||||
- Bug Fixes:
|
||||
Fixes multithreaded encoder race condition in loopfilter
|
||||
Fixes multi-resolution threaded encoding
|
||||
Fix potential encoder dead-lock after picture resize
|
||||
|
||||
|
||||
2012-05-09 v1.1.0 "Eider"
|
||||
This introduces a number of enhancements, mostly focused on real-time
|
||||
encoding. In addition, it fixes a decoder bug (first introduced in
|
||||
Duclair) so all users of that release are encouraged to upgrade.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI and API compatible with Duclair (v1.0.0). Users
|
||||
of older releases should refer to the Upgrading notes in this
|
||||
document for that release.
|
||||
|
||||
This release introduces a new temporal denoiser, controlled by the
|
||||
VP8E_SET_NOISE_SENSITIVITY control. The temporal denoiser does not
|
||||
currently take a strength parameter, so the control is effectively
|
||||
a boolean - zero (off) or non-zero (on). For compatibility with
|
||||
existing applications, the values accepted are the same as those
|
||||
for the spatial denoiser (0-6). The temporal denoiser is enabled
|
||||
by default, and the older spatial denoiser may be restored by
|
||||
configuring with --disable-temporal-denoising. The temporal denoiser
|
||||
is more computationally intensive than the spatial one.
|
||||
|
||||
This release removes support for a legacy, decode only API that was
|
||||
supported, but deprecated, at the initial release of libvpx
|
||||
(v0.9.0). This is not expected to have any impact. If you are
|
||||
impacted, you can apply a reversion to commit 2bf8fb58 locally.
|
||||
Please update to the latest libvpx API if you are affected.
|
||||
|
||||
- Enhancements:
|
||||
Adds a motion compensated temporal denoiser to the encoder, which
|
||||
gives higher quality than the older spatial denoiser. (See above
|
||||
for notes on upgrading).
|
||||
|
||||
In addition, support for new compilers and platforms were added,
|
||||
including:
|
||||
improved support for XCode
|
||||
Android x86 NDK build
|
||||
OS/2 support
|
||||
SunCC support
|
||||
|
||||
Changing resolution with vpx_codec_enc_config_set() is now
|
||||
supported. Previously, reinitializing the codec was required to
|
||||
change the input resolution.
|
||||
|
||||
The vpxenc application has initial support for producing multiple
|
||||
encodes from the same input in one call. Resizing is not yet
|
||||
supported, but varying other codec parameters is. Use -- to
|
||||
delineate output streams. Options persist from one stream to the
|
||||
next.
|
||||
|
||||
Also, the vpxenc application will now use a keyframe interval of
|
||||
5 seconds by default. Use the --kf-max-dist option to override.
|
||||
|
||||
- Speed:
|
||||
Decoder performance improved 2.5% versus Duclair. Encoder speed is
|
||||
consistent with Duclair for most material. Two pass encoding of
|
||||
slideshow-like material will see significant improvements.
|
||||
|
||||
Large realtime encoding speed gains at a small quality expense are
|
||||
possible by configuring the on-the-fly bitpacking experiment with
|
||||
--enable-onthefly-bitpacking. Realtime encoder can be up to 13%
|
||||
faster (ARM) depending on the number of threads and bitrate
|
||||
settings. This technique sees constant gain over the 5-16 speed
|
||||
range. For VC style input the loss seen is up to 0.2dB. See commit
|
||||
52cf4dca for further details.
|
||||
|
||||
- Quality:
|
||||
On the whole, quality is consistent with the Duclair release. Some
|
||||
tweaks:
|
||||
|
||||
Reduced blockiness in easy sections by applying a penalty to
|
||||
intra modes.
|
||||
|
||||
Improved quality of static sections (like slideshows) with
|
||||
two pass encoding.
|
||||
|
||||
Improved keyframe sizing with multiple temporal layers
|
||||
|
||||
- Bug Fixes:
|
||||
Corrected alt-ref contribution to frame rate for visible updates
|
||||
to the alt-ref buffer. This affected applications making manual
|
||||
usage of the frame reference flags, or temporal layers.
|
||||
|
||||
Additional constraints were added to disable multi-frame quality
|
||||
enhancement (MFQE) in sections of the frame where there is motion.
|
||||
(#392)
|
||||
|
||||
Fixed corruption issues when vpx_codec_enc_config_set() was called
|
||||
with spatial resampling enabled.
|
||||
|
||||
Fixed a decoder error introduced in Duclair where the segmentation
|
||||
map was not being reinitialized on keyframes (#378)
|
||||
|
||||
|
||||
2012-01-27 v1.0.0 "Duclair"
|
||||
Our fourth named release, focused on performance and features related to
|
||||
real-time encoding. It also fixes a decoder crash bug introduced in
|
||||
v0.9.7, so all users of that release are encouraged to upgrade.
|
||||
|
||||
- Upgrading:
|
||||
This release is ABI incompatible with prior releases of libvpx, so the
|
||||
"major" version number has been bumped to 1. You must recompile your
|
||||
applications against the latest version of the libvpx headers. The
|
||||
API remains compatible, and this should not require code changes in most
|
||||
applications.
|
||||
|
||||
- Enhancements:
|
||||
This release introduces several substantial new features to the encoder,
|
||||
of particular interest to real time streaming applications.
|
||||
|
||||
Temporal scalability allows the encoder to produce a stream that can
|
||||
be decimated to different frame rates, with independent rate targetting
|
||||
for each substream.
|
||||
|
||||
Multiframe quality enhancement postprocessing can make visual quality
|
||||
more consistent in the presence of frames that are substantially
|
||||
different quality than the surrounding frames, as in the temporal
|
||||
scalability case and in some forced keyframe scenarios.
|
||||
|
||||
Multiple-resolution encoding support allows the encoding of the
|
||||
same content at different resolutions faster than encoding them
|
||||
separately.
|
||||
|
||||
- Speed:
|
||||
Optimization targets for this release included the decoder and the real-
|
||||
time modes of the encoder. Decoder speed on x86 has improved 10.5% with
|
||||
this release. Encoder improvements followed a curve where speeds 1-3
|
||||
improved 4.0%-1.5%, speeds 4-8 improved <1%, and speeds 9-16 improved
|
||||
1.5% to 10.5%, respectively. "Best" mode speed is consistent with the
|
||||
Cayuga release.
|
||||
|
||||
- Quality:
|
||||
Encoder quality in the single stream case is consistent with the Cayuga
|
||||
release.
|
||||
|
||||
- Bug Fixes:
|
||||
This release fixes an OOB read decoder crash bug present in v0.9.7
|
||||
related to the clamping of motion vectors in SPLITMV blocks. This
|
||||
behavior could be triggered by corrupt input or by starting
|
||||
decoding from a P-frame.
|
||||
|
||||
|
||||
2011-08-15 v0.9.7-p1 "Cayuga" patch 1
|
||||
This is an incremental bugfix release against Cayuga. All users of that
|
||||
release are strongly encouraged to upgrade.
|
||||
|
||||
- Fix potential OOB reads (cdae03a)
|
||||
|
||||
An unbounded out of bounds read was discovered when the
|
||||
decoder was requested to perform error concealment (new in
|
||||
Cayuga) given a frame with corrupt partition sizes.
|
||||
|
||||
A bounded out of bounds read was discovered affecting all
|
||||
versions of libvpx. Given an multipartition input frame that
|
||||
is truncated between the mode/mv partition and the first
|
||||
residiual paritition (in the block of partition offsets), up
|
||||
to 3 extra bytes could have been read from the source buffer.
|
||||
The code will not take any action regardless of the contents
|
||||
of these undefined bytes, as the truncated buffer is detected
|
||||
immediately following the read based on the calculated
|
||||
starting position of the coefficient partition.
|
||||
|
||||
- Fix potential error concealment crash when the very first frame
|
||||
is missing or corrupt (a609be5)
|
||||
|
||||
- Fix significant artifacts in error concealment (a4c2211, 99d870a)
|
||||
|
||||
- Revert 1-pass CBR rate control changes (e961317)
|
||||
Further testing showed this change produced undesirable visual
|
||||
artifacts, rolling back for now.
|
||||
|
||||
|
||||
2011-08-02 v0.9.7 "Cayuga"
|
||||
Our third named release, focused on a faster, higher quality, encoder.
|
||||
|
||||
- Upgrading:
|
||||
This release is backwards compatible with Aylesbury (v0.9.5) and
|
||||
Bali (v0.9.6). Users of older releases should refer to the Upgrading
|
||||
notes in this document for that release.
|
||||
|
||||
- Enhancements:
|
||||
Stereo 3D format support for vpxenc
|
||||
Runtime detection of available processor cores.
|
||||
Allow specifying --end-usage by enum name
|
||||
vpxdec: test for frame corruption
|
||||
vpxenc: add quantizer histogram display
|
||||
vpxenc: add rate histogram display
|
||||
Set VPX_FRAME_IS_DROPPABLE
|
||||
update configure for ios sdk 4.3
|
||||
Avoid text relocations in ARM vp8 decoder
|
||||
Generate a vpx.pc file for pkg-config.
|
||||
New ways of passing encoded data between encoder and decoder.
|
||||
|
||||
- Speed:
|
||||
This release includes across-the-board speed improvements to the
|
||||
encoder. On x86, these measure at approximately 11.5% in Best mode,
|
||||
21.5% in Good mode (speed 0), and 22.5% in Realtime mode (speed 6).
|
||||
On ARM Cortex A9 with Neon extensions, real-time encoding of video
|
||||
telephony content is 35% faster than Bali on single core and 48%
|
||||
faster on multi-core. On the NVidia Tegra2 platform, real time
|
||||
encoding is 40% faster than Bali.
|
||||
|
||||
Decoder speed was not a priority for this release, but improved
|
||||
approximately 8.4% on x86.
|
||||
|
||||
Reduce motion vector search on alt-ref frame.
|
||||
Encoder loopfilter running in its own thread
|
||||
Reworked loopfilter to precalculate more parameters
|
||||
SSE2/SSSE3 optimizations for build_predictors_mbuv{,_s}().
|
||||
Make hor UV predict ~2x faster (73 vs 132 cycles) using SSSE3.
|
||||
Removed redundant checks
|
||||
Reduced structure sizes
|
||||
utilize preload in ARMv6 MC/LPF/Copy routines
|
||||
ARM optimized quantization, dfct, variance, subtract
|
||||
Increase chrow row alignment to 16 bytes.
|
||||
disable trellis optimization for first pass
|
||||
Write SSSE3 sub-pixel filter function
|
||||
Improve SSE2 half-pixel filter funtions
|
||||
Add vp8_sub_pixel_variance16x8_ssse3 function
|
||||
Reduce unnecessary distortion computation
|
||||
Use diamond search to replace full search
|
||||
Preload reference area in sub-pixel motion search (real-time mode)
|
||||
|
||||
- Quality:
|
||||
This release focused primarily on one-pass use cases, including
|
||||
video conferencing. Low latency data rate control was significantly
|
||||
improved, improving streamability over bandwidth constrained links.
|
||||
Added support for error concealment, allowing frames to maintain
|
||||
visual quality in the presence of substantial packet loss.
|
||||
|
||||
Add rc_max_intra_bitrate_pct control
|
||||
Limit size of initial keyframe in one-pass.
|
||||
Improve framerate adaptation
|
||||
Improved 1-pass CBR rate control
|
||||
Improved KF insertion after fades to still.
|
||||
Improved key frame detection.
|
||||
Improved activity masking (lower PSNR impact for same SSIM boost)
|
||||
Improved interaction between GF and ARFs
|
||||
Adding error-concealment to the decoder.
|
||||
Adding support for independent partitions
|
||||
Adjusted rate-distortion constants
|
||||
|
||||
|
||||
- Bug Fixes:
|
||||
Removed firstpass motion map
|
||||
Fix parallel make install
|
||||
Fix multithreaded encoding for 1 MB wide frame
|
||||
Fixed iwalsh_neon build problems with RVDS4.1
|
||||
Fix semaphore emulation, spin-wait intrinsics on Windows
|
||||
Fix build with xcode4 and simplify GLOBAL.
|
||||
Mark ARM asm objects as allowing a non-executable stack.
|
||||
Fix vpxenc encoding incorrect webm file header on big endian
|
||||
|
||||
|
||||
2011-03-07 v0.9.6 "Bali"
|
||||
Our second named release, focused on a faster, higher quality, encoder.
|
||||
|
||||
- Upgrading:
|
||||
This release is backwards compatible with Aylesbury (v0.9.5). Users
|
||||
of older releases should refer to the Upgrading notes in this
|
||||
document for that release.
|
||||
|
||||
- Enhancements:
|
||||
vpxenc --psnr shows a summary when encode completes
|
||||
--tune=ssim option to enable activity masking
|
||||
improved postproc visualizations for development
|
||||
updated support for Apple iOS to SDK 4.2
|
||||
query decoder to determine which reference frames were updated
|
||||
implemented error tracking in the decoder
|
||||
fix pipe support on windows
|
||||
|
||||
- Speed:
|
||||
Primary focus was on good quality mode, speed 0. Average improvement
|
||||
on x86 about 40%, up to 100% on user-generated content at that speed.
|
||||
Best quality mode speed improved 35%, and realtime speed 10-20%. This
|
||||
release also saw significant improvement in realtime encoding speed
|
||||
on ARM platforms.
|
||||
|
||||
Improved encoder threading
|
||||
Dont pick encoder filter level when loopfilter is disabled.
|
||||
Avoid double copying of key frames into alt and golden buffer
|
||||
FDCT optimizations.
|
||||
x86 sse2 temporal filter
|
||||
SSSE3 version of fast quantizer
|
||||
vp8_rd_pick_best_mbsegmentation code restructure
|
||||
Adjusted breakout RD for SPLITMV
|
||||
Changed segmentation check order
|
||||
Improved rd_pick_intra4x4block
|
||||
Adds armv6 optimized variance calculation
|
||||
ARMv6 optimized sad16x16
|
||||
ARMv6 optimized half pixel variance calculations
|
||||
Full search SAD function optimization in SSE4.1
|
||||
Improve MV prediction accuracy to achieve performance gain
|
||||
Improve MV prediction in vp8_pick_inter_mode() for speed>3
|
||||
|
||||
- Quality:
|
||||
Best quality mode improved PSNR 6.3%, and SSIM 6.1%. This release
|
||||
also includes support for "activity masking," which greatly improves
|
||||
SSIM at the expense of PSNR. For now, this feature is available with
|
||||
the --tune=ssim option. Further experimentation in this area
|
||||
is ongoing. This release also introduces a new rate control mode
|
||||
called "CQ," which changes the allocation of bits within a clip to
|
||||
the sections where they will have the most visual impact.
|
||||
|
||||
Tuning for the more exact quantizer.
|
||||
Relax rate control for last few frames
|
||||
CQ Mode
|
||||
Limit key frame quantizer for forced key frames.
|
||||
KF/GF Pulsing
|
||||
Add simple version of activity masking.
|
||||
make rdmult adaptive for intra in quantizer RDO
|
||||
cap the best quantizer for 2nd order DC
|
||||
change the threshold of DC check for encode breakout
|
||||
|
||||
- Bug Fixes:
|
||||
Fix crash on Sparc Solaris.
|
||||
Fix counter of fixed keyframe distance
|
||||
ARNR filter pointer update bug fix
|
||||
Fixed use of motion percentage in KF/GF group calc
|
||||
Changed condition for using RD in Intra Mode
|
||||
Fix encoder real-time only configuration.
|
||||
Fix ARM encoder crash with multiple token partitions
|
||||
Fixed bug first cluster timecode of webm file is wrong.
|
||||
Fixed various encoder bugs with odd-sized images
|
||||
vp8e_get_preview fixed when spatial resampling enabled
|
||||
quantizer: fix assertion in fast quantizer path
|
||||
Allocate source buffers to be multiples of 16
|
||||
Fix for manual Golden frame frequency
|
||||
Fix drastic undershoot in long form content
|
||||
|
||||
|
||||
2010-10-28 v0.9.5 "Aylesbury"
|
||||
Our first named release, focused on a faster decoder, and a better encoder.
|
||||
|
||||
- Upgrading:
|
||||
This release incorporates backwards-incompatible changes to the
|
||||
ivfenc and ivfdec tools. These tools are now called vpxenc and vpxdec.
|
||||
|
||||
vpxdec
|
||||
* the -q (quiet) option has been removed, and replaced with
|
||||
-v (verbose). the output is quiet by default. Use -v to see
|
||||
the version number of the binary.
|
||||
|
||||
* The default behavior is now to write output to a single file
|
||||
instead of individual frames. The -y option has been removed.
|
||||
Y4M output is the default.
|
||||
|
||||
* For raw I420/YV12 output instead of Y4M, the --i420 or --yv12
|
||||
options must be specified.
|
||||
|
||||
$ ivfdec -o OUTPUT INPUT
|
||||
$ vpxdec --i420 -o OUTPUT INPUT
|
||||
|
||||
* If an output file is not specified, the default is to write
|
||||
Y4M to stdout. This makes piping more natural.
|
||||
|
||||
$ ivfdec -y -o - INPUT | ...
|
||||
$ vpxdec INPUT | ...
|
||||
|
||||
* The output file has additional flexibility for formatting the
|
||||
filename. It supports escape characters for constructing a
|
||||
filename from the width, height, and sequence number. This
|
||||
replaces the -p option. To get the equivalent:
|
||||
|
||||
$ ivfdec -p frame INPUT
|
||||
$ vpxdec --i420 -o frame-%wx%h-%4.i420 INPUT
|
||||
|
||||
vpxenc
|
||||
* The output file must be specified with -o, rather than as the
|
||||
last argument.
|
||||
|
||||
$ ivfenc <options> INPUT OUTPUT
|
||||
$ vpxenc <options> -o OUTPUT INPUT
|
||||
|
||||
* The output defaults to webm. To get IVF output, use the --ivf
|
||||
option.
|
||||
|
||||
$ ivfenc <options> INPUT OUTPUT.ivf
|
||||
$ vpxenc <options> -o OUTPUT.ivf --ivf INPUT
|
||||
|
||||
|
||||
- Enhancements:
|
||||
ivfenc and ivfdec have been renamed to vpxenc, vpxdec.
|
||||
vpxdec supports .webm input
|
||||
vpxdec writes .y4m by default
|
||||
vpxenc writes .webm output by default
|
||||
vpxenc --psnr now shows the average/overall PSNR at the end
|
||||
ARM platforms now support runtime cpu detection
|
||||
vpxdec visualizations added for motion vectors, block modes, references
|
||||
vpxdec now silent by default
|
||||
vpxdec --progress shows frame-by-frame timing information
|
||||
vpxenc supports the distinction between --fps and --timebase
|
||||
NASM is now a supported assembler
|
||||
configure: enable PIC for shared libs by default
|
||||
configure: add --enable-small
|
||||
configure: support for ppc32-linux-gcc
|
||||
configure: support for sparc-solaris-gcc
|
||||
|
||||
- Bugs:
|
||||
Improve handling of invalid frames
|
||||
Fix valgrind errors in the NEON loop filters.
|
||||
Fix loopfilter delta zero transitions
|
||||
Fix valgrind errors in vp8_sixtap_predict8x4_armv6().
|
||||
Build fixes for darwin-icc
|
||||
|
||||
- Speed:
|
||||
20-40% (average 28%) improvement in libvpx decoder speed,
|
||||
including:
|
||||
Rewrite vp8_short_walsh4x4_sse2()
|
||||
Optimizations on the loopfilters.
|
||||
Miscellaneous improvements for Atom
|
||||
Add 4-tap version of 2nd-pass ARMv6 MC filter.
|
||||
Improved multithread utilization
|
||||
Better instruction choices on x86
|
||||
reorder data to use wider instructions
|
||||
Update NEON wide idcts
|
||||
Make block access to frame buffer sequential
|
||||
Improved subset block search
|
||||
Bilinear subpixel optimizations for ssse3.
|
||||
Decrease memory footprint
|
||||
|
||||
Encoder speed improvements (percentage gain not measured):
|
||||
Skip unnecessary search of identical frames
|
||||
Add SSE2 subtract functions
|
||||
Improve bounds checking in vp8_diamond_search_sadx4()
|
||||
Added vp8_fast_quantize_b_sse2
|
||||
|
||||
- Quality:
|
||||
Over 7% overall PSNR improvement (6.3% SSIM) in "best" quality
|
||||
encoding mode, and up to 60% improvement on very noisy, still
|
||||
or slow moving source video
|
||||
|
||||
Motion compensated temporal filter for Alt-Ref Noise Reduction
|
||||
Improved use of trellis quantization on 2nd order Y blocks
|
||||
Tune effect of motion on KF/GF boost in two pass
|
||||
Allow coefficient optimization for good quality speed 0.
|
||||
Improved control of active min quantizer for two pass.
|
||||
Enable ARFs for non-lagged compress
|
||||
|
||||
2010-09-02 v0.9.2
|
||||
- Enhancements:
|
||||
Disable frame dropping by default
|
||||
Improved multithreaded performance
|
||||
Improved Force Key Frame Behaviour
|
||||
Increased rate control buffer level precision
|
||||
Fix bug in 1st pass motion compensation
|
||||
ivfenc: correct fixed kf interval, --disable-kf
|
||||
- Speed:
|
||||
Changed above and left context data layout
|
||||
Rework idct calling structure.
|
||||
Removed unnecessary MB_MODE_INFO copies
|
||||
x86: SSSE3 sixtap prediction
|
||||
Reworked IDCT to include reconstruction (add) step
|
||||
Swap alt/gold/new/last frame buffer ptrs instead of copying.
|
||||
Improve SSE2 loopfilter functions
|
||||
Change bitreader to use a larger window.
|
||||
Avoid loopfilter reinitialization when possible
|
||||
- Quality:
|
||||
Normalize quantizer's zero bin and rounding factors
|
||||
Add trellis quantization.
|
||||
Make the quantizer exact.
|
||||
Updates to ARNR filtering algorithm
|
||||
Fix breakout thresh computation for golden & AltRef frames
|
||||
Redo the forward 4x4 dct
|
||||
Improve the accuracy of forward walsh-hadamard transform
|
||||
Further adjustment of RD behaviour with Q and Zbin.
|
||||
- Build System:
|
||||
Allow linking of libs built with MinGW to MSVC
|
||||
Fix target auto-detection on mingw32
|
||||
Allow --cpu= to work for x86.
|
||||
configure: pass original arguments through to make dist
|
||||
Fix builds without runtime CPU detection
|
||||
msvs: fix install of codec sources
|
||||
msvs: Change devenv.com command line for better msys support
|
||||
msvs: Add vs9 targets.
|
||||
Add x86_64-linux-icc target
|
||||
- Bugs:
|
||||
Potential crashes on older MinGW builds
|
||||
Fix two-pass framrate for Y4M input.
|
||||
Fixed simple loop filter, other crashes on ARM v6
|
||||
arm: fix missing dependency with --enable-shared
|
||||
configure: support directories containing .o
|
||||
Replace pinsrw (SSE) with MMX instructions
|
||||
apple: include proper mach primatives
|
||||
Fixed rate control bug with long key frame interval.
|
||||
Fix DSO link errors on x86-64 when not using a version script
|
||||
Fixed buffer selection for UV in AltRef filtering
|
||||
|
||||
|
||||
2010-06-17 v0.9.1
|
||||
- Enhancements:
|
||||
* ivfenc/ivfdec now support YUV4MPEG2 input and pipe I/O
|
||||
* Speed optimizations
|
||||
- Bugfixes:
|
||||
* Rate control
|
||||
* Prevent out-of-bounds accesses on invalid data
|
||||
- Build system updates:
|
||||
* Detect toolchain to be used automatically for native builds
|
||||
* Support building shared libraries
|
||||
* Better autotools emulation (--prefix, --libdir, DESTDIR)
|
||||
- Updated LICENSE
|
||||
* http://webmproject.blogspot.com/2010/06/changes-to-webm-open-source-license.html
|
||||
|
||||
|
||||
2010-05-18 v0.9.0
|
||||
- Initial open source release. Welcome to WebM and VP8!
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
Copyright (c) 2010, The WebM Project authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google, nor the WebM Project, nor the names
|
||||
of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
------------------------------------
|
||||
|
||||
"These implementations" means the copyrightable works that implement the WebM
|
||||
codecs distributed by Google as part of the WebM Project.
|
||||
|
||||
Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
|
||||
royalty-free, irrevocable (except as stated in this section) patent license to
|
||||
make, have made, use, offer to sell, sell, import, transfer, and otherwise
|
||||
run, modify and propagate the contents of these implementations of WebM, where
|
||||
such license applies only to those patent claims, both currently owned by
|
||||
Google and acquired in the future, licensable by Google that are necessarily
|
||||
infringed by these implementations of WebM. This grant does not include claims
|
||||
that would be infringed only as a consequence of further modification of these
|
||||
implementations. If you or your agent or exclusive licensee institute or order
|
||||
or agree to the institution of patent litigation or any other patent
|
||||
enforcement activity against any entity (including a cross-claim or
|
||||
counterclaim in a lawsuit) alleging that any of these implementations of WebM
|
||||
or any code incorporated within any of these implementations of WebM
|
||||
constitute direct or contributory patent infringement, or inducement of
|
||||
patent infringement, then any patent rights granted to you under this License
|
||||
for these implementations of WebM shall terminate as of the date such
|
||||
litigation is filed.
|
||||
@@ -0,0 +1,169 @@
|
||||
README - 15 July 2019
|
||||
|
||||
Welcome to the WebM VP8/VP9 Codec SDK!
|
||||
|
||||
COMPILING THE APPLICATIONS/LIBRARIES:
|
||||
The build system used is similar to autotools. Building generally consists of
|
||||
"configuring" with your desired build options, then using GNU make to build
|
||||
the application.
|
||||
|
||||
1. Prerequisites
|
||||
|
||||
* All x86 targets require the Yasm[1] assembler be installed[2].
|
||||
* All Windows builds require that Cygwin[3] be installed.
|
||||
* Building the documentation requires Doxygen[4]. If you do not
|
||||
have this package, the install-docs option will be disabled.
|
||||
* Downloading the data for the unit tests requires curl[5] and sha1sum.
|
||||
sha1sum is provided via the GNU coreutils, installed by default on
|
||||
many *nix platforms, as well as MinGW and Cygwin. If coreutils is not
|
||||
available, a compatible version of sha1sum can be built from
|
||||
source[6]. These requirements are optional if not running the unit
|
||||
tests.
|
||||
|
||||
[1]: http://www.tortall.net/projects/yasm
|
||||
[2]: For Visual Studio the base yasm binary (not vsyasm) should be in the
|
||||
PATH for Visual Studio. For VS2017 it is sufficient to rename
|
||||
yasm-<version>-<arch>.exe to yasm.exe and place it in:
|
||||
Program Files (x86)/Microsoft Visual Studio/2017/<level>/Common7/Tools/
|
||||
[3]: http://www.cygwin.com
|
||||
[4]: http://www.doxygen.org
|
||||
[5]: http://curl.haxx.se
|
||||
[6]: http://www.microbrew.org/tools/md5sha1sum/
|
||||
|
||||
2. Out-of-tree builds
|
||||
Out of tree builds are a supported method of building the application. For
|
||||
an out of tree build, the source tree is kept separate from the object
|
||||
files produced during compilation. For instance:
|
||||
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ ../libvpx/configure <options>
|
||||
$ make
|
||||
|
||||
3. Configuration options
|
||||
The 'configure' script supports a number of options. The --help option can be
|
||||
used to get a list of supported options:
|
||||
$ ../libvpx/configure --help
|
||||
|
||||
4. Compiler analyzers
|
||||
Compilers have added sanitizers which instrument binaries with information
|
||||
about address calculation, memory usage, threading, undefined behavior, and
|
||||
other common errors. To simplify building libvpx with some of these features
|
||||
use tools/set_analyzer_env.sh before running configure. It will set the
|
||||
compiler and necessary flags for building as well as environment variables
|
||||
read by the analyzer when testing the binaries.
|
||||
$ source ../libvpx/tools/set_analyzer_env.sh address
|
||||
|
||||
5. Cross development
|
||||
For cross development, the most notable option is the --target option. The
|
||||
most up-to-date list of supported targets can be found at the bottom of the
|
||||
--help output of the configure script. As of this writing, the list of
|
||||
available targets is:
|
||||
|
||||
arm64-android-gcc
|
||||
arm64-darwin-gcc
|
||||
arm64-linux-gcc
|
||||
arm64-win64-gcc
|
||||
arm64-win64-vs15
|
||||
armv7-android-gcc
|
||||
armv7-darwin-gcc
|
||||
armv7-linux-rvct
|
||||
armv7-linux-gcc
|
||||
armv7-none-rvct
|
||||
armv7-win32-gcc
|
||||
armv7-win32-vs14
|
||||
armv7-win32-vs15
|
||||
armv7s-darwin-gcc
|
||||
armv8-linux-gcc
|
||||
mips32-linux-gcc
|
||||
mips64-linux-gcc
|
||||
ppc64le-linux-gcc
|
||||
sparc-solaris-gcc
|
||||
x86-android-gcc
|
||||
x86-darwin8-gcc
|
||||
x86-darwin8-icc
|
||||
x86-darwin9-gcc
|
||||
x86-darwin9-icc
|
||||
x86-darwin10-gcc
|
||||
x86-darwin11-gcc
|
||||
x86-darwin12-gcc
|
||||
x86-darwin13-gcc
|
||||
x86-darwin14-gcc
|
||||
x86-darwin15-gcc
|
||||
x86-darwin16-gcc
|
||||
x86-darwin17-gcc
|
||||
x86-iphonesimulator-gcc
|
||||
x86-linux-gcc
|
||||
x86-linux-icc
|
||||
x86-os2-gcc
|
||||
x86-solaris-gcc
|
||||
x86-win32-gcc
|
||||
x86-win32-vs14
|
||||
x86-win32-vs15
|
||||
x86-win32-vs16
|
||||
x86_64-android-gcc
|
||||
x86_64-darwin9-gcc
|
||||
x86_64-darwin10-gcc
|
||||
x86_64-darwin11-gcc
|
||||
x86_64-darwin12-gcc
|
||||
x86_64-darwin13-gcc
|
||||
x86_64-darwin14-gcc
|
||||
x86_64-darwin15-gcc
|
||||
x86_64-darwin16-gcc
|
||||
x86_64-darwin17-gcc
|
||||
x86_64-darwin18-gcc
|
||||
x86_64-iphonesimulator-gcc
|
||||
x86_64-linux-gcc
|
||||
x86_64-linux-icc
|
||||
x86_64-solaris-gcc
|
||||
x86_64-win64-gcc
|
||||
x86_64-win64-vs14
|
||||
x86_64-win64-vs15
|
||||
x86_64-win64-vs16
|
||||
generic-gnu
|
||||
|
||||
The generic-gnu target, in conjunction with the CROSS environment variable,
|
||||
can be used to cross compile architectures that aren't explicitly listed, if
|
||||
the toolchain is a cross GNU (gcc/binutils) toolchain. Other POSIX toolchains
|
||||
will likely work as well. For instance, to build using the mipsel-linux-uclibc
|
||||
toolchain, the following command could be used (note, POSIX SH syntax, adapt
|
||||
to your shell as necessary):
|
||||
|
||||
$ CROSS=mipsel-linux-uclibc- ../libvpx/configure
|
||||
|
||||
In addition, the executables to be invoked can be overridden by specifying the
|
||||
environment variables: CC, AR, LD, AS, STRIP, NM. Additional flags can be
|
||||
passed to these executables with CFLAGS, LDFLAGS, and ASFLAGS.
|
||||
|
||||
6. Configuration errors
|
||||
If the configuration step fails, the first step is to look in the error log.
|
||||
This defaults to config.log. This should give a good indication of what went
|
||||
wrong. If not, contact us for support.
|
||||
|
||||
VP8/VP9 TEST VECTORS:
|
||||
The test vectors can be downloaded and verified using the build system after
|
||||
running configure. To specify an alternate directory the
|
||||
LIBVPX_TEST_DATA_PATH environment variable can be used.
|
||||
|
||||
$ ./configure --enable-unit-tests
|
||||
$ LIBVPX_TEST_DATA_PATH=../libvpx-test-data make testdata
|
||||
|
||||
CODE STYLE:
|
||||
The coding style used by this project is enforced with clang-format using the
|
||||
configuration contained in the .clang-format file in the root of the
|
||||
repository.
|
||||
|
||||
Before pushing changes for review you can format your code with:
|
||||
# Apply clang-format to modified .c, .h and .cc files
|
||||
$ clang-format -i --style=file \
|
||||
$(git diff --name-only --diff-filter=ACMR '*.[hc]' '*.cc')
|
||||
|
||||
Check the .clang-format file for the version used to generate it if there is
|
||||
any difference between your local formatting and the review system.
|
||||
|
||||
See also: http://clang.llvm.org/docs/ClangFormat.html
|
||||
|
||||
SUPPORT
|
||||
This library is an open source project supported by its community. Please
|
||||
email webm-discuss@webmproject.org for help.
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "args.h"
|
||||
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_ports/msvc.h"
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__
|
||||
extern void die(const char *fmt, ...) __attribute__((noreturn));
|
||||
#else
|
||||
extern void die(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
struct arg arg_init(char **argv) {
|
||||
struct arg a;
|
||||
|
||||
a.argv = argv;
|
||||
a.argv_step = 1;
|
||||
a.name = NULL;
|
||||
a.val = NULL;
|
||||
a.def = NULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
int arg_match(struct arg *arg_, const struct arg_def *def, char **argv) {
|
||||
struct arg arg;
|
||||
|
||||
if (!argv[0] || argv[0][0] != '-') return 0;
|
||||
|
||||
arg = arg_init(argv);
|
||||
|
||||
if (def->short_name && strlen(arg.argv[0]) == strlen(def->short_name) + 1 &&
|
||||
!strcmp(arg.argv[0] + 1, def->short_name)) {
|
||||
arg.name = arg.argv[0] + 1;
|
||||
arg.val = def->has_val ? arg.argv[1] : NULL;
|
||||
arg.argv_step = def->has_val ? 2 : 1;
|
||||
} else if (def->long_name) {
|
||||
const size_t name_len = strlen(def->long_name);
|
||||
|
||||
if (strlen(arg.argv[0]) >= name_len + 2 && arg.argv[0][1] == '-' &&
|
||||
!strncmp(arg.argv[0] + 2, def->long_name, name_len) &&
|
||||
(arg.argv[0][name_len + 2] == '=' ||
|
||||
arg.argv[0][name_len + 2] == '\0')) {
|
||||
arg.name = arg.argv[0] + 2;
|
||||
arg.val = arg.name[name_len] == '=' ? arg.name + name_len + 1 : NULL;
|
||||
arg.argv_step = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.name && !arg.val && def->has_val)
|
||||
die("Error: option %s requires argument.\n", arg.name);
|
||||
|
||||
if (arg.name && arg.val && !def->has_val)
|
||||
die("Error: option %s requires no argument.\n", arg.name);
|
||||
|
||||
if (arg.name && (arg.val || !def->has_val)) {
|
||||
arg.def = def;
|
||||
*arg_ = arg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *arg_next(struct arg *arg) {
|
||||
if (arg->argv[0]) arg->argv += arg->argv_step;
|
||||
|
||||
return *arg->argv;
|
||||
}
|
||||
|
||||
char **argv_dup(int argc, const char **argv) {
|
||||
char **new_argv = malloc((argc + 1) * sizeof(*argv));
|
||||
|
||||
memcpy(new_argv, argv, argc * sizeof(*argv));
|
||||
new_argv[argc] = NULL;
|
||||
return new_argv;
|
||||
}
|
||||
|
||||
void arg_show_usage(FILE *fp, const struct arg_def *const *defs) {
|
||||
char option_text[40] = { 0 };
|
||||
|
||||
for (; *defs; defs++) {
|
||||
const struct arg_def *def = *defs;
|
||||
char *short_val = def->has_val ? " <arg>" : "";
|
||||
char *long_val = def->has_val ? "=<arg>" : "";
|
||||
|
||||
if (def->short_name && def->long_name) {
|
||||
char *comma = def->has_val ? "," : ", ";
|
||||
|
||||
snprintf(option_text, 37, "-%s%s%s --%s%6s", def->short_name, short_val,
|
||||
comma, def->long_name, long_val);
|
||||
} else if (def->short_name)
|
||||
snprintf(option_text, 37, "-%s%s", def->short_name, short_val);
|
||||
else if (def->long_name)
|
||||
snprintf(option_text, 37, " --%s%s", def->long_name, long_val);
|
||||
|
||||
fprintf(fp, " %-37s\t%s\n", option_text, def->desc);
|
||||
|
||||
if (def->enums) {
|
||||
const struct arg_enum_list *listptr;
|
||||
|
||||
fprintf(fp, " %-37s\t ", "");
|
||||
|
||||
for (listptr = def->enums; listptr->name; listptr++)
|
||||
fprintf(fp, "%s%s", listptr->name, listptr[1].name ? ", " : "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int arg_parse_uint(const struct arg *arg) {
|
||||
uint32_t rawval;
|
||||
char *endptr;
|
||||
|
||||
rawval = (uint32_t)strtoul(arg->val, &endptr, 10);
|
||||
|
||||
if (arg->val[0] != '\0' && endptr[0] == '\0') {
|
||||
if (rawval <= UINT_MAX) return rawval;
|
||||
|
||||
die("Option %s: Value %ld out of range for unsigned int\n", arg->name,
|
||||
rawval);
|
||||
}
|
||||
|
||||
die("Option %s: Invalid character '%c'\n", arg->name, *endptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arg_parse_int(const struct arg *arg) {
|
||||
int32_t rawval;
|
||||
char *endptr;
|
||||
|
||||
rawval = (int32_t)strtol(arg->val, &endptr, 10);
|
||||
|
||||
if (arg->val[0] != '\0' && endptr[0] == '\0') {
|
||||
if (rawval >= INT_MIN && rawval <= INT_MAX) return (int)rawval;
|
||||
|
||||
die("Option %s: Value %ld out of range for signed int\n", arg->name,
|
||||
rawval);
|
||||
}
|
||||
|
||||
die("Option %s: Invalid character '%c'\n", arg->name, *endptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vpx_rational {
|
||||
int num; /**< fraction numerator */
|
||||
int den; /**< fraction denominator */
|
||||
};
|
||||
struct vpx_rational arg_parse_rational(const struct arg *arg) {
|
||||
long int rawval;
|
||||
char *endptr;
|
||||
struct vpx_rational rat;
|
||||
|
||||
/* parse numerator */
|
||||
rawval = strtol(arg->val, &endptr, 10);
|
||||
|
||||
if (arg->val[0] != '\0' && endptr[0] == '/') {
|
||||
if (rawval >= INT_MIN && rawval <= INT_MAX)
|
||||
rat.num = (int)rawval;
|
||||
else
|
||||
die("Option %s: Value %ld out of range for signed int\n", arg->name,
|
||||
rawval);
|
||||
} else
|
||||
die("Option %s: Expected / at '%c'\n", arg->name, *endptr);
|
||||
|
||||
/* parse denominator */
|
||||
rawval = strtol(endptr + 1, &endptr, 10);
|
||||
|
||||
if (arg->val[0] != '\0' && endptr[0] == '\0') {
|
||||
if (rawval >= INT_MIN && rawval <= INT_MAX)
|
||||
rat.den = (int)rawval;
|
||||
else
|
||||
die("Option %s: Value %ld out of range for signed int\n", arg->name,
|
||||
rawval);
|
||||
} else
|
||||
die("Option %s: Invalid character '%c'\n", arg->name, *endptr);
|
||||
|
||||
return rat;
|
||||
}
|
||||
|
||||
int arg_parse_enum(const struct arg *arg) {
|
||||
const struct arg_enum_list *listptr;
|
||||
long int rawval;
|
||||
char *endptr;
|
||||
|
||||
/* First see if the value can be parsed as a raw value */
|
||||
rawval = strtol(arg->val, &endptr, 10);
|
||||
if (arg->val[0] != '\0' && endptr[0] == '\0') {
|
||||
/* Got a raw value, make sure it's valid */
|
||||
for (listptr = arg->def->enums; listptr->name; listptr++)
|
||||
if (listptr->val == rawval) return (int)rawval;
|
||||
}
|
||||
|
||||
/* Next see if it can be parsed as a string */
|
||||
for (listptr = arg->def->enums; listptr->name; listptr++)
|
||||
if (!strcmp(arg->val, listptr->name)) return listptr->val;
|
||||
|
||||
die("Option %s: Invalid value '%s'\n", arg->name, arg->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arg_parse_enum_or_int(const struct arg *arg) {
|
||||
if (arg->def->enums) return arg_parse_enum(arg);
|
||||
return arg_parse_int(arg);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef VPX_ARGS_H_
|
||||
#define VPX_ARGS_H_
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct arg {
|
||||
char **argv;
|
||||
const char *name;
|
||||
const char *val;
|
||||
unsigned int argv_step;
|
||||
const struct arg_def *def;
|
||||
};
|
||||
|
||||
struct arg_enum_list {
|
||||
const char *name;
|
||||
int val;
|
||||
};
|
||||
#define ARG_ENUM_LIST_END \
|
||||
{ 0 }
|
||||
|
||||
typedef struct arg_def {
|
||||
const char *short_name;
|
||||
const char *long_name;
|
||||
int has_val;
|
||||
const char *desc;
|
||||
const struct arg_enum_list *enums;
|
||||
} arg_def_t;
|
||||
#define ARG_DEF(s, l, v, d) \
|
||||
{ s, l, v, d, NULL }
|
||||
#define ARG_DEF_ENUM(s, l, v, d, e) \
|
||||
{ s, l, v, d, e }
|
||||
#define ARG_DEF_LIST_END \
|
||||
{ 0 }
|
||||
|
||||
struct arg arg_init(char **argv);
|
||||
int arg_match(struct arg *arg_, const struct arg_def *def, char **argv);
|
||||
const char *arg_next(struct arg *arg);
|
||||
void arg_show_usage(FILE *fp, const struct arg_def *const *defs);
|
||||
char **argv_dup(int argc, const char **argv);
|
||||
|
||||
unsigned int arg_parse_uint(const struct arg *arg);
|
||||
int arg_parse_int(const struct arg *arg);
|
||||
struct vpx_rational arg_parse_rational(const struct arg *arg);
|
||||
int arg_parse_enum(const struct arg *arg);
|
||||
int arg_parse_enum_or_int(const struct arg *arg);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // VPX_ARGS_H_
|
||||
+2
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 646 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 898 KiB |
+2
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
# This file is used by git cl to get repository specific information.
|
||||
GERRIT_HOST: True
|
||||
CODE_REVIEW_SERVER: chromium-review.googlesource.com
|
||||
GERRIT_SQUASH_UPLOADS: False
|
||||
+786
@@ -0,0 +1,786 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## configure
|
||||
##
|
||||
## This script is the front-end to the build system. It provides a similar
|
||||
## interface to standard configure scripts with some extra bits for dealing
|
||||
## with toolchains that differ from the standard POSIX interface and
|
||||
## for extracting subsets of the source tree. In theory, reusable parts
|
||||
## of this script were intended to live in build/make/configure.sh,
|
||||
## but in practice, the line is pretty blurry.
|
||||
##
|
||||
## This build system is based in part on the FFmpeg configure script.
|
||||
##
|
||||
|
||||
#source_path="`dirname \"$0\"`"
|
||||
source_path=${0%/*}
|
||||
. "${source_path}/build/make/configure.sh"
|
||||
|
||||
show_help(){
|
||||
show_help_pre
|
||||
cat << EOF
|
||||
Advanced options:
|
||||
${toggle_libs} libraries
|
||||
${toggle_examples} examples
|
||||
${toggle_tools} tools
|
||||
${toggle_docs} documentation
|
||||
${toggle_unit_tests} unit tests
|
||||
${toggle_decode_perf_tests} build decoder perf tests with unit tests
|
||||
${toggle_encode_perf_tests} build encoder perf tests with unit tests
|
||||
--cpu=CPU tune for the specified CPU (ARM: cortex-a8, X86: sse3)
|
||||
--libc=PATH path to alternate libc
|
||||
--size-limit=WxH max size to allow in the decoder
|
||||
--as={yasm|nasm|auto} use specified assembler [auto, yasm preferred]
|
||||
${toggle_codec_srcs} in/exclude codec library source code
|
||||
${toggle_debug_libs} in/exclude debug version of libraries
|
||||
${toggle_static_msvcrt} use static MSVCRT (VS builds only)
|
||||
${toggle_vp9_highbitdepth} use VP9 high bit depth (10/12) profiles
|
||||
${toggle_better_hw_compatibility}
|
||||
enable encoder to produce streams with better
|
||||
hardware decoder compatibility
|
||||
${toggle_vp8} VP8 codec support
|
||||
${toggle_vp9} VP9 codec support
|
||||
${toggle_internal_stats} output of encoder internal stats for debug, if supported (encoders)
|
||||
${toggle_postproc} postprocessing
|
||||
${toggle_vp9_postproc} vp9 specific postprocessing
|
||||
${toggle_multithread} multithreaded encoding and decoding
|
||||
${toggle_spatial_resampling} spatial sampling (scaling) support
|
||||
${toggle_realtime_only} enable this option while building for real-time encoding
|
||||
${toggle_onthefly_bitpacking} enable on-the-fly bitpacking in real-time encoding
|
||||
${toggle_error_concealment} enable this option to get a decoder which is able to conceal losses
|
||||
${toggle_coefficient_range_checking}
|
||||
enable decoder to check if intermediate
|
||||
transform coefficients are in valid range
|
||||
${toggle_runtime_cpu_detect} runtime cpu detection
|
||||
${toggle_shared} shared library support
|
||||
${toggle_static} static library support
|
||||
${toggle_small} favor smaller size over speed
|
||||
${toggle_postproc_visualizer} macro block / block level visualizers
|
||||
${toggle_multi_res_encoding} enable multiple-resolution encoding
|
||||
${toggle_temporal_denoising} enable temporal denoising and disable the spatial denoiser
|
||||
${toggle_vp9_temporal_denoising}
|
||||
enable vp9 temporal denoising
|
||||
${toggle_webm_io} enable input from and output to WebM container
|
||||
${toggle_libyuv} enable libyuv
|
||||
|
||||
Codecs:
|
||||
Codecs can be selectively enabled or disabled individually, or by family:
|
||||
--disable-<codec>
|
||||
is equivalent to:
|
||||
--disable-<codec>-encoder
|
||||
--disable-<codec>-decoder
|
||||
|
||||
Codecs available in this distribution:
|
||||
EOF
|
||||
#restore editor state '
|
||||
|
||||
family="";
|
||||
last_family="";
|
||||
c="";
|
||||
str="";
|
||||
for c in ${CODECS}; do
|
||||
family=${c%_*}
|
||||
if [ "${family}" != "${last_family}" ]; then
|
||||
[ -z "${str}" ] || echo "${str}"
|
||||
str="$(printf ' %10s:' ${family})"
|
||||
fi
|
||||
str="${str} $(printf '%10s' ${c#*_})"
|
||||
last_family=${family}
|
||||
done
|
||||
echo "${str}"
|
||||
show_help_post
|
||||
}
|
||||
|
||||
##
|
||||
## BEGIN APPLICATION SPECIFIC CONFIGURATION
|
||||
##
|
||||
|
||||
# all_platforms is a list of all supported target platforms. Maintain
|
||||
# alphabetically by architecture, generic-gnu last.
|
||||
all_platforms="${all_platforms} arm64-android-gcc"
|
||||
all_platforms="${all_platforms} arm64-darwin-gcc"
|
||||
all_platforms="${all_platforms} arm64-linux-gcc"
|
||||
all_platforms="${all_platforms} arm64-win64-gcc"
|
||||
all_platforms="${all_platforms} arm64-win64-vs15"
|
||||
all_platforms="${all_platforms} armv7-android-gcc" #neon Cortex-A8
|
||||
all_platforms="${all_platforms} armv7-darwin-gcc" #neon Cortex-A8
|
||||
all_platforms="${all_platforms} armv7-linux-rvct" #neon Cortex-A8
|
||||
all_platforms="${all_platforms} armv7-linux-gcc" #neon Cortex-A8
|
||||
all_platforms="${all_platforms} armv7-none-rvct" #neon Cortex-A8
|
||||
all_platforms="${all_platforms} armv7-win32-gcc"
|
||||
all_platforms="${all_platforms} armv7-win32-vs14"
|
||||
all_platforms="${all_platforms} armv7-win32-vs15"
|
||||
all_platforms="${all_platforms} armv7s-darwin-gcc"
|
||||
all_platforms="${all_platforms} armv8-linux-gcc"
|
||||
all_platforms="${all_platforms} mips32-linux-gcc"
|
||||
all_platforms="${all_platforms} mips64-linux-gcc"
|
||||
all_platforms="${all_platforms} ppc64le-linux-gcc"
|
||||
all_platforms="${all_platforms} sparc-solaris-gcc"
|
||||
all_platforms="${all_platforms} x86-android-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin8-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin8-icc"
|
||||
all_platforms="${all_platforms} x86-darwin9-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin9-icc"
|
||||
all_platforms="${all_platforms} x86-darwin10-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin11-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin12-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin13-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin14-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin15-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin16-gcc"
|
||||
all_platforms="${all_platforms} x86-darwin17-gcc"
|
||||
all_platforms="${all_platforms} x86-iphonesimulator-gcc"
|
||||
all_platforms="${all_platforms} x86-linux-gcc"
|
||||
all_platforms="${all_platforms} x86-linux-icc"
|
||||
all_platforms="${all_platforms} x86-os2-gcc"
|
||||
all_platforms="${all_platforms} x86-solaris-gcc"
|
||||
all_platforms="${all_platforms} x86-win32-gcc"
|
||||
all_platforms="${all_platforms} x86-win32-vs14"
|
||||
all_platforms="${all_platforms} x86-win32-vs15"
|
||||
all_platforms="${all_platforms} x86-win32-vs16"
|
||||
all_platforms="${all_platforms} x86_64-android-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin9-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin10-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin11-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin12-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin13-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin14-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin15-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin16-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin17-gcc"
|
||||
all_platforms="${all_platforms} x86_64-darwin18-gcc"
|
||||
all_platforms="${all_platforms} x86_64-iphonesimulator-gcc"
|
||||
all_platforms="${all_platforms} x86_64-linux-gcc"
|
||||
all_platforms="${all_platforms} x86_64-linux-icc"
|
||||
all_platforms="${all_platforms} x86_64-solaris-gcc"
|
||||
all_platforms="${all_platforms} x86_64-win64-gcc"
|
||||
all_platforms="${all_platforms} x86_64-win64-vs14"
|
||||
all_platforms="${all_platforms} x86_64-win64-vs15"
|
||||
all_platforms="${all_platforms} x86_64-win64-vs16"
|
||||
all_platforms="${all_platforms} generic-gnu"
|
||||
|
||||
# all_targets is a list of all targets that can be configured
|
||||
# note that these should be in dependency order for now.
|
||||
all_targets="libs examples tools docs"
|
||||
|
||||
# all targets available are enabled, by default.
|
||||
for t in ${all_targets}; do
|
||||
[ -f "${source_path}/${t}.mk" ] && enable_feature ${t}
|
||||
done
|
||||
|
||||
if ! diff --version >/dev/null; then
|
||||
die "diff missing: Try installing diffutils via your package manager."
|
||||
fi
|
||||
|
||||
if ! perl --version >/dev/null; then
|
||||
die "Perl is required to build"
|
||||
fi
|
||||
|
||||
if [ "`cd \"${source_path}\" && pwd`" != "`pwd`" ]; then
|
||||
# test to see if source_path already configured
|
||||
if [ -f "${source_path}/vpx_config.h" ]; then
|
||||
die "source directory already configured; run 'make distclean' there first"
|
||||
fi
|
||||
fi
|
||||
|
||||
# check installed doxygen version
|
||||
doxy_version=$(doxygen --version 2>/dev/null)
|
||||
doxy_major=${doxy_version%%.*}
|
||||
if [ ${doxy_major:-0} -ge 1 ]; then
|
||||
doxy_version=${doxy_version#*.}
|
||||
doxy_minor=${doxy_version%%.*}
|
||||
doxy_patch=${doxy_version##*.}
|
||||
|
||||
[ $doxy_major -gt 1 ] && enable_feature doxygen
|
||||
[ $doxy_minor -gt 5 ] && enable_feature doxygen
|
||||
[ $doxy_minor -eq 5 ] && [ $doxy_patch -ge 3 ] && enable_feature doxygen
|
||||
fi
|
||||
|
||||
# disable codecs when their source directory does not exist
|
||||
[ -d "${source_path}/vp8" ] || disable_codec vp8
|
||||
[ -d "${source_path}/vp9" ] || disable_codec vp9
|
||||
|
||||
# install everything except the sources, by default. sources will have
|
||||
# to be enabled when doing dist builds, since that's no longer a common
|
||||
# case.
|
||||
enabled doxygen && enable_feature install_docs
|
||||
enable_feature install_bins
|
||||
enable_feature install_libs
|
||||
|
||||
enable_feature static
|
||||
enable_feature optimizations
|
||||
enable_feature dependency_tracking
|
||||
enable_feature spatial_resampling
|
||||
enable_feature multithread
|
||||
enable_feature os_support
|
||||
enable_feature temporal_denoising
|
||||
|
||||
CODECS="
|
||||
vp8_encoder
|
||||
vp8_decoder
|
||||
vp9_encoder
|
||||
vp9_decoder
|
||||
"
|
||||
CODEC_FAMILIES="
|
||||
vp8
|
||||
vp9
|
||||
"
|
||||
|
||||
ARCH_LIST="
|
||||
arm
|
||||
mips
|
||||
x86
|
||||
x86_64
|
||||
ppc
|
||||
"
|
||||
ARCH_EXT_LIST_X86="
|
||||
mmx
|
||||
sse
|
||||
sse2
|
||||
sse3
|
||||
ssse3
|
||||
sse4_1
|
||||
avx
|
||||
avx2
|
||||
avx512
|
||||
"
|
||||
|
||||
ARCH_EXT_LIST_LOONGSON="
|
||||
mmi
|
||||
"
|
||||
|
||||
ARCH_EXT_LIST="
|
||||
neon
|
||||
neon_asm
|
||||
|
||||
mips32
|
||||
dspr2
|
||||
msa
|
||||
mips64
|
||||
|
||||
${ARCH_EXT_LIST_X86}
|
||||
|
||||
vsx
|
||||
|
||||
${ARCH_EXT_LIST_LOONGSON}
|
||||
"
|
||||
HAVE_LIST="
|
||||
${ARCH_EXT_LIST}
|
||||
vpx_ports
|
||||
pthread_h
|
||||
unistd_h
|
||||
"
|
||||
EXPERIMENT_LIST="
|
||||
fp_mb_stats
|
||||
emulate_hardware
|
||||
non_greedy_mv
|
||||
rate_ctrl
|
||||
"
|
||||
CONFIG_LIST="
|
||||
dependency_tracking
|
||||
external_build
|
||||
install_docs
|
||||
install_bins
|
||||
install_libs
|
||||
install_srcs
|
||||
debug
|
||||
gprof
|
||||
gcov
|
||||
rvct
|
||||
gcc
|
||||
msvs
|
||||
pic
|
||||
big_endian
|
||||
|
||||
codec_srcs
|
||||
debug_libs
|
||||
|
||||
dequant_tokens
|
||||
dc_recon
|
||||
runtime_cpu_detect
|
||||
postproc
|
||||
vp9_postproc
|
||||
multithread
|
||||
internal_stats
|
||||
${CODECS}
|
||||
${CODEC_FAMILIES}
|
||||
encoders
|
||||
decoders
|
||||
static_msvcrt
|
||||
spatial_resampling
|
||||
realtime_only
|
||||
onthefly_bitpacking
|
||||
error_concealment
|
||||
shared
|
||||
static
|
||||
small
|
||||
postproc_visualizer
|
||||
os_support
|
||||
unit_tests
|
||||
webm_io
|
||||
libyuv
|
||||
decode_perf_tests
|
||||
encode_perf_tests
|
||||
multi_res_encoding
|
||||
temporal_denoising
|
||||
vp9_temporal_denoising
|
||||
consistent_recode
|
||||
coefficient_range_checking
|
||||
vp9_highbitdepth
|
||||
better_hw_compatibility
|
||||
experimental
|
||||
size_limit
|
||||
always_adjust_bpm
|
||||
bitstream_debug
|
||||
mismatch_debug
|
||||
${EXPERIMENT_LIST}
|
||||
"
|
||||
CMDLINE_SELECT="
|
||||
dependency_tracking
|
||||
external_build
|
||||
extra_warnings
|
||||
werror
|
||||
install_docs
|
||||
install_bins
|
||||
install_libs
|
||||
install_srcs
|
||||
debug
|
||||
gprof
|
||||
gcov
|
||||
pic
|
||||
optimizations
|
||||
ccache
|
||||
runtime_cpu_detect
|
||||
thumb
|
||||
|
||||
libs
|
||||
examples
|
||||
tools
|
||||
docs
|
||||
libc
|
||||
as
|
||||
size_limit
|
||||
codec_srcs
|
||||
debug_libs
|
||||
|
||||
dequant_tokens
|
||||
dc_recon
|
||||
postproc
|
||||
vp9_postproc
|
||||
multithread
|
||||
internal_stats
|
||||
${CODECS}
|
||||
${CODEC_FAMILIES}
|
||||
static_msvcrt
|
||||
spatial_resampling
|
||||
realtime_only
|
||||
onthefly_bitpacking
|
||||
error_concealment
|
||||
shared
|
||||
static
|
||||
small
|
||||
postproc_visualizer
|
||||
unit_tests
|
||||
webm_io
|
||||
libyuv
|
||||
decode_perf_tests
|
||||
encode_perf_tests
|
||||
multi_res_encoding
|
||||
temporal_denoising
|
||||
vp9_temporal_denoising
|
||||
consistent_recode
|
||||
coefficient_range_checking
|
||||
better_hw_compatibility
|
||||
vp9_highbitdepth
|
||||
experimental
|
||||
always_adjust_bpm
|
||||
bitstream_debug
|
||||
mismatch_debug
|
||||
"
|
||||
|
||||
process_cmdline() {
|
||||
for opt do
|
||||
optval="${opt#*=}"
|
||||
case "$opt" in
|
||||
--disable-codecs)
|
||||
for c in ${CODEC_FAMILIES}; do disable_codec $c; done
|
||||
;;
|
||||
--enable-?*|--disable-?*)
|
||||
eval `echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'`
|
||||
if is_in ${option} ${EXPERIMENT_LIST}; then
|
||||
if enabled experimental; then
|
||||
${action}_feature $option
|
||||
else
|
||||
log_echo "Ignoring $opt -- not in experimental mode."
|
||||
fi
|
||||
elif is_in ${option} "${CODECS} ${CODEC_FAMILIES}"; then
|
||||
${action}_codec ${option}
|
||||
else
|
||||
process_common_cmdline $opt
|
||||
fi
|
||||
;;
|
||||
*) process_common_cmdline "$opt"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
post_process_cmdline() {
|
||||
if enabled coefficient_range_checking; then
|
||||
echo "coefficient-range-checking is for decoders only, disabling encoders:"
|
||||
soft_disable vp8_encoder
|
||||
soft_disable vp9_encoder
|
||||
fi
|
||||
|
||||
c=""
|
||||
|
||||
# Enable all detected codecs, if they haven't been disabled
|
||||
for c in ${CODECS}; do soft_enable $c; done
|
||||
|
||||
# Enable the codec family if any component of that family is enabled
|
||||
for c in ${CODECS}; do
|
||||
enabled $c && enable_feature ${c%_*}
|
||||
done
|
||||
|
||||
# Set the {en,de}coders variable if any algorithm in that class is enabled
|
||||
for c in ${CODECS}; do
|
||||
enabled ${c} && enable_feature ${c##*_}s
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
process_targets() {
|
||||
enabled child || write_common_config_banner
|
||||
write_common_target_config_h ${BUILD_PFX}vpx_config.h
|
||||
write_common_config_targets
|
||||
enabled win_arm64_neon_h_workaround && write_win_arm64_neon_h_workaround ${BUILD_PFX}arm_neon.h
|
||||
|
||||
# Calculate the default distribution name, based on the enabled features
|
||||
cf=""
|
||||
DIST_DIR=vpx
|
||||
for cf in $CODEC_FAMILIES; do
|
||||
if enabled ${cf}_encoder && enabled ${cf}_decoder; then
|
||||
DIST_DIR="${DIST_DIR}-${cf}"
|
||||
elif enabled ${cf}_encoder; then
|
||||
DIST_DIR="${DIST_DIR}-${cf}cx"
|
||||
elif enabled ${cf}_decoder; then
|
||||
DIST_DIR="${DIST_DIR}-${cf}dx"
|
||||
fi
|
||||
done
|
||||
enabled debug_libs && DIST_DIR="${DIST_DIR}-debug"
|
||||
enabled codec_srcs && DIST_DIR="${DIST_DIR}-src"
|
||||
! enabled postproc && ! enabled vp9_postproc && DIST_DIR="${DIST_DIR}-nopost"
|
||||
! enabled multithread && DIST_DIR="${DIST_DIR}-nomt"
|
||||
! enabled install_docs && DIST_DIR="${DIST_DIR}-nodocs"
|
||||
DIST_DIR="${DIST_DIR}-${tgt_isa}-${tgt_os}"
|
||||
case "${tgt_os}" in
|
||||
win*) enabled static_msvcrt && DIST_DIR="${DIST_DIR}mt" || DIST_DIR="${DIST_DIR}md"
|
||||
DIST_DIR="${DIST_DIR}-${tgt_cc}"
|
||||
;;
|
||||
esac
|
||||
if [ -f "${source_path}/build/make/version.sh" ]; then
|
||||
ver=`"$source_path/build/make/version.sh" --bare "$source_path"`
|
||||
DIST_DIR="${DIST_DIR}-${ver}"
|
||||
VERSION_STRING=${ver}
|
||||
ver=${ver%%-*}
|
||||
VERSION_PATCH=${ver##*.}
|
||||
ver=${ver%.*}
|
||||
VERSION_MINOR=${ver##*.}
|
||||
ver=${ver#v}
|
||||
VERSION_MAJOR=${ver%.*}
|
||||
fi
|
||||
enabled child || cat <<EOF >> config.mk
|
||||
|
||||
PREFIX=${prefix}
|
||||
ifeq (\$(MAKECMDGOALS),dist)
|
||||
DIST_DIR?=${DIST_DIR}
|
||||
else
|
||||
DIST_DIR?=\$(DESTDIR)${prefix}
|
||||
endif
|
||||
LIBSUBDIR=${libdir##${prefix}/}
|
||||
|
||||
VERSION_STRING=${VERSION_STRING}
|
||||
|
||||
VERSION_MAJOR=${VERSION_MAJOR}
|
||||
VERSION_MINOR=${VERSION_MINOR}
|
||||
VERSION_PATCH=${VERSION_PATCH}
|
||||
|
||||
CONFIGURE_ARGS=${CONFIGURE_ARGS}
|
||||
EOF
|
||||
enabled child || echo "CONFIGURE_ARGS?=${CONFIGURE_ARGS}" >> config.mk
|
||||
|
||||
#
|
||||
# Write makefiles for all enabled targets
|
||||
#
|
||||
for tgt in libs examples tools docs solution; do
|
||||
tgt_fn="$tgt-$toolchain.mk"
|
||||
|
||||
if enabled $tgt; then
|
||||
echo "Creating makefiles for ${toolchain} ${tgt}"
|
||||
write_common_target_config_mk $tgt_fn ${BUILD_PFX}vpx_config.h
|
||||
#write_${tgt}_config
|
||||
fi
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
process_detect() {
|
||||
if enabled shared; then
|
||||
# Can only build shared libs on a subset of platforms. Doing this check
|
||||
# here rather than at option parse time because the target auto-detect
|
||||
# magic happens after the command line has been parsed.
|
||||
case "${tgt_os}" in
|
||||
linux|os2|solaris|darwin*|iphonesimulator*)
|
||||
# Supported platforms
|
||||
;;
|
||||
*)
|
||||
if enabled gnu; then
|
||||
echo "--enable-shared is only supported on ELF; assuming this is OK"
|
||||
else
|
||||
die "--enable-shared only supported on ELF, OS/2, and Darwin for now"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ -z "$CC" ] || enabled external_build; then
|
||||
echo "Bypassing toolchain for environment detection."
|
||||
enable_feature external_build
|
||||
check_header() {
|
||||
log fake_check_header "$@"
|
||||
header=$1
|
||||
shift
|
||||
var=`echo $header | sed 's/[^A-Za-z0-9_]/_/g'`
|
||||
disable_feature $var
|
||||
# Headers common to all environments
|
||||
case $header in
|
||||
stdio.h)
|
||||
true;
|
||||
;;
|
||||
*)
|
||||
result=false
|
||||
for d in "$@"; do
|
||||
[ -f "${d##-I}/$header" ] && result=true && break
|
||||
done
|
||||
${result:-true}
|
||||
esac && enable_feature $var
|
||||
|
||||
# Specialize windows and POSIX environments.
|
||||
case $toolchain in
|
||||
*-win*-*)
|
||||
# Don't check for any headers in Windows builds.
|
||||
false
|
||||
;;
|
||||
*)
|
||||
case $header in
|
||||
pthread.h) true;;
|
||||
unistd.h) true;;
|
||||
*) false;;
|
||||
esac && enable_feature $var
|
||||
esac
|
||||
enabled $var
|
||||
}
|
||||
check_ld() {
|
||||
true
|
||||
}
|
||||
check_lib() {
|
||||
true
|
||||
}
|
||||
fi
|
||||
check_header stdio.h || die "Unable to invoke compiler: ${CC} ${CFLAGS}"
|
||||
check_ld <<EOF || die "Toolchain is unable to link executables"
|
||||
int main(void) {return 0;}
|
||||
EOF
|
||||
# check system headers
|
||||
|
||||
# Use both check_header and check_lib here, since check_lib
|
||||
# could be a stub that always returns true.
|
||||
check_header pthread.h && check_lib -lpthread <<EOF || disable_feature pthread_h
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
int main(void) { return pthread_create(NULL, NULL, NULL, NULL); }
|
||||
EOF
|
||||
check_header unistd.h # for sysconf(3) and friends.
|
||||
|
||||
check_header vpx/vpx_integer.h -I${source_path} && enable_feature vpx_ports
|
||||
|
||||
if enabled neon && ! enabled external_build; then
|
||||
check_header arm_neon.h || die "Unable to find arm_neon.h"
|
||||
fi
|
||||
}
|
||||
|
||||
process_toolchain() {
|
||||
process_common_toolchain
|
||||
|
||||
# Enable some useful compiler flags
|
||||
if enabled gcc; then
|
||||
enabled werror && check_add_cflags -Werror
|
||||
check_add_cflags -Wall
|
||||
check_add_cflags -Wdeclaration-after-statement
|
||||
check_add_cflags -Wdisabled-optimization
|
||||
check_add_cflags -Wfloat-conversion
|
||||
check_add_cflags -Wparentheses-equality
|
||||
check_add_cflags -Wpointer-arith
|
||||
check_add_cflags -Wtype-limits
|
||||
check_add_cflags -Wcast-qual
|
||||
check_add_cflags -Wvla
|
||||
check_add_cflags -Wimplicit-function-declaration
|
||||
check_add_cflags -Wmissing-declarations
|
||||
check_add_cflags -Wmissing-prototypes
|
||||
check_add_cflags -Wuninitialized
|
||||
check_add_cflags -Wunused
|
||||
check_add_cflags -Wextra
|
||||
# check_add_cflags also adds to cxxflags. gtest does not do well with
|
||||
# these flags so add them explicitly to CFLAGS only.
|
||||
check_cflags -Wundef && add_cflags_only -Wundef
|
||||
check_cflags -Wframe-larger-than=52000 && \
|
||||
add_cflags_only -Wframe-larger-than=52000
|
||||
if enabled mips || [ -z "${INLINE}" ]; then
|
||||
enabled extra_warnings || check_add_cflags -Wno-unused-function
|
||||
fi
|
||||
# Enforce c89 for c files. Don't be too strict about it though. Allow
|
||||
# gnu extensions like "//" for comments.
|
||||
check_cflags -std=gnu89 && add_cflags_only -std=gnu89
|
||||
# Avoid this warning for third_party C++ sources. Some reorganization
|
||||
# would be needed to apply this only to test/*.cc.
|
||||
check_cflags -Wshorten-64-to-32 && add_cflags_only -Wshorten-64-to-32
|
||||
|
||||
# Quiet gcc 6 vs 7 abi warnings:
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77728
|
||||
if enabled arm; then
|
||||
check_add_cxxflags -Wno-psabi
|
||||
fi
|
||||
|
||||
# disable some warnings specific to libyuv.
|
||||
check_cxxflags -Wno-missing-declarations \
|
||||
&& LIBYUV_CXXFLAGS="${LIBYUV_CXXFLAGS} -Wno-missing-declarations"
|
||||
check_cxxflags -Wno-missing-prototypes \
|
||||
&& LIBYUV_CXXFLAGS="${LIBYUV_CXXFLAGS} -Wno-missing-prototypes"
|
||||
check_cxxflags -Wno-unused-parameter \
|
||||
&& LIBYUV_CXXFLAGS="${LIBYUV_CXXFLAGS} -Wno-unused-parameter"
|
||||
fi
|
||||
|
||||
if enabled icc; then
|
||||
enabled werror && check_add_cflags -Werror
|
||||
check_add_cflags -Wall
|
||||
check_add_cflags -Wpointer-arith
|
||||
|
||||
# ICC has a number of floating point optimizations that we disable
|
||||
# in favor of deterministic output WRT to other compilers
|
||||
add_cflags -fp-model precise
|
||||
fi
|
||||
|
||||
# Enable extra, harmless warnings. These might provide additional insight
|
||||
# to what the compiler is doing and why, but in general, but they shouldn't
|
||||
# be treated as fatal, even if we're treating warnings as errors.
|
||||
GCC_EXTRA_WARNINGS="
|
||||
-Wdisabled-optimization
|
||||
-Winline
|
||||
"
|
||||
enabled gcc && EXTRA_WARNINGS="${GCC_EXTRA_WARNINGS}"
|
||||
RVCT_EXTRA_WARNINGS="
|
||||
--remarks
|
||||
"
|
||||
enabled rvct && EXTRA_WARNINGS="${RVCT_EXTRA_WARNINGS}"
|
||||
if enabled extra_warnings; then
|
||||
for w in ${EXTRA_WARNINGS}; do
|
||||
check_add_cflags ${w}
|
||||
enabled gcc && enabled werror && check_add_cflags -Wno-error=${w}
|
||||
done
|
||||
fi
|
||||
|
||||
# ccache only really works on gcc toolchains
|
||||
enabled gcc || soft_disable ccache
|
||||
if enabled mips; then
|
||||
enable_feature dequant_tokens
|
||||
enable_feature dc_recon
|
||||
fi
|
||||
|
||||
if enabled internal_stats; then
|
||||
enable_feature vp9_postproc
|
||||
fi
|
||||
|
||||
# Enable the postbuild target if building for visual studio.
|
||||
case "$tgt_cc" in
|
||||
vs*) enable_feature msvs
|
||||
enable_feature solution
|
||||
vs_version=${tgt_cc##vs}
|
||||
VCPROJ_SFX=vcxproj
|
||||
gen_vcproj_cmd=${source_path}/build/make/gen_msvs_vcxproj.sh
|
||||
enabled werror && gen_vcproj_cmd="${gen_vcproj_cmd} --enable-werror"
|
||||
all_targets="${all_targets} solution"
|
||||
INLINE="__inline"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Other toolchain specific defaults
|
||||
case $toolchain in x86*) soft_enable postproc;; esac
|
||||
|
||||
if enabled postproc_visualizer; then
|
||||
enabled postproc || die "postproc_visualizer requires postproc to be enabled"
|
||||
fi
|
||||
|
||||
# Enable unit tests by default if we have a working C++ compiler.
|
||||
case "$toolchain" in
|
||||
*-vs*)
|
||||
soft_enable unit_tests
|
||||
soft_enable webm_io
|
||||
soft_enable libyuv
|
||||
;;
|
||||
*-android-*)
|
||||
check_add_cxxflags -std=c++11 && soft_enable webm_io
|
||||
soft_enable libyuv
|
||||
# GTestLog must be modified to use Android logging utilities.
|
||||
;;
|
||||
*-darwin-*)
|
||||
# iOS/ARM builds do not work with gtest. This does not match
|
||||
# x86 targets.
|
||||
;;
|
||||
*-iphonesimulator-*)
|
||||
check_add_cxxflags -std=c++11 && soft_enable webm_io
|
||||
soft_enable libyuv
|
||||
;;
|
||||
*-win*)
|
||||
# Some mingw toolchains don't have pthread available by default.
|
||||
# Treat these more like visual studio where threading in gtest
|
||||
# would be disabled for the same reason.
|
||||
check_add_cxxflags -std=c++11 && soft_enable unit_tests \
|
||||
&& soft_enable webm_io
|
||||
check_cxx "$@" <<EOF && soft_enable libyuv
|
||||
int z;
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
enabled pthread_h && check_add_cxxflags -std=c++11 \
|
||||
&& soft_enable unit_tests
|
||||
check_add_cxxflags -std=c++11 && soft_enable webm_io
|
||||
check_cxx "$@" <<EOF && soft_enable libyuv
|
||||
int z;
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
# libwebm needs to be linked with C++ standard library
|
||||
enabled webm_io && LD=${CXX}
|
||||
|
||||
# append any user defined extra cflags
|
||||
if [ -n "${extra_cflags}" ] ; then
|
||||
check_add_cflags ${extra_cflags} || \
|
||||
die "Requested extra CFLAGS '${extra_cflags}' not supported by compiler"
|
||||
fi
|
||||
if [ -n "${extra_cxxflags}" ]; then
|
||||
check_add_cxxflags ${extra_cxxflags} || \
|
||||
die "Requested extra CXXFLAGS '${extra_cxxflags}' not supported by compiler"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## END APPLICATION SPECIFIC CONFIGURATION
|
||||
##
|
||||
CONFIGURE_ARGS="$@"
|
||||
process "$@"
|
||||
print_webm_license ${BUILD_PFX}vpx_config.c "/*" " */"
|
||||
cat <<EOF >> ${BUILD_PFX}vpx_config.c
|
||||
#include "vpx/vpx_codec.h"
|
||||
static const char* const cfg = "$CONFIGURE_ARGS";
|
||||
const char *vpx_codec_build_config(void) {return cfg;}
|
||||
EOF
|
||||
@@ -0,0 +1,48 @@
|
||||
##
|
||||
## Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
|
||||
|
||||
INSTALL_MAPS += docs/% docs/%
|
||||
INSTALL_MAPS += src/% %
|
||||
INSTALL_MAPS += % %
|
||||
|
||||
# Static documentation authored in doxygen
|
||||
CODEC_DOX := mainpage.dox \
|
||||
keywords.dox \
|
||||
usage.dox \
|
||||
usage_cx.dox \
|
||||
usage_dx.dox \
|
||||
|
||||
# Other doxy files sourced in Markdown
|
||||
TXT_DOX = $(call enabled,TXT_DOX)
|
||||
|
||||
EXAMPLE_PATH += $(SRC_PATH_BARE) #for CHANGELOG, README, etc
|
||||
EXAMPLE_PATH += $(SRC_PATH_BARE)/examples
|
||||
|
||||
doxyfile: $(if $(findstring examples, $(ALL_TARGETS)),examples.doxy)
|
||||
doxyfile: libs.doxy_template libs.doxy
|
||||
@echo " [CREATE] $@"
|
||||
@cat $^ > $@
|
||||
@echo "STRIP_FROM_PATH += $(SRC_PATH_BARE) $(BUILD_ROOT)" >> $@
|
||||
@echo "INPUT += $(addprefix $(SRC_PATH_BARE)/,$(CODEC_DOX))" >> $@;
|
||||
@echo "INPUT += $(TXT_DOX)" >> $@;
|
||||
@echo "EXAMPLE_PATH += $(EXAMPLE_PATH)" >> $@
|
||||
|
||||
CLEAN-OBJS += doxyfile $(wildcard docs/html/*)
|
||||
docs/html/index.html: doxyfile $(CODEC_DOX) $(TXT_DOX)
|
||||
@echo " [DOXYGEN] $<"
|
||||
@doxygen $<
|
||||
DOCS-yes += docs/html/index.html
|
||||
|
||||
DIST-DOCS-yes = $(wildcard docs/html/*)
|
||||
DIST-DOCS-$(CONFIG_CODEC_SRCS) += $(addprefix src/,$(CODEC_DOX))
|
||||
DIST-DOCS-$(CONFIG_CODEC_SRCS) += src/libs.doxy_template
|
||||
DIST-DOCS-yes += CHANGELOG
|
||||
DIST-DOCS-yes += README
|
||||
@@ -0,0 +1,421 @@
|
||||
##
|
||||
## Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
|
||||
LIBYUV_SRCS += third_party/libyuv/include/libyuv/basic_types.h \
|
||||
third_party/libyuv/include/libyuv/convert.h \
|
||||
third_party/libyuv/include/libyuv/convert_argb.h \
|
||||
third_party/libyuv/include/libyuv/convert_from.h \
|
||||
third_party/libyuv/include/libyuv/cpu_id.h \
|
||||
third_party/libyuv/include/libyuv/planar_functions.h \
|
||||
third_party/libyuv/include/libyuv/rotate.h \
|
||||
third_party/libyuv/include/libyuv/row.h \
|
||||
third_party/libyuv/include/libyuv/scale.h \
|
||||
third_party/libyuv/include/libyuv/scale_row.h \
|
||||
third_party/libyuv/source/cpu_id.cc \
|
||||
third_party/libyuv/source/planar_functions.cc \
|
||||
third_party/libyuv/source/row_any.cc \
|
||||
third_party/libyuv/source/row_common.cc \
|
||||
third_party/libyuv/source/row_gcc.cc \
|
||||
third_party/libyuv/source/row_msa.cc \
|
||||
third_party/libyuv/source/row_neon.cc \
|
||||
third_party/libyuv/source/row_neon64.cc \
|
||||
third_party/libyuv/source/row_win.cc \
|
||||
third_party/libyuv/source/scale.cc \
|
||||
third_party/libyuv/source/scale_any.cc \
|
||||
third_party/libyuv/source/scale_common.cc \
|
||||
third_party/libyuv/source/scale_gcc.cc \
|
||||
third_party/libyuv/source/scale_msa.cc \
|
||||
third_party/libyuv/source/scale_neon.cc \
|
||||
third_party/libyuv/source/scale_neon64.cc \
|
||||
third_party/libyuv/source/scale_win.cc \
|
||||
|
||||
LIBWEBM_COMMON_SRCS += third_party/libwebm/common/hdr_util.cc \
|
||||
third_party/libwebm/common/hdr_util.h \
|
||||
third_party/libwebm/common/webmids.h
|
||||
|
||||
LIBWEBM_MUXER_SRCS += third_party/libwebm/mkvmuxer/mkvmuxer.cc \
|
||||
third_party/libwebm/mkvmuxer/mkvmuxerutil.cc \
|
||||
third_party/libwebm/mkvmuxer/mkvwriter.cc \
|
||||
third_party/libwebm/mkvmuxer/mkvmuxer.h \
|
||||
third_party/libwebm/mkvmuxer/mkvmuxertypes.h \
|
||||
third_party/libwebm/mkvmuxer/mkvmuxerutil.h \
|
||||
third_party/libwebm/mkvparser/mkvparser.h \
|
||||
third_party/libwebm/mkvmuxer/mkvwriter.h
|
||||
|
||||
LIBWEBM_PARSER_SRCS = third_party/libwebm/mkvparser/mkvparser.cc \
|
||||
third_party/libwebm/mkvparser/mkvreader.cc \
|
||||
third_party/libwebm/mkvparser/mkvparser.h \
|
||||
third_party/libwebm/mkvparser/mkvreader.h
|
||||
|
||||
# Add compile flags and include path for libwebm sources.
|
||||
ifeq ($(CONFIG_WEBM_IO),yes)
|
||||
CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS
|
||||
INC_PATH-yes += $(SRC_PATH_BARE)/third_party/libwebm
|
||||
endif
|
||||
|
||||
|
||||
# List of examples to build. UTILS are tools meant for distribution
|
||||
# while EXAMPLES demonstrate specific portions of the API.
|
||||
UTILS-$(CONFIG_DECODERS) += vpxdec.c
|
||||
vpxdec.SRCS += md5_utils.c md5_utils.h
|
||||
vpxdec.SRCS += vpx_ports/mem_ops.h
|
||||
vpxdec.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
vpxdec.SRCS += vpx_ports/msvc.h
|
||||
vpxdec.SRCS += vpx_ports/vpx_timer.h
|
||||
vpxdec.SRCS += vpx/vpx_integer.h
|
||||
vpxdec.SRCS += args.c args.h
|
||||
vpxdec.SRCS += ivfdec.c ivfdec.h
|
||||
vpxdec.SRCS += y4minput.c y4minput.h
|
||||
vpxdec.SRCS += tools_common.c tools_common.h
|
||||
vpxdec.SRCS += y4menc.c y4menc.h
|
||||
ifeq ($(CONFIG_LIBYUV),yes)
|
||||
vpxdec.SRCS += $(LIBYUV_SRCS)
|
||||
$(BUILD_PFX)third_party/libyuv/%.cc.o: CXXFLAGS += ${LIBYUV_CXXFLAGS}
|
||||
endif
|
||||
ifeq ($(CONFIG_WEBM_IO),yes)
|
||||
vpxdec.SRCS += $(LIBWEBM_COMMON_SRCS)
|
||||
vpxdec.SRCS += $(LIBWEBM_MUXER_SRCS)
|
||||
vpxdec.SRCS += $(LIBWEBM_PARSER_SRCS)
|
||||
vpxdec.SRCS += webmdec.cc webmdec.h
|
||||
endif
|
||||
vpxdec.GUID = BA5FE66F-38DD-E034-F542-B1578C5FB950
|
||||
vpxdec.DESCRIPTION = Full featured decoder
|
||||
UTILS-$(CONFIG_ENCODERS) += vpxenc.c
|
||||
vpxenc.SRCS += args.c args.h y4minput.c y4minput.h vpxenc.h
|
||||
vpxenc.SRCS += ivfdec.c ivfdec.h
|
||||
vpxenc.SRCS += ivfenc.c ivfenc.h
|
||||
vpxenc.SRCS += rate_hist.c rate_hist.h
|
||||
vpxenc.SRCS += tools_common.c tools_common.h
|
||||
vpxenc.SRCS += warnings.c warnings.h
|
||||
vpxenc.SRCS += vpx_ports/mem_ops.h
|
||||
vpxenc.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
vpxenc.SRCS += vpx_ports/msvc.h
|
||||
vpxenc.SRCS += vpx_ports/vpx_timer.h
|
||||
vpxenc.SRCS += vpxstats.c vpxstats.h
|
||||
ifeq ($(CONFIG_LIBYUV),yes)
|
||||
vpxenc.SRCS += $(LIBYUV_SRCS)
|
||||
endif
|
||||
ifeq ($(CONFIG_WEBM_IO),yes)
|
||||
vpxenc.SRCS += $(LIBWEBM_COMMON_SRCS)
|
||||
vpxenc.SRCS += $(LIBWEBM_MUXER_SRCS)
|
||||
vpxenc.SRCS += $(LIBWEBM_PARSER_SRCS)
|
||||
vpxenc.SRCS += webmenc.cc webmenc.h
|
||||
endif
|
||||
vpxenc.GUID = 548DEC74-7A15-4B2B-AFC3-AA102E7C25C1
|
||||
vpxenc.DESCRIPTION = Full featured encoder
|
||||
|
||||
EXAMPLES-$(CONFIG_VP9_ENCODER) += vp9_spatial_svc_encoder.c
|
||||
vp9_spatial_svc_encoder.SRCS += args.c args.h
|
||||
vp9_spatial_svc_encoder.SRCS += ivfenc.c ivfenc.h
|
||||
vp9_spatial_svc_encoder.SRCS += y4minput.c y4minput.h
|
||||
vp9_spatial_svc_encoder.SRCS += tools_common.c tools_common.h
|
||||
vp9_spatial_svc_encoder.SRCS += video_common.h
|
||||
vp9_spatial_svc_encoder.SRCS += video_writer.h video_writer.c
|
||||
vp9_spatial_svc_encoder.SRCS += vpx_ports/msvc.h
|
||||
vp9_spatial_svc_encoder.SRCS += vpxstats.c vpxstats.h
|
||||
vp9_spatial_svc_encoder.SRCS += examples/svc_encodeframe.c
|
||||
vp9_spatial_svc_encoder.SRCS += examples/svc_context.h
|
||||
vp9_spatial_svc_encoder.GUID = 4A38598D-627D-4505-9C7B-D4020C84100D
|
||||
vp9_spatial_svc_encoder.DESCRIPTION = VP9 Spatial SVC Encoder
|
||||
|
||||
ifneq ($(CONFIG_SHARED),yes)
|
||||
EXAMPLES-$(CONFIG_VP9_ENCODER) += resize_util.c
|
||||
endif
|
||||
|
||||
EXAMPLES-$(CONFIG_ENCODERS) += vpx_temporal_svc_encoder.c
|
||||
vpx_temporal_svc_encoder.SRCS += ivfenc.c ivfenc.h
|
||||
vpx_temporal_svc_encoder.SRCS += y4minput.c y4minput.h
|
||||
vpx_temporal_svc_encoder.SRCS += tools_common.c tools_common.h
|
||||
vpx_temporal_svc_encoder.SRCS += video_common.h
|
||||
vpx_temporal_svc_encoder.SRCS += video_writer.h video_writer.c
|
||||
vpx_temporal_svc_encoder.SRCS += vpx_ports/msvc.h
|
||||
vpx_temporal_svc_encoder.GUID = B18C08F2-A439-4502-A78E-849BE3D60947
|
||||
vpx_temporal_svc_encoder.DESCRIPTION = Temporal SVC Encoder
|
||||
EXAMPLES-$(CONFIG_DECODERS) += simple_decoder.c
|
||||
simple_decoder.GUID = D3BBF1E9-2427-450D-BBFF-B2843C1D44CC
|
||||
simple_decoder.SRCS += ivfdec.h ivfdec.c
|
||||
simple_decoder.SRCS += y4minput.c y4minput.h
|
||||
simple_decoder.SRCS += tools_common.h tools_common.c
|
||||
simple_decoder.SRCS += video_common.h
|
||||
simple_decoder.SRCS += video_reader.h video_reader.c
|
||||
simple_decoder.SRCS += vpx_ports/mem_ops.h
|
||||
simple_decoder.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
simple_decoder.SRCS += vpx_ports/msvc.h
|
||||
simple_decoder.DESCRIPTION = Simplified decoder loop
|
||||
EXAMPLES-$(CONFIG_DECODERS) += postproc.c
|
||||
postproc.SRCS += ivfdec.h ivfdec.c
|
||||
postproc.SRCS += y4minput.c y4minput.h
|
||||
postproc.SRCS += tools_common.h tools_common.c
|
||||
postproc.SRCS += video_common.h
|
||||
postproc.SRCS += video_reader.h video_reader.c
|
||||
postproc.SRCS += vpx_ports/mem_ops.h
|
||||
postproc.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
postproc.SRCS += vpx_ports/msvc.h
|
||||
postproc.GUID = 65E33355-F35E-4088-884D-3FD4905881D7
|
||||
postproc.DESCRIPTION = Decoder postprocessor control
|
||||
EXAMPLES-$(CONFIG_DECODERS) += decode_to_md5.c
|
||||
decode_to_md5.SRCS += md5_utils.h md5_utils.c
|
||||
decode_to_md5.SRCS += ivfdec.h ivfdec.c
|
||||
decode_to_md5.SRCS += y4minput.c y4minput.h
|
||||
decode_to_md5.SRCS += tools_common.h tools_common.c
|
||||
decode_to_md5.SRCS += video_common.h
|
||||
decode_to_md5.SRCS += video_reader.h video_reader.c
|
||||
decode_to_md5.SRCS += vpx_ports/mem_ops.h
|
||||
decode_to_md5.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
decode_to_md5.SRCS += vpx_ports/msvc.h
|
||||
decode_to_md5.GUID = 59120B9B-2735-4BFE-B022-146CA340FE42
|
||||
decode_to_md5.DESCRIPTION = Frame by frame MD5 checksum
|
||||
EXAMPLES-$(CONFIG_ENCODERS) += simple_encoder.c
|
||||
simple_encoder.SRCS += ivfenc.h ivfenc.c
|
||||
simple_encoder.SRCS += y4minput.c y4minput.h
|
||||
simple_encoder.SRCS += tools_common.h tools_common.c
|
||||
simple_encoder.SRCS += video_common.h
|
||||
simple_encoder.SRCS += video_writer.h video_writer.c
|
||||
simple_encoder.SRCS += vpx_ports/msvc.h
|
||||
simple_encoder.GUID = 4607D299-8A71-4D2C-9B1D-071899B6FBFD
|
||||
simple_encoder.DESCRIPTION = Simplified encoder loop
|
||||
EXAMPLES-$(CONFIG_VP9_ENCODER) += vp9_lossless_encoder.c
|
||||
vp9_lossless_encoder.SRCS += ivfenc.h ivfenc.c
|
||||
vp9_lossless_encoder.SRCS += y4minput.c y4minput.h
|
||||
vp9_lossless_encoder.SRCS += tools_common.h tools_common.c
|
||||
vp9_lossless_encoder.SRCS += video_common.h
|
||||
vp9_lossless_encoder.SRCS += video_writer.h video_writer.c
|
||||
vp9_lossless_encoder.SRCS += vpx_ports/msvc.h
|
||||
vp9_lossless_encoder.GUID = B63C7C88-5348-46DC-A5A6-CC151EF93366
|
||||
vp9_lossless_encoder.DESCRIPTION = Simplified lossless VP9 encoder
|
||||
EXAMPLES-$(CONFIG_ENCODERS) += twopass_encoder.c
|
||||
twopass_encoder.SRCS += ivfenc.h ivfenc.c
|
||||
twopass_encoder.SRCS += y4minput.c y4minput.h
|
||||
twopass_encoder.SRCS += tools_common.h tools_common.c
|
||||
twopass_encoder.SRCS += video_common.h
|
||||
twopass_encoder.SRCS += video_writer.h video_writer.c
|
||||
twopass_encoder.SRCS += vpx_ports/msvc.h
|
||||
twopass_encoder.GUID = 73494FA6-4AF9-4763-8FBB-265C92402FD8
|
||||
twopass_encoder.DESCRIPTION = Two-pass encoder loop
|
||||
EXAMPLES-$(CONFIG_DECODERS) += decode_with_drops.c
|
||||
decode_with_drops.SRCS += ivfdec.h ivfdec.c
|
||||
decode_with_drops.SRCS += y4minput.c y4minput.h
|
||||
decode_with_drops.SRCS += tools_common.h tools_common.c
|
||||
decode_with_drops.SRCS += video_common.h
|
||||
decode_with_drops.SRCS += video_reader.h video_reader.c
|
||||
decode_with_drops.SRCS += vpx_ports/mem_ops.h
|
||||
decode_with_drops.SRCS += vpx_ports/mem_ops_aligned.h
|
||||
decode_with_drops.SRCS += vpx_ports/msvc.h
|
||||
decode_with_drops.GUID = CE5C53C4-8DDA-438A-86ED-0DDD3CDB8D26
|
||||
decode_with_drops.DESCRIPTION = Drops frames while decoding
|
||||
EXAMPLES-$(CONFIG_ENCODERS) += set_maps.c
|
||||
set_maps.SRCS += ivfenc.h ivfenc.c
|
||||
set_maps.SRCS += y4minput.c y4minput.h
|
||||
set_maps.SRCS += tools_common.h tools_common.c
|
||||
set_maps.SRCS += video_common.h
|
||||
set_maps.SRCS += video_writer.h video_writer.c
|
||||
set_maps.SRCS += vpx_ports/msvc.h
|
||||
set_maps.GUID = ECB2D24D-98B8-4015-A465-A4AF3DCC145F
|
||||
set_maps.DESCRIPTION = Set active and ROI maps
|
||||
EXAMPLES-$(CONFIG_VP8_ENCODER) += vp8cx_set_ref.c
|
||||
vp8cx_set_ref.SRCS += ivfenc.h ivfenc.c
|
||||
vp8cx_set_ref.SRCS += y4minput.c y4minput.h
|
||||
vp8cx_set_ref.SRCS += tools_common.h tools_common.c
|
||||
vp8cx_set_ref.SRCS += video_common.h
|
||||
vp8cx_set_ref.SRCS += video_writer.h video_writer.c
|
||||
vp8cx_set_ref.SRCS += vpx_ports/msvc.h
|
||||
vp8cx_set_ref.GUID = C5E31F7F-96F6-48BD-BD3E-10EBF6E8057A
|
||||
vp8cx_set_ref.DESCRIPTION = VP8 set encoder reference frame
|
||||
|
||||
ifeq ($(CONFIG_VP9_ENCODER),yes)
|
||||
ifeq ($(CONFIG_DECODERS),yes)
|
||||
EXAMPLES-yes += vp9cx_set_ref.c
|
||||
vp9cx_set_ref.SRCS += ivfenc.h ivfenc.c
|
||||
vp9cx_set_ref.SRCS += y4minput.c y4minput.h
|
||||
vp9cx_set_ref.SRCS += tools_common.h tools_common.c
|
||||
vp9cx_set_ref.SRCS += video_common.h
|
||||
vp9cx_set_ref.SRCS += video_writer.h video_writer.c
|
||||
vp9cx_set_ref.GUID = 65D7F14A-2EE6-4293-B958-AB5107A03B55
|
||||
vp9cx_set_ref.DESCRIPTION = VP9 set encoder reference frame
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_MULTI_RES_ENCODING),yes)
|
||||
ifeq ($(CONFIG_LIBYUV),yes)
|
||||
EXAMPLES-$(CONFIG_VP8_ENCODER) += vp8_multi_resolution_encoder.c
|
||||
vp8_multi_resolution_encoder.SRCS += ivfenc.h ivfenc.c
|
||||
vp8_multi_resolution_encoder.SRCS += y4minput.c y4minput.h
|
||||
vp8_multi_resolution_encoder.SRCS += tools_common.h tools_common.c
|
||||
vp8_multi_resolution_encoder.SRCS += video_writer.h video_writer.c
|
||||
vp8_multi_resolution_encoder.SRCS += vpx_ports/msvc.h
|
||||
vp8_multi_resolution_encoder.SRCS += $(LIBYUV_SRCS)
|
||||
vp8_multi_resolution_encoder.GUID = 04f8738e-63c8-423b-90fa-7c2703a374de
|
||||
vp8_multi_resolution_encoder.DESCRIPTION = VP8 Multiple-resolution Encoding
|
||||
endif
|
||||
endif
|
||||
|
||||
# Handle extra library flags depending on codec configuration
|
||||
|
||||
# We should not link to math library (libm) on RVCT
|
||||
# when building for bare-metal targets
|
||||
ifeq ($(CONFIG_OS_SUPPORT), yes)
|
||||
CODEC_EXTRA_LIBS-$(CONFIG_VP8) += m
|
||||
CODEC_EXTRA_LIBS-$(CONFIG_VP9) += m
|
||||
else
|
||||
ifeq ($(CONFIG_GCC), yes)
|
||||
CODEC_EXTRA_LIBS-$(CONFIG_VP8) += m
|
||||
CODEC_EXTRA_LIBS-$(CONFIG_VP9) += m
|
||||
endif
|
||||
endif
|
||||
#
|
||||
# End of specified files. The rest of the build rules should happen
|
||||
# automagically from here.
|
||||
#
|
||||
|
||||
|
||||
# Examples need different flags based on whether we're building
|
||||
# from an installed tree or a version controlled tree. Determine
|
||||
# the proper paths.
|
||||
ifeq ($(HAVE_ALT_TREE_LAYOUT),yes)
|
||||
LIB_PATH-yes := $(SRC_PATH_BARE)/../lib
|
||||
INC_PATH-yes := $(SRC_PATH_BARE)/../include
|
||||
else
|
||||
LIB_PATH-yes += $(if $(BUILD_PFX),$(BUILD_PFX),.)
|
||||
INC_PATH-$(CONFIG_VP8_DECODER) += $(SRC_PATH_BARE)/vp8
|
||||
INC_PATH-$(CONFIG_VP8_ENCODER) += $(SRC_PATH_BARE)/vp8
|
||||
INC_PATH-$(CONFIG_VP9_DECODER) += $(SRC_PATH_BARE)/vp9
|
||||
INC_PATH-$(CONFIG_VP9_ENCODER) += $(SRC_PATH_BARE)/vp9
|
||||
endif
|
||||
INC_PATH-$(CONFIG_LIBYUV) += $(SRC_PATH_BARE)/third_party/libyuv/include
|
||||
LIB_PATH := $(call enabled,LIB_PATH)
|
||||
INC_PATH := $(call enabled,INC_PATH)
|
||||
INTERNAL_CFLAGS = $(addprefix -I,$(INC_PATH))
|
||||
INTERNAL_LDFLAGS += $(addprefix -L,$(LIB_PATH))
|
||||
|
||||
|
||||
# Expand list of selected examples to build (as specified above)
|
||||
UTILS = $(call enabled,UTILS)
|
||||
EXAMPLES = $(addprefix examples/,$(call enabled,EXAMPLES))
|
||||
ALL_EXAMPLES = $(UTILS) $(EXAMPLES)
|
||||
UTIL_SRCS = $(foreach ex,$(UTILS),$($(ex:.c=).SRCS))
|
||||
ALL_SRCS = $(foreach ex,$(ALL_EXAMPLES),$($(notdir $(ex:.c=)).SRCS))
|
||||
CODEC_EXTRA_LIBS=$(sort $(call enabled,CODEC_EXTRA_LIBS))
|
||||
|
||||
|
||||
# Expand all example sources into a variable containing all sources
|
||||
# for that example (not just them main one specified in UTILS/EXAMPLES)
|
||||
# and add this file to the list (for MSVS workspace generation)
|
||||
$(foreach ex,$(ALL_EXAMPLES),$(eval $(notdir $(ex:.c=)).SRCS += $(ex) examples.mk))
|
||||
|
||||
|
||||
# Create build/install dependencies for all examples. The common case
|
||||
# is handled here. The MSVS case is handled below.
|
||||
NOT_MSVS = $(if $(CONFIG_MSVS),,yes)
|
||||
DIST-BINS-$(NOT_MSVS) += $(addprefix bin/,$(ALL_EXAMPLES:.c=$(EXE_SFX)))
|
||||
INSTALL-BINS-$(NOT_MSVS) += $(addprefix bin/,$(UTILS:.c=$(EXE_SFX)))
|
||||
DIST-SRCS-yes += $(ALL_SRCS)
|
||||
INSTALL-SRCS-yes += $(UTIL_SRCS)
|
||||
OBJS-$(NOT_MSVS) += $(call objs,$(ALL_SRCS))
|
||||
BINS-$(NOT_MSVS) += $(addprefix $(BUILD_PFX),$(ALL_EXAMPLES:.c=$(EXE_SFX)))
|
||||
|
||||
|
||||
# Instantiate linker template for all examples.
|
||||
CODEC_LIB=$(if $(CONFIG_DEBUG_LIBS),vpx_g,vpx)
|
||||
ifneq ($(filter darwin%,$(TGT_OS)),)
|
||||
SHARED_LIB_SUF=.dylib
|
||||
else
|
||||
ifneq ($(filter os2%,$(TGT_OS)),)
|
||||
SHARED_LIB_SUF=_dll.a
|
||||
else
|
||||
SHARED_LIB_SUF=.so
|
||||
endif
|
||||
endif
|
||||
CODEC_LIB_SUF=$(if $(CONFIG_SHARED),$(SHARED_LIB_SUF),.a)
|
||||
$(foreach bin,$(BINS-yes),\
|
||||
$(eval $(bin):$(LIB_PATH)/lib$(CODEC_LIB)$(CODEC_LIB_SUF))\
|
||||
$(eval $(call linker_template,$(bin),\
|
||||
$(call objs,$($(notdir $(bin:$(EXE_SFX)=)).SRCS)) \
|
||||
-l$(CODEC_LIB) $(addprefix -l,$(CODEC_EXTRA_LIBS))\
|
||||
)))
|
||||
|
||||
# The following pairs define a mapping of locations in the distribution
|
||||
# tree to locations in the source/build trees.
|
||||
INSTALL_MAPS += src/%.c %.c
|
||||
INSTALL_MAPS += src/% $(SRC_PATH_BARE)/%
|
||||
INSTALL_MAPS += bin/% %
|
||||
INSTALL_MAPS += % %
|
||||
|
||||
|
||||
# Set up additional MSVS environment
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
CODEC_LIB=$(if $(CONFIG_SHARED),vpx,$(if $(CONFIG_STATIC_MSVCRT),vpxmt,vpxmd))
|
||||
# This variable uses deferred expansion intentionally, since the results of
|
||||
# $(wildcard) may change during the course of the Make.
|
||||
VS_PLATFORMS = $(foreach d,$(wildcard */Release/$(CODEC_LIB).lib),$(word 1,$(subst /, ,$(d))))
|
||||
INSTALL_MAPS += $(foreach p,$(VS_PLATFORMS),bin/$(p)/% $(p)/Release/%)
|
||||
endif
|
||||
|
||||
# Build Visual Studio Projects. We use a template here to instantiate
|
||||
# explicit rules rather than using an implicit rule because we want to
|
||||
# leverage make's VPATH searching rather than specifying the paths on
|
||||
# each file in ALL_EXAMPLES. This has the unfortunate side effect that
|
||||
# touching the source files trigger a rebuild of the project files
|
||||
# even though there is no real dependency there (the dependency is on
|
||||
# the makefiles). We may want to revisit this.
|
||||
define vcproj_template
|
||||
$(1): $($(1:.$(VCPROJ_SFX)=).SRCS) vpx.$(VCPROJ_SFX)
|
||||
$(if $(quiet),@echo " [vcproj] $$@")
|
||||
$(qexec)$$(GEN_VCPROJ)\
|
||||
--exe\
|
||||
--target=$$(TOOLCHAIN)\
|
||||
--name=$$(@:.$(VCPROJ_SFX)=)\
|
||||
--ver=$$(CONFIG_VS_VERSION)\
|
||||
--proj-guid=$$($$(@:.$(VCPROJ_SFX)=).GUID)\
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
$$(if $$(CONFIG_STATIC_MSVCRT),--static-crt) \
|
||||
--out=$$@ $$(INTERNAL_CFLAGS) $$(CFLAGS) \
|
||||
$$(INTERNAL_LDFLAGS) $$(LDFLAGS) -l$$(CODEC_LIB) $$^
|
||||
endef
|
||||
ALL_EXAMPLES_BASENAME := $(notdir $(ALL_EXAMPLES))
|
||||
PROJECTS-$(CONFIG_MSVS) += $(ALL_EXAMPLES_BASENAME:.c=.$(VCPROJ_SFX))
|
||||
INSTALL-BINS-$(CONFIG_MSVS) += $(foreach p,$(VS_PLATFORMS),\
|
||||
$(addprefix bin/$(p)/,$(ALL_EXAMPLES_BASENAME:.c=.exe)))
|
||||
$(foreach proj,$(call enabled,PROJECTS),\
|
||||
$(eval $(call vcproj_template,$(proj))))
|
||||
|
||||
#
|
||||
# Documentation Rules
|
||||
#
|
||||
%.dox: %.c
|
||||
@echo " [DOXY] $@"
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "/*!\page example_$(@F:.dox=) $(@F:.dox=)" > $@
|
||||
@echo " \includelineno $(<F)" >> $@
|
||||
@echo "*/" >> $@
|
||||
|
||||
samples.dox: examples.mk
|
||||
@echo " [DOXY] $@"
|
||||
@echo "/*!\page samples Sample Code" > $@
|
||||
@echo " This SDK includes a number of sample applications."\
|
||||
"Each sample documents a feature of the SDK in both prose"\
|
||||
"and the associated C code."\
|
||||
"The following samples are included: ">>$@
|
||||
@$(foreach ex,$(sort $(notdir $(EXAMPLES:.c=))),\
|
||||
echo " - \subpage example_$(ex) $($(ex).DESCRIPTION)" >> $@;)
|
||||
@echo >> $@
|
||||
@echo " In addition, the SDK contains a number of utilities."\
|
||||
"Since these utilities are built upon the concepts described"\
|
||||
"in the sample code listed above, they are not documented in"\
|
||||
"pieces like the samples are. Their source is included here"\
|
||||
"for reference. The following utilities are included:" >> $@
|
||||
@$(foreach ex,$(sort $(UTILS:.c=)),\
|
||||
echo " - \subpage example_$(ex) $($(ex).DESCRIPTION)" >> $@;)
|
||||
@echo "*/" >> $@
|
||||
|
||||
CLEAN-OBJS += examples.doxy samples.dox $(ALL_EXAMPLES:.c=.dox)
|
||||
DOCS-yes += examples.doxy samples.dox
|
||||
examples.doxy: samples.dox $(ALL_EXAMPLES:.c=.dox)
|
||||
@echo "INPUT += $^" > $@
|
||||
@echo "ENABLED_SECTIONS += samples" >> $@
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Frame-by-frame MD5 Checksum
|
||||
// ===========================
|
||||
//
|
||||
// This example builds upon the simple decoder loop to show how checksums
|
||||
// of the decoded output can be generated. These are used for validating
|
||||
// decoder implementations against the reference implementation, for example.
|
||||
//
|
||||
// MD5 algorithm
|
||||
// -------------
|
||||
// The Message-Digest 5 (MD5) is a well known hash function. We have provided
|
||||
// an implementation derived from the RSA Data Security, Inc. MD5 Message-Digest
|
||||
// Algorithm for your use. Our implmentation only changes the interface of this
|
||||
// reference code. You must include the `md5_utils.h` header for access to these
|
||||
// functions.
|
||||
//
|
||||
// Processing The Decoded Data
|
||||
// ---------------------------
|
||||
// Each row of the image is passed to the MD5 accumulator. First the Y plane
|
||||
// is processed, then U, then V. It is important to honor the image's `stride`
|
||||
// values.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
#include "../md5_utils.h"
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./vpx_config.h"
|
||||
|
||||
static void get_image_md5(const vpx_image_t *img, unsigned char digest[16]) {
|
||||
int plane, y;
|
||||
MD5Context md5;
|
||||
|
||||
MD5Init(&md5);
|
||||
|
||||
for (plane = 0; plane < 3; ++plane) {
|
||||
const unsigned char *buf = img->planes[plane];
|
||||
const int stride = img->stride[plane];
|
||||
const int w = plane ? (img->d_w + 1) >> 1 : img->d_w;
|
||||
const int h = plane ? (img->d_h + 1) >> 1 : img->d_h;
|
||||
|
||||
for (y = 0; y < h; ++y) {
|
||||
MD5Update(&md5, buf, w);
|
||||
buf += stride;
|
||||
}
|
||||
}
|
||||
|
||||
MD5Final(digest, &md5);
|
||||
}
|
||||
|
||||
static void print_md5(FILE *stream, unsigned char digest[16]) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; ++i) fprintf(stream, "%02x", digest[i]);
|
||||
}
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
VpxVideoReader *reader = NULL;
|
||||
const VpxVideoInfo *info = NULL;
|
||||
const VpxInterface *decoder = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 3) die("Invalid number of arguments.");
|
||||
|
||||
reader = vpx_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
info = vpx_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_vpx_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (vpx_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder");
|
||||
|
||||
while (vpx_video_reader_read_frame(reader)) {
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
vpx_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame =
|
||||
vpx_video_reader_get_frame(reader, &frame_size);
|
||||
if (vpx_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame");
|
||||
|
||||
while ((img = vpx_codec_get_frame(&codec, &iter)) != NULL) {
|
||||
unsigned char digest[16];
|
||||
|
||||
get_image_md5(img, digest);
|
||||
print_md5(outfile, digest);
|
||||
fprintf(outfile, " img-%dx%d-%04d.i420\n", img->d_w, img->d_h,
|
||||
++frame_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_reader_close(reader);
|
||||
|
||||
fclose(outfile);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Decode With Drops Example
|
||||
// =========================
|
||||
//
|
||||
// This is an example utility which drops a series of frames, as specified
|
||||
// on the command line. This is useful for observing the error recovery
|
||||
// features of the codec.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// This example adds a single argument to the `simple_decoder` example,
|
||||
// which specifies the range or pattern of frames to drop. The parameter is
|
||||
// parsed as follows:
|
||||
//
|
||||
// Dropping A Range Of Frames
|
||||
// --------------------------
|
||||
// To drop a range of frames, specify the starting frame and the ending
|
||||
// frame to drop, separated by a dash. The following command will drop
|
||||
// frames 5 through 10 (base 1).
|
||||
//
|
||||
// $ ./decode_with_drops in.ivf out.i420 5-10
|
||||
//
|
||||
//
|
||||
// Dropping A Pattern Of Frames
|
||||
// ----------------------------
|
||||
// To drop a pattern of frames, specify the number of frames to drop and
|
||||
// the number of frames after which to repeat the pattern, separated by
|
||||
// a forward-slash. The following command will drop 3 of 7 frames.
|
||||
// Specifically, it will decode 4 frames, then drop 3 frames, and then
|
||||
// repeat.
|
||||
//
|
||||
// $ ./decode_with_drops in.ivf out.i420 3/7
|
||||
//
|
||||
//
|
||||
// Extra Variables
|
||||
// ---------------
|
||||
// This example maintains the pattern passed on the command line in the
|
||||
// `n`, `m`, and `is_range` variables:
|
||||
//
|
||||
//
|
||||
// Making The Drop Decision
|
||||
// ------------------------
|
||||
// The example decides whether to drop the frame based on the current
|
||||
// frame number, immediately before decoding the frame.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./vpx_config.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile> <N-M|N/M>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
const VpxInterface *decoder = NULL;
|
||||
VpxVideoReader *reader = NULL;
|
||||
const VpxVideoInfo *info = NULL;
|
||||
int n = 0;
|
||||
int m = 0;
|
||||
int is_range = 0;
|
||||
char *nptr = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 4) die("Invalid number of arguments.");
|
||||
|
||||
reader = vpx_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
n = (int)strtol(argv[3], &nptr, 0);
|
||||
m = (int)strtol(nptr + 1, NULL, 0);
|
||||
is_range = (*nptr == '-');
|
||||
if (!n || !m || (*nptr != '-' && *nptr != '/'))
|
||||
die("Couldn't parse pattern %s.\n", argv[3]);
|
||||
|
||||
info = vpx_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_vpx_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (vpx_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder.");
|
||||
|
||||
while (vpx_video_reader_read_frame(reader)) {
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
vpx_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
int skip;
|
||||
const unsigned char *frame =
|
||||
vpx_video_reader_get_frame(reader, &frame_size);
|
||||
if (vpx_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame.");
|
||||
|
||||
++frame_cnt;
|
||||
|
||||
skip = (is_range && frame_cnt >= n && frame_cnt <= m) ||
|
||||
(!is_range && m - (frame_cnt - 1) % m <= n);
|
||||
|
||||
if (!skip) {
|
||||
putc('.', stdout);
|
||||
|
||||
while ((img = vpx_codec_get_frame(&codec, &iter)) != NULL)
|
||||
vpx_img_write(img, outfile);
|
||||
} else {
|
||||
putc('X', stdout);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n",
|
||||
info->frame_width, info->frame_height, argv[2]);
|
||||
|
||||
vpx_video_reader_close(reader);
|
||||
fclose(outfile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Postprocessing Decoder
|
||||
// ======================
|
||||
//
|
||||
// This example adds postprocessing to the simple decoder loop.
|
||||
//
|
||||
// Initializing Postprocessing
|
||||
// ---------------------------
|
||||
// You must inform the codec that you might request postprocessing at
|
||||
// initialization time. This is done by passing the VPX_CODEC_USE_POSTPROC
|
||||
// flag to `vpx_codec_dec_init`. If the codec does not support
|
||||
// postprocessing, this call will return VPX_CODEC_INCAPABLE. For
|
||||
// demonstration purposes, we also fall back to default initialization if
|
||||
// the codec does not provide support.
|
||||
//
|
||||
// Using Adaptive Postprocessing
|
||||
// -----------------------------
|
||||
// VP6 provides "adaptive postprocessing." It will automatically select the
|
||||
// best postprocessing filter on a frame by frame basis based on the amount
|
||||
// of time remaining before the user's specified deadline expires. The
|
||||
// special value 0 indicates that the codec should take as long as
|
||||
// necessary to provide the best quality frame. This example gives the
|
||||
// codec 15ms (15000us) to return a frame. Remember that this is a soft
|
||||
// deadline, and the codec may exceed it doing its regular processing. In
|
||||
// these cases, no additional postprocessing will be done.
|
||||
//
|
||||
// Codec Specific Postprocessing Controls
|
||||
// --------------------------------------
|
||||
// Some codecs provide fine grained controls over their built-in
|
||||
// postprocessors. VP8 is one example. The following sample code toggles
|
||||
// postprocessing on and off every 15 frames.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./vpx_config.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoReader *reader = NULL;
|
||||
const VpxInterface *decoder = NULL;
|
||||
const VpxVideoInfo *info = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 3) die("Invalid number of arguments.");
|
||||
|
||||
reader = vpx_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing", argv[2]);
|
||||
|
||||
info = vpx_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_vpx_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_dec_init(&codec, decoder->codec_interface(), NULL,
|
||||
VPX_CODEC_USE_POSTPROC);
|
||||
if (res == VPX_CODEC_INCAPABLE)
|
||||
die_codec(&codec, "Postproc not supported by this decoder.");
|
||||
|
||||
if (res) die_codec(&codec, "Failed to initialize decoder.");
|
||||
|
||||
while (vpx_video_reader_read_frame(reader)) {
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
vpx_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame =
|
||||
vpx_video_reader_get_frame(reader, &frame_size);
|
||||
|
||||
++frame_cnt;
|
||||
|
||||
if (frame_cnt % 30 == 1) {
|
||||
vp8_postproc_cfg_t pp = { 0, 0, 0 };
|
||||
|
||||
if (vpx_codec_control(&codec, VP8_SET_POSTPROC, &pp))
|
||||
die_codec(&codec, "Failed to turn off postproc.");
|
||||
} else if (frame_cnt % 30 == 16) {
|
||||
vp8_postproc_cfg_t pp = { VP8_DEBLOCK | VP8_DEMACROBLOCK | VP8_MFQE, 4,
|
||||
0 };
|
||||
if (vpx_codec_control(&codec, VP8_SET_POSTPROC, &pp))
|
||||
die_codec(&codec, "Failed to turn on postproc.");
|
||||
};
|
||||
|
||||
// Decode the frame with 15ms deadline
|
||||
if (vpx_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 15000))
|
||||
die_codec(&codec, "Failed to decode frame");
|
||||
|
||||
while ((img = vpx_codec_get_frame(&codec, &iter)) != NULL) {
|
||||
vpx_img_write(img, outfile);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
|
||||
printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n",
|
||||
info->frame_width, info->frame_height, argv[2]);
|
||||
|
||||
vpx_video_reader_close(reader);
|
||||
|
||||
fclose(outfile);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../vp9/encoder/vp9_resize.h"
|
||||
|
||||
static const char *exec_name = NULL;
|
||||
|
||||
static void usage() {
|
||||
printf("Usage:\n");
|
||||
printf("%s <input_yuv> <width>x<height> <target_width>x<target_height> ",
|
||||
exec_name);
|
||||
printf("<output_yuv> [<frames>]\n");
|
||||
}
|
||||
|
||||
void usage_exit(void) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int parse_dim(char *v, int *width, int *height) {
|
||||
char *x = strchr(v, 'x');
|
||||
if (x == NULL) x = strchr(v, 'X');
|
||||
if (x == NULL) return 0;
|
||||
*width = atoi(v);
|
||||
*height = atoi(&x[1]);
|
||||
if (*width <= 0 || *height <= 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *fin, *fout;
|
||||
FILE *fpin, *fpout;
|
||||
uint8_t *inbuf, *outbuf;
|
||||
uint8_t *inbuf_u, *outbuf_u;
|
||||
uint8_t *inbuf_v, *outbuf_v;
|
||||
int f, frames;
|
||||
int width, height, target_width, target_height;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc < 5) {
|
||||
printf("Incorrect parameters:\n");
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
fin = argv[1];
|
||||
fout = argv[4];
|
||||
if (!parse_dim(argv[2], &width, &height)) {
|
||||
printf("Incorrect parameters: %s\n", argv[2]);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (!parse_dim(argv[3], &target_width, &target_height)) {
|
||||
printf("Incorrect parameters: %s\n", argv[3]);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
fpin = fopen(fin, "rb");
|
||||
if (fpin == NULL) {
|
||||
printf("Can't open file %s to read\n", fin);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
fpout = fopen(fout, "wb");
|
||||
if (fpout == NULL) {
|
||||
printf("Can't open file %s to write\n", fout);
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
if (argc >= 6)
|
||||
frames = atoi(argv[5]);
|
||||
else
|
||||
frames = INT_MAX;
|
||||
|
||||
printf("Input size: %dx%d\n", width, height);
|
||||
printf("Target size: %dx%d, Frames: ", target_width, target_height);
|
||||
if (frames == INT_MAX)
|
||||
printf("All\n");
|
||||
else
|
||||
printf("%d\n", frames);
|
||||
|
||||
inbuf = (uint8_t *)malloc(width * height * 3 / 2);
|
||||
outbuf = (uint8_t *)malloc(target_width * target_height * 3 / 2);
|
||||
inbuf_u = inbuf + width * height;
|
||||
inbuf_v = inbuf_u + width * height / 4;
|
||||
outbuf_u = outbuf + target_width * target_height;
|
||||
outbuf_v = outbuf_u + target_width * target_height / 4;
|
||||
f = 0;
|
||||
while (f < frames) {
|
||||
if (fread(inbuf, width * height * 3 / 2, 1, fpin) != 1) break;
|
||||
vp9_resize_frame420(inbuf, width, inbuf_u, inbuf_v, width / 2, height,
|
||||
width, outbuf, target_width, outbuf_u, outbuf_v,
|
||||
target_width / 2, target_height, target_width);
|
||||
fwrite(outbuf, target_width * target_height * 3 / 2, 1, fpout);
|
||||
f++;
|
||||
}
|
||||
printf("%d frames processed\n", f);
|
||||
fclose(fpin);
|
||||
fclose(fpout);
|
||||
|
||||
free(inbuf);
|
||||
free(outbuf);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// VP8 Set Active and ROI Maps
|
||||
// ===========================
|
||||
//
|
||||
// This is an example demonstrating how to control the VP8 encoder's
|
||||
// ROI and Active maps.
|
||||
//
|
||||
// ROI (Reigon of Interest) maps are a way for the application to assign
|
||||
// each macroblock in the image to a region, and then set quantizer and
|
||||
// filtering parameters on that image.
|
||||
//
|
||||
// Active maps are a way for the application to specify on a
|
||||
// macroblock-by-macroblock basis whether there is any activity in that
|
||||
// macroblock.
|
||||
//
|
||||
//
|
||||
// Configuration
|
||||
// -------------
|
||||
// An ROI map is set on frame 22. If the width of the image in macroblocks
|
||||
// is evenly divisble by 4, then the output will appear to have distinct
|
||||
// columns, where the quantizer, loopfilter, and static threshold differ
|
||||
// from column to column.
|
||||
//
|
||||
// An active map is set on frame 33. If the width of the image in macroblocks
|
||||
// is evenly divisble by 4, then the output will appear to have distinct
|
||||
// columns, where one column will have motion and the next will not.
|
||||
//
|
||||
// The active map is cleared on frame 44.
|
||||
//
|
||||
// Observing The Effects
|
||||
// ---------------------
|
||||
// Use the `simple_decoder` example to decode this sample, and observe
|
||||
// the change in the image at frames 22, 33, and 44.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <codec> <width> <height> <infile> <outfile>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void set_roi_map(const vpx_codec_enc_cfg_t *cfg,
|
||||
vpx_codec_ctx_t *codec) {
|
||||
unsigned int i;
|
||||
vpx_roi_map_t roi;
|
||||
memset(&roi, 0, sizeof(roi));
|
||||
|
||||
roi.rows = (cfg->g_h + 15) / 16;
|
||||
roi.cols = (cfg->g_w + 15) / 16;
|
||||
|
||||
roi.delta_q[0] = 0;
|
||||
roi.delta_q[1] = -2;
|
||||
roi.delta_q[2] = -4;
|
||||
roi.delta_q[3] = -6;
|
||||
|
||||
roi.delta_lf[0] = 0;
|
||||
roi.delta_lf[1] = 1;
|
||||
roi.delta_lf[2] = 2;
|
||||
roi.delta_lf[3] = 3;
|
||||
|
||||
roi.static_threshold[0] = 1500;
|
||||
roi.static_threshold[1] = 1000;
|
||||
roi.static_threshold[2] = 500;
|
||||
roi.static_threshold[3] = 0;
|
||||
|
||||
roi.roi_map = (uint8_t *)malloc(roi.rows * roi.cols);
|
||||
for (i = 0; i < roi.rows * roi.cols; ++i) roi.roi_map[i] = i % 4;
|
||||
|
||||
if (vpx_codec_control(codec, VP8E_SET_ROI_MAP, &roi))
|
||||
die_codec(codec, "Failed to set ROI map");
|
||||
|
||||
free(roi.roi_map);
|
||||
}
|
||||
|
||||
static void set_active_map(const vpx_codec_enc_cfg_t *cfg,
|
||||
vpx_codec_ctx_t *codec) {
|
||||
unsigned int i;
|
||||
vpx_active_map_t map = { 0, 0, 0 };
|
||||
|
||||
map.rows = (cfg->g_h + 15) / 16;
|
||||
map.cols = (cfg->g_w + 15) / 16;
|
||||
|
||||
map.active_map = (uint8_t *)malloc(map.rows * map.cols);
|
||||
for (i = 0; i < map.rows * map.cols; ++i) map.active_map[i] = i % 2;
|
||||
|
||||
if (vpx_codec_control(codec, VP8E_SET_ACTIVEMAP, &map))
|
||||
die_codec(codec, "Failed to set active map");
|
||||
|
||||
free(map.active_map);
|
||||
}
|
||||
|
||||
static void unset_active_map(const vpx_codec_enc_cfg_t *cfg,
|
||||
vpx_codec_ctx_t *codec) {
|
||||
vpx_active_map_t map = { 0, 0, 0 };
|
||||
|
||||
map.rows = (cfg->g_h + 15) / 16;
|
||||
map.cols = (cfg->g_w + 15) / 16;
|
||||
map.active_map = NULL;
|
||||
|
||||
if (vpx_codec_control(codec, VP8E_SET_ACTIVEMAP, &map))
|
||||
die_codec(codec, "Failed to set active map");
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
|
||||
int frame_index, VpxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(codec, img, frame_index, 1, 0, VPX_DL_GOOD_QUALITY);
|
||||
if (res != VPX_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoInfo info;
|
||||
VpxVideoWriter *writer = NULL;
|
||||
const VpxInterface *encoder = NULL;
|
||||
const int fps = 2; // TODO(dkovalev) add command line argument
|
||||
const double bits_per_pixel_per_frame = 0.067;
|
||||
|
||||
exec_name = argv[0];
|
||||
if (argc != 6) die("Invalid number of arguments");
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
encoder = get_vpx_encoder_by_name(argv[1]);
|
||||
if (encoder == NULL) {
|
||||
die("Unsupported codec.");
|
||||
}
|
||||
assert(encoder != NULL);
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(argv[2], NULL, 0);
|
||||
info.frame_height = (int)strtol(argv[3], NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate =
|
||||
(unsigned int)(bits_per_pixel_per_frame * cfg.g_w * cfg.g_h * fps / 1000);
|
||||
cfg.g_lag_in_frames = 0;
|
||||
|
||||
writer = vpx_video_writer_open(argv[5], kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", argv[5]);
|
||||
|
||||
if (!(infile = fopen(argv[4], "rb")))
|
||||
die("Failed to open %s for reading.", argv[4]);
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(&raw, infile)) {
|
||||
++frame_count;
|
||||
|
||||
if (frame_count == 22 && encoder->fourcc == VP8_FOURCC) {
|
||||
set_roi_map(&cfg, &codec);
|
||||
} else if (frame_count == 33) {
|
||||
set_active_map(&cfg, &codec);
|
||||
} else if (frame_count == 44) {
|
||||
unset_active_map(&cfg, &codec);
|
||||
}
|
||||
|
||||
encode_frame(&codec, &raw, frame_count, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
vpx_img_free(&raw);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Simple Decoder
|
||||
// ==============
|
||||
//
|
||||
// This is an example of a simple decoder loop. It takes an input file
|
||||
// containing the compressed data (in IVF format), passes it through the
|
||||
// decoder, and writes the decompressed frames to disk. Other decoder
|
||||
// examples build upon this one.
|
||||
//
|
||||
// The details of the IVF format have been elided from this example for
|
||||
// simplicity of presentation, as IVF files will not generally be used by
|
||||
// your application. In general, an IVF file consists of a file header,
|
||||
// followed by a variable number of frames. Each frame consists of a frame
|
||||
// header followed by a variable length payload. The length of the payload
|
||||
// is specified in the first four bytes of the frame header. The payload is
|
||||
// the raw compressed data.
|
||||
//
|
||||
// Standard Includes
|
||||
// -----------------
|
||||
// For decoders, you only have to include `vpx_decoder.h` and then any
|
||||
// header files for the specific codecs you use. In this case, we're using
|
||||
// vp8.
|
||||
//
|
||||
// Initializing The Codec
|
||||
// ----------------------
|
||||
// The libvpx decoder is initialized by the call to vpx_codec_dec_init().
|
||||
// Determining the codec interface to use is handled by VpxVideoReader and the
|
||||
// functions prefixed with vpx_video_reader_. Discussion of those functions is
|
||||
// beyond the scope of this example, but the main gist is to open the input file
|
||||
// and parse just enough of it to determine if it's a VPx file and which VPx
|
||||
// codec is contained within the file.
|
||||
// Note the NULL pointer passed to vpx_codec_dec_init(). We do that in this
|
||||
// example because we want the algorithm to determine the stream configuration
|
||||
// (width/height) and allocate memory automatically.
|
||||
//
|
||||
// Decoding A Frame
|
||||
// ----------------
|
||||
// Once the frame has been read into memory, it is decoded using the
|
||||
// `vpx_codec_decode` function. The call takes a pointer to the data
|
||||
// (`frame`) and the length of the data (`frame_size`). No application data
|
||||
// is associated with the frame in this example, so the `user_priv`
|
||||
// parameter is NULL. The `deadline` parameter is left at zero for this
|
||||
// example. This parameter is generally only used when doing adaptive post
|
||||
// processing.
|
||||
//
|
||||
// Codecs may produce a variable number of output frames for every call to
|
||||
// `vpx_codec_decode`. These frames are retrieved by the
|
||||
// `vpx_codec_get_frame` iterator function. The iterator variable `iter` is
|
||||
// initialized to NULL each time `vpx_codec_decode` is called.
|
||||
// `vpx_codec_get_frame` is called in a loop, returning a pointer to a
|
||||
// decoded image or NULL to indicate the end of list.
|
||||
//
|
||||
// Processing The Decoded Data
|
||||
// ---------------------------
|
||||
// In this example, we simply write the encoded data to disk. It is
|
||||
// important to honor the image's `stride` values.
|
||||
//
|
||||
// Cleanup
|
||||
// -------
|
||||
// The `vpx_codec_destroy` call frees any memory allocated by the codec.
|
||||
//
|
||||
// Error Handling
|
||||
// --------------
|
||||
// This example does not special case any error return codes. If there was
|
||||
// an error, a descriptive message is printed and the program exits. With
|
||||
// few exceptions, vpx_codec functions return an enumerated error status,
|
||||
// with the value `0` indicating success.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_reader.h"
|
||||
#include "./vpx_config.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int frame_cnt = 0;
|
||||
FILE *outfile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
VpxVideoReader *reader = NULL;
|
||||
const VpxInterface *decoder = NULL;
|
||||
const VpxVideoInfo *info = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 3) die("Invalid number of arguments.");
|
||||
|
||||
reader = vpx_video_reader_open(argv[1]);
|
||||
if (!reader) die("Failed to open %s for reading.", argv[1]);
|
||||
|
||||
if (!(outfile = fopen(argv[2], "wb")))
|
||||
die("Failed to open %s for writing.", argv[2]);
|
||||
|
||||
info = vpx_video_reader_get_info(reader);
|
||||
|
||||
decoder = get_vpx_decoder_by_fourcc(info->codec_fourcc);
|
||||
if (!decoder) die("Unknown input codec.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(decoder->codec_interface()));
|
||||
|
||||
if (vpx_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&codec, "Failed to initialize decoder.");
|
||||
|
||||
while (vpx_video_reader_read_frame(reader)) {
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
vpx_image_t *img = NULL;
|
||||
size_t frame_size = 0;
|
||||
const unsigned char *frame =
|
||||
vpx_video_reader_get_frame(reader, &frame_size);
|
||||
if (vpx_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0))
|
||||
die_codec(&codec, "Failed to decode frame.");
|
||||
|
||||
while ((img = vpx_codec_get_frame(&codec, &iter)) != NULL) {
|
||||
vpx_img_write(img, outfile);
|
||||
++frame_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Processed %d frames.\n", frame_cnt);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
|
||||
printf("Play: ffplay -f rawvideo -pix_fmt yuv420p -s %dx%d %s\n",
|
||||
info->frame_width, info->frame_height, argv[2]);
|
||||
|
||||
vpx_video_reader_close(reader);
|
||||
|
||||
fclose(outfile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Simple Encoder
|
||||
// ==============
|
||||
//
|
||||
// This is an example of a simple encoder loop. It takes an input file in
|
||||
// YV12 format, passes it through the encoder, and writes the compressed
|
||||
// frames to disk in IVF format. Other decoder examples build upon this
|
||||
// one.
|
||||
//
|
||||
// The details of the IVF format have been elided from this example for
|
||||
// simplicity of presentation, as IVF files will not generally be used by
|
||||
// your application. In general, an IVF file consists of a file header,
|
||||
// followed by a variable number of frames. Each frame consists of a frame
|
||||
// header followed by a variable length payload. The length of the payload
|
||||
// is specified in the first four bytes of the frame header. The payload is
|
||||
// the raw compressed data.
|
||||
//
|
||||
// Standard Includes
|
||||
// -----------------
|
||||
// For encoders, you only have to include `vpx_encoder.h` and then any
|
||||
// header files for the specific codecs you use. In this case, we're using
|
||||
// vp8.
|
||||
//
|
||||
// Getting The Default Configuration
|
||||
// ---------------------------------
|
||||
// Encoders have the notion of "usage profiles." For example, an encoder
|
||||
// may want to publish default configurations for both a video
|
||||
// conferencing application and a best quality offline encoder. These
|
||||
// obviously have very different default settings. Consult the
|
||||
// documentation for your codec to see if it provides any default
|
||||
// configurations. All codecs provide a default configuration, number 0,
|
||||
// which is valid for material in the vacinity of QCIF/QVGA.
|
||||
//
|
||||
// Updating The Configuration
|
||||
// ---------------------------------
|
||||
// Almost all applications will want to update the default configuration
|
||||
// with settings specific to their usage. Here we set the width and height
|
||||
// of the video file to that specified on the command line. We also scale
|
||||
// the default bitrate based on the ratio between the default resolution
|
||||
// and the resolution specified on the command line.
|
||||
//
|
||||
// Initializing The Codec
|
||||
// ----------------------
|
||||
// The encoder is initialized by the following code.
|
||||
//
|
||||
// Encoding A Frame
|
||||
// ----------------
|
||||
// The frame is read as a continuous block (size width * height * 3 / 2)
|
||||
// from the input file. If a frame was read (the input file has not hit
|
||||
// EOF) then the frame is passed to the encoder. Otherwise, a NULL
|
||||
// is passed, indicating the End-Of-Stream condition to the encoder. The
|
||||
// `frame_cnt` is reused as the presentation time stamp (PTS) and each
|
||||
// frame is shown for one frame-time in duration. The flags parameter is
|
||||
// unused in this example. The deadline is set to VPX_DL_REALTIME to
|
||||
// make the example run as quickly as possible.
|
||||
|
||||
// Forced Keyframes
|
||||
// ----------------
|
||||
// Keyframes can be forced by setting the VPX_EFLAG_FORCE_KF bit of the
|
||||
// flags passed to `vpx_codec_control()`. In this example, we force a
|
||||
// keyframe every <keyframe-interval> frames. Note, the output stream can
|
||||
// contain additional keyframes beyond those that have been forced using the
|
||||
// VPX_EFLAG_FORCE_KF flag because of automatic keyframe placement by the
|
||||
// encoder.
|
||||
//
|
||||
// Processing The Encoded Data
|
||||
// ---------------------------
|
||||
// Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
|
||||
// for this frame. We write a IVF frame header, followed by the raw data.
|
||||
//
|
||||
// Cleanup
|
||||
// -------
|
||||
// The `vpx_codec_destroy` call frees any memory allocated by the codec.
|
||||
//
|
||||
// Error Handling
|
||||
// --------------
|
||||
// This example does not special case any error return codes. If there was
|
||||
// an error, a descriptive message is printed and the program exits. With
|
||||
// few exeptions, vpx_codec functions return an enumerated error status,
|
||||
// with the value `0` indicating success.
|
||||
//
|
||||
// Error Resiliency Features
|
||||
// -------------------------
|
||||
// Error resiliency is controlled by the g_error_resilient member of the
|
||||
// configuration structure. Use the `decode_with_drops` example to decode with
|
||||
// frames 5-10 dropped. Compare the output for a file encoded with this example
|
||||
// versus one encoded with the `simple_encoder` example.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <codec> <width> <height> <infile> <outfile> "
|
||||
"<keyframe-interval> <error-resilient> <frames to encode>\n"
|
||||
"See comments in simple_encoder.c for more information.\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
|
||||
int frame_index, int flags, VpxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(codec, img, frame_index, 1, flags, VPX_DL_GOOD_QUALITY);
|
||||
if (res != VPX_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
// TODO(tomfinegan): Improve command line parsing and add args for bitrate/fps.
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoInfo info = { 0, 0, 0, { 0, 0 } };
|
||||
VpxVideoWriter *writer = NULL;
|
||||
const VpxInterface *encoder = NULL;
|
||||
const int fps = 30;
|
||||
const int bitrate = 200;
|
||||
int keyframe_interval = 0;
|
||||
int max_frames = 0;
|
||||
int frames_encoded = 0;
|
||||
const char *codec_arg = NULL;
|
||||
const char *width_arg = NULL;
|
||||
const char *height_arg = NULL;
|
||||
const char *infile_arg = NULL;
|
||||
const char *outfile_arg = NULL;
|
||||
const char *keyframe_interval_arg = NULL;
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 9) die("Invalid number of arguments");
|
||||
|
||||
codec_arg = argv[1];
|
||||
width_arg = argv[2];
|
||||
height_arg = argv[3];
|
||||
infile_arg = argv[4];
|
||||
outfile_arg = argv[5];
|
||||
keyframe_interval_arg = argv[6];
|
||||
max_frames = (int)strtol(argv[8], NULL, 0);
|
||||
|
||||
encoder = get_vpx_encoder_by_name(codec_arg);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(width_arg, NULL, 0);
|
||||
info.frame_height = (int)strtol(height_arg, NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
keyframe_interval = (int)strtol(keyframe_interval_arg, NULL, 0);
|
||||
if (keyframe_interval < 0) die("Invalid keyframe interval value.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
cfg.g_error_resilient = (vpx_codec_er_flags_t)strtoul(argv[7], NULL, 0);
|
||||
|
||||
writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", outfile_arg);
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading.", infile_arg);
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(&raw, infile)) {
|
||||
int flags = 0;
|
||||
if (keyframe_interval > 0 && frame_count % keyframe_interval == 0)
|
||||
flags |= VPX_EFLAG_FORCE_KF;
|
||||
encode_frame(&codec, &raw, frame_count++, flags, writer);
|
||||
frames_encoded++;
|
||||
if (max_frames > 0 && frames_encoded >= max_frames) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 0, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
vpx_img_free(&raw);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SvcContext - input parameters and state to encode a multi-layered
|
||||
* spatial SVC frame
|
||||
*/
|
||||
|
||||
#ifndef VPX_EXAMPLES_SVC_CONTEXT_H_
|
||||
#define VPX_EXAMPLES_SVC_CONTEXT_H_
|
||||
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum SVC_LOG_LEVEL {
|
||||
SVC_LOG_ERROR,
|
||||
SVC_LOG_INFO,
|
||||
SVC_LOG_DEBUG
|
||||
} SVC_LOG_LEVEL;
|
||||
|
||||
typedef struct {
|
||||
// public interface to svc_command options
|
||||
int spatial_layers; // number of spatial layers
|
||||
int temporal_layers; // number of temporal layers
|
||||
int temporal_layering_mode;
|
||||
SVC_LOG_LEVEL log_level; // amount of information to display
|
||||
int output_rc_stat; // for outputting rc stats
|
||||
int speed; // speed setting for codec
|
||||
int threads;
|
||||
int aqmode; // turns on aq-mode=3 (cyclic_refresh): 0=off, 1=on.
|
||||
// private storage for vpx_svc_encode
|
||||
void *internal;
|
||||
} SvcContext;
|
||||
|
||||
#define OPTION_BUFFER_SIZE 1024
|
||||
#define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v
|
||||
|
||||
typedef struct SvcInternal {
|
||||
char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options
|
||||
|
||||
// values extracted from option, quantizers
|
||||
vpx_svc_extra_cfg_t svc_params;
|
||||
int enable_auto_alt_ref[VPX_SS_MAX_LAYERS];
|
||||
int bitrates[VPX_MAX_LAYERS];
|
||||
|
||||
// accumulated statistics
|
||||
double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V
|
||||
uint64_t sse_sum[VPX_SS_MAX_LAYERS][COMPONENTS];
|
||||
uint32_t bytes_sum[VPX_SS_MAX_LAYERS];
|
||||
|
||||
// codec encoding values
|
||||
int width; // width of highest layer
|
||||
int height; // height of highest layer
|
||||
int kf_dist; // distance between keyframes
|
||||
|
||||
// state variables
|
||||
int psnr_pkt_received;
|
||||
int layer;
|
||||
int use_multiple_frame_contexts;
|
||||
|
||||
vpx_codec_ctx_t *codec_ctx;
|
||||
} SvcInternal_t;
|
||||
|
||||
/**
|
||||
* Set SVC options
|
||||
* options are supplied as a single string separated by spaces
|
||||
* Format: encoding-mode=<i|ip|alt-ip|gf>
|
||||
* layers=<layer_count>
|
||||
* scaling-factors=<n1>/<d1>,<n2>/<d2>,...
|
||||
* quantizers=<q1>,<q2>,...
|
||||
*/
|
||||
vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options);
|
||||
|
||||
/**
|
||||
* initialize SVC encoding
|
||||
*/
|
||||
vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
vpx_codec_enc_cfg_t *cfg);
|
||||
/**
|
||||
* encode a frame of video with multiple layers
|
||||
*/
|
||||
vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
||||
struct vpx_image *rawimg, vpx_codec_pts_t pts,
|
||||
int64_t duration, int deadline);
|
||||
|
||||
/**
|
||||
* finished with svc encoding, release allocated resources
|
||||
*/
|
||||
void vpx_svc_release(SvcContext *svc_ctx);
|
||||
|
||||
/**
|
||||
* dump accumulated statistics and reset accumulated values
|
||||
*/
|
||||
void vpx_svc_dump_statistics(SvcContext *svc_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // VPX_EXAMPLES_SVC_CONTEXT_H_
|
||||
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* VP9 SVC encoding support via libvpx
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define VPX_DISABLE_CTRL_TYPECHECKS 1
|
||||
#include "./vpx_config.h"
|
||||
#include "./svc_context.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
#include "vp9/common/vp9_onyxc_int.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define strtok_r strtok_s
|
||||
#ifndef MINGW_HAS_SECURE_API
|
||||
// proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
|
||||
_CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
|
||||
#endif /* MINGW_HAS_SECURE_API */
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strdup _strdup
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
|
||||
#define SVC_REFERENCE_FRAMES 8
|
||||
#define SUPERFRAME_SLOTS (8)
|
||||
#define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
|
||||
|
||||
#define MAX_QUANTIZER 63
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = { 4, 5, 7, 11,
|
||||
16 };
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = { 16, 16, 16,
|
||||
16, 16 };
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_NUM_2x[VPX_SS_MAX_LAYERS] = { 1, 2, 4 };
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_DEN_2x[VPX_SS_MAX_LAYERS] = { 4, 4, 4 };
|
||||
|
||||
typedef enum {
|
||||
QUANTIZER = 0,
|
||||
BITRATE,
|
||||
SCALE_FACTOR,
|
||||
AUTO_ALT_REF,
|
||||
ALL_OPTION_TYPES
|
||||
} LAYER_OPTION_TYPE;
|
||||
|
||||
static const int option_max_values[ALL_OPTION_TYPES] = { 63, INT_MAX, INT_MAX,
|
||||
1 };
|
||||
|
||||
static const int option_min_values[ALL_OPTION_TYPES] = { 0, 0, 1, 0 };
|
||||
|
||||
// One encoded frame
|
||||
typedef struct FrameData {
|
||||
void *buf; // compressed data buffer
|
||||
size_t size; // length of compressed data
|
||||
vpx_codec_frame_flags_t flags; /**< flags for this frame */
|
||||
struct FrameData *next;
|
||||
} FrameData;
|
||||
|
||||
static SvcInternal_t *get_svc_internal(SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
if (svc_ctx->internal == NULL) {
|
||||
SvcInternal_t *const si = (SvcInternal_t *)malloc(sizeof(*si));
|
||||
if (si != NULL) {
|
||||
memset(si, 0, sizeof(*si));
|
||||
}
|
||||
svc_ctx->internal = si;
|
||||
}
|
||||
return (SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static const SvcInternal_t *get_const_svc_internal(const SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
return (const SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level, const char *fmt,
|
||||
...) {
|
||||
char buf[512];
|
||||
int retval = 0;
|
||||
va_list ap;
|
||||
|
||||
if (level > svc_ctx->log_level) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
retval = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
printf("%s", buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type, char *input,
|
||||
int *value0, int *value1) {
|
||||
if (type == SCALE_FACTOR) {
|
||||
*value0 = (int)strtol(input, &input, 10);
|
||||
if (*input++ != '/') return VPX_CODEC_INVALID_PARAM;
|
||||
*value1 = (int)strtol(input, &input, 10);
|
||||
|
||||
if (*value0 < option_min_values[SCALE_FACTOR] ||
|
||||
*value1 < option_min_values[SCALE_FACTOR] ||
|
||||
*value0 > option_max_values[SCALE_FACTOR] ||
|
||||
*value1 > option_max_values[SCALE_FACTOR] ||
|
||||
*value0 > *value1) // num shouldn't be greater than den
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
} else {
|
||||
*value0 = atoi(input);
|
||||
if (*value0 < option_min_values[type] || *value0 > option_max_values[type])
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
|
||||
LAYER_OPTION_TYPE type,
|
||||
const char *input,
|
||||
int *option0,
|
||||
int *option1) {
|
||||
int i;
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
char *input_string;
|
||||
char *token;
|
||||
const char *delim = ",";
|
||||
char *save_ptr;
|
||||
int num_layers = svc_ctx->spatial_layers;
|
||||
if (type == BITRATE)
|
||||
num_layers = svc_ctx->spatial_layers * svc_ctx->temporal_layers;
|
||||
|
||||
if (input == NULL || option0 == NULL ||
|
||||
(option1 == NULL && type == SCALE_FACTOR))
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
input_string = strdup(input);
|
||||
if (input_string == NULL) return VPX_CODEC_MEM_ERROR;
|
||||
token = strtok_r(input_string, delim, &save_ptr);
|
||||
for (i = 0; i < num_layers; ++i) {
|
||||
if (token != NULL) {
|
||||
res = extract_option(type, token, option0 + i, option1 + i);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
token = strtok_r(NULL, delim, &save_ptr);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res == VPX_CODEC_OK && i != num_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: layer params type: %d %d values required, "
|
||||
"but only %d specified\n",
|
||||
type, num_layers, i);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
free(input_string);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SVC encoding options
|
||||
* Format: encoding-mode=<svc_mode>,layers=<layer_count>
|
||||
* scale-factors=<n1>/<d1>,<n2>/<d2>,...
|
||||
* quantizers=<q1>,<q2>,...
|
||||
* svc_mode = [i|ip|alt_ip|gf]
|
||||
*/
|
||||
static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
|
||||
char *input_string;
|
||||
char *option_name;
|
||||
char *option_value;
|
||||
char *input_ptr = NULL;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
int i, alt_ref_enabled = 0;
|
||||
|
||||
if (options == NULL) return VPX_CODEC_OK;
|
||||
input_string = strdup(options);
|
||||
if (input_string == NULL) return VPX_CODEC_MEM_ERROR;
|
||||
|
||||
// parse option name
|
||||
option_name = strtok_r(input_string, "=", &input_ptr);
|
||||
while (option_name != NULL) {
|
||||
// parse option value
|
||||
option_value = strtok_r(NULL, " ", &input_ptr);
|
||||
if (option_value == NULL) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
|
||||
option_name);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
if (strcmp("spatial-layers", option_name) == 0) {
|
||||
svc_ctx->spatial_layers = atoi(option_value);
|
||||
} else if (strcmp("temporal-layers", option_name) == 0) {
|
||||
svc_ctx->temporal_layers = atoi(option_value);
|
||||
} else if (strcmp("scale-factors", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
|
||||
si->svc_params.scaling_factor_num,
|
||||
si->svc_params.scaling_factor_den);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("max-quantizers", option_name) == 0) {
|
||||
res =
|
||||
parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.max_quantizers, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("min-quantizers", option_name) == 0) {
|
||||
res =
|
||||
parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.min_quantizers, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("auto-alt-refs", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
|
||||
si->enable_auto_alt_ref, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("bitrates", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
|
||||
si->bitrates, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("multi-frame-contexts", option_name) == 0) {
|
||||
si->use_multiple_frame_contexts = atoi(option_value);
|
||||
} else {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
option_name = strtok_r(NULL, "=", &input_ptr);
|
||||
}
|
||||
free(input_string);
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER ||
|
||||
si->svc_params.max_quantizers[i] < 0 ||
|
||||
si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] ||
|
||||
si->svc_params.min_quantizers[i] < 0)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (si->use_multiple_frame_contexts &&
|
||||
(svc_ctx->spatial_layers > 3 ||
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
alt_ref_enabled += si->enable_auto_alt_ref[i];
|
||||
if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
|
||||
"enabled auto alt reference frame, but % layers are enabled\n",
|
||||
REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) {
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || options == NULL || si == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
strncpy(si->options, options, sizeof(si->options));
|
||||
si->options[sizeof(si->options) - 1] = '\0';
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t assign_layer_bitrates(
|
||||
const SvcContext *svc_ctx, vpx_codec_enc_cfg_t *const enc_cfg) {
|
||||
int i;
|
||||
const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
|
||||
int sl, tl, spatial_layer_target;
|
||||
|
||||
if (svc_ctx->temporal_layering_mode != 0) {
|
||||
if (si->bitrates[0] != 0) {
|
||||
unsigned int total_bitrate = 0;
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
total_bitrate += si->bitrates[sl * svc_ctx->temporal_layers +
|
||||
svc_ctx->temporal_layers - 1];
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
enc_cfg->ss_target_bitrate[sl * svc_ctx->temporal_layers] +=
|
||||
(unsigned int)si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + tl] =
|
||||
si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
if (tl > 0 && (si->bitrates[sl * svc_ctx->temporal_layers + tl] <=
|
||||
si->bitrates[sl * svc_ctx->temporal_layers + tl - 1]))
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
}
|
||||
if (total_bitrate != enc_cfg->rc_target_bitrate)
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[VPX_MAX_LAYERS] = { 0 };
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
if (si->svc_params.scaling_factor_den[sl] > 0) {
|
||||
alloc_ratio[sl] = (float)(pow(2, sl));
|
||||
total += alloc_ratio[sl];
|
||||
}
|
||||
}
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
enc_cfg->ss_target_bitrate[sl] = spatial_layer_target =
|
||||
(unsigned int)(enc_cfg->rc_target_bitrate * alloc_ratio[sl] /
|
||||
total);
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
(spatial_layer_target * 6) / 10; // 60%
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
(spatial_layer_target * 8) / 10; // 80%
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 2] =
|
||||
spatial_layer_target;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
spatial_layer_target * 2 / 3;
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
spatial_layer_target;
|
||||
} else {
|
||||
// User should explicitly assign bitrates in this case.
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (si->bitrates[0] != 0) {
|
||||
unsigned int total_bitrate = 0;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
|
||||
enc_cfg->layer_target_bitrate[i] = (unsigned int)si->bitrates[i];
|
||||
total_bitrate += si->bitrates[i];
|
||||
}
|
||||
if (total_bitrate != enc_cfg->rc_target_bitrate)
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[VPX_MAX_LAYERS] = { 0 };
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.scaling_factor_den[i] > 0) {
|
||||
alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 /
|
||||
si->svc_params.scaling_factor_den[i]);
|
||||
|
||||
alloc_ratio[i] *= alloc_ratio[i];
|
||||
total += alloc_ratio[i];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
|
||||
if (total > 0) {
|
||||
enc_cfg->layer_target_bitrate[i] =
|
||||
(unsigned int)(enc_cfg->rc_target_bitrate * alloc_ratio[i] /
|
||||
total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
vpx_codec_enc_cfg_t *enc_cfg) {
|
||||
vpx_codec_err_t res;
|
||||
int i, sl, tl;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
|
||||
enc_cfg == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
if (si == NULL) return VPX_CODEC_MEM_ERROR;
|
||||
|
||||
si->codec_ctx = codec_ctx;
|
||||
|
||||
si->width = enc_cfg->g_w;
|
||||
si->height = enc_cfg->g_h;
|
||||
|
||||
si->kf_dist = enc_cfg->kf_max_dist;
|
||||
|
||||
if (svc_ctx->spatial_layers == 0)
|
||||
svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
|
||||
if (svc_ctx->spatial_layers < 1 ||
|
||||
svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
|
||||
svc_ctx->spatial_layers);
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Note: temporal_layering_mode only applies to one-pass CBR
|
||||
// si->svc_params.temporal_layering_mode = svc_ctx->temporal_layering_mode;
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
svc_ctx->temporal_layers = 3;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
svc_ctx->temporal_layers = 2;
|
||||
}
|
||||
|
||||
for (sl = 0; sl < VPX_SS_MAX_LAYERS; ++sl) {
|
||||
si->svc_params.scaling_factor_num[sl] = DEFAULT_SCALE_FACTORS_NUM[sl];
|
||||
si->svc_params.scaling_factor_den[sl] = DEFAULT_SCALE_FACTORS_DEN[sl];
|
||||
si->svc_params.speed_per_layer[sl] = svc_ctx->speed;
|
||||
}
|
||||
if (enc_cfg->rc_end_usage == VPX_CBR && enc_cfg->g_pass == VPX_RC_ONE_PASS &&
|
||||
svc_ctx->spatial_layers <= 3) {
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
int sl2 = (svc_ctx->spatial_layers == 2) ? sl + 1 : sl;
|
||||
si->svc_params.scaling_factor_num[sl] = DEFAULT_SCALE_FACTORS_NUM_2x[sl2];
|
||||
si->svc_params.scaling_factor_den[sl] = DEFAULT_SCALE_FACTORS_DEN_2x[sl2];
|
||||
}
|
||||
if (svc_ctx->spatial_layers == 1) {
|
||||
si->svc_params.scaling_factor_num[0] = 1;
|
||||
si->svc_params.scaling_factor_den[0] = 1;
|
||||
}
|
||||
}
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
i = sl * svc_ctx->temporal_layers + tl;
|
||||
si->svc_params.max_quantizers[i] = MAX_QUANTIZER;
|
||||
si->svc_params.min_quantizers[i] = 0;
|
||||
if (enc_cfg->rc_end_usage == VPX_CBR &&
|
||||
enc_cfg->g_pass == VPX_RC_ONE_PASS) {
|
||||
si->svc_params.max_quantizers[i] = 56;
|
||||
si->svc_params.min_quantizers[i] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse aggregate command line options. Options must start with
|
||||
// "layers=xx" then followed by other options
|
||||
res = parse_options(svc_ctx, si->options);
|
||||
if (res != VPX_CODEC_OK) return res;
|
||||
|
||||
if (svc_ctx->spatial_layers < 1) svc_ctx->spatial_layers = 1;
|
||||
if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS)
|
||||
svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers < 1) svc_ctx->temporal_layers = 1;
|
||||
if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS)
|
||||
svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers * svc_ctx->spatial_layers > VPX_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"spatial layers * temporal layers exceeds the maximum number of "
|
||||
"allowed layers of %d\n",
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers, VPX_MAX_LAYERS);
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
res = assign_layer_bitrates(svc_ctx, enc_cfg);
|
||||
if (res != VPX_CODEC_OK) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"layer bitrates incorrect: \n"
|
||||
"1) spatial layer bitrates should sum up to target \n"
|
||||
"2) temporal layer bitrates should be increasing within \n"
|
||||
"a spatial layer \n");
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (svc_ctx->temporal_layers > 1) {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->temporal_layers; ++i) {
|
||||
enc_cfg->ts_target_bitrate[i] =
|
||||
enc_cfg->rc_target_bitrate / svc_ctx->temporal_layers;
|
||||
enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
|
||||
}
|
||||
}
|
||||
|
||||
if (svc_ctx->threads) enc_cfg->g_threads = svc_ctx->threads;
|
||||
|
||||
// Modify encoder configuration
|
||||
enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
|
||||
enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
|
||||
|
||||
if (enc_cfg->rc_end_usage == VPX_CBR) {
|
||||
enc_cfg->rc_resize_allowed = 0;
|
||||
enc_cfg->rc_min_quantizer = 2;
|
||||
enc_cfg->rc_max_quantizer = 56;
|
||||
enc_cfg->rc_undershoot_pct = 50;
|
||||
enc_cfg->rc_overshoot_pct = 50;
|
||||
enc_cfg->rc_buf_initial_sz = 500;
|
||||
enc_cfg->rc_buf_optimal_sz = 600;
|
||||
enc_cfg->rc_buf_sz = 1000;
|
||||
}
|
||||
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
i = sl * svc_ctx->temporal_layers + tl;
|
||||
if (enc_cfg->rc_end_usage == VPX_CBR &&
|
||||
enc_cfg->g_pass == VPX_RC_ONE_PASS) {
|
||||
si->svc_params.max_quantizers[i] = enc_cfg->rc_max_quantizer;
|
||||
si->svc_params.min_quantizers[i] = enc_cfg->rc_min_quantizer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
|
||||
enc_cfg->g_error_resilient = 1;
|
||||
|
||||
// Initialize codec
|
||||
res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
|
||||
if (res != VPX_CODEC_OK) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
|
||||
return res;
|
||||
}
|
||||
if (svc_ctx->spatial_layers > 1 || svc_ctx->temporal_layers > 1) {
|
||||
vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
|
||||
vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &si->svc_params);
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a frame into multiple layers
|
||||
* Create a superframe containing the individual layers
|
||||
*/
|
||||
vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
||||
struct vpx_image *rawimg, vpx_codec_pts_t pts,
|
||||
int64_t duration, int deadline) {
|
||||
vpx_codec_err_t res;
|
||||
vpx_codec_iter_t iter;
|
||||
const vpx_codec_cx_pkt_t *cx_pkt;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
res =
|
||||
vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0, deadline);
|
||||
if (res != VPX_CODEC_OK) {
|
||||
return res;
|
||||
}
|
||||
// save compressed data
|
||||
iter = NULL;
|
||||
while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
|
||||
switch (cx_pkt->kind) {
|
||||
case VPX_CODEC_PSNR_PKT: {
|
||||
}
|
||||
++si->psnr_pkt_received;
|
||||
break;
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static double calc_psnr(double d) {
|
||||
if (d == 0) return 100;
|
||||
return -10.0 * log(d) / log(10.0);
|
||||
}
|
||||
|
||||
// dump accumulated statistics and reset accumulated values
|
||||
void vpx_svc_dump_statistics(SvcContext *svc_ctx) {
|
||||
int number_of_frames;
|
||||
int i, j;
|
||||
uint32_t bytes_total = 0;
|
||||
double scale[COMPONENTS];
|
||||
double psnr[COMPONENTS];
|
||||
double mse[COMPONENTS];
|
||||
double y_scale;
|
||||
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || si == NULL) return;
|
||||
|
||||
number_of_frames = si->psnr_pkt_received;
|
||||
if (number_of_frames <= 0) return;
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "\n");
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
|
||||
i, si->psnr_sum[i][0] / number_of_frames,
|
||||
si->psnr_sum[i][1] / number_of_frames,
|
||||
si->psnr_sum[i][2] / number_of_frames,
|
||||
si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
|
||||
// the following psnr calculation is deduced from ffmpeg.c#print_report
|
||||
y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
|
||||
scale[1] = y_scale;
|
||||
scale[2] = scale[3] = y_scale / 4; // U or V
|
||||
scale[0] = y_scale * 1.5; // total
|
||||
|
||||
for (j = 0; j < COMPONENTS; j++) {
|
||||
psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
|
||||
mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
|
||||
}
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
|
||||
psnr[1], psnr[2], psnr[3]);
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
|
||||
mse[1], mse[2], mse[3]);
|
||||
|
||||
bytes_total += si->bytes_sum[i];
|
||||
// Clear sums for next time.
|
||||
si->bytes_sum[i] = 0;
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[i][j] = 0;
|
||||
si->sse_sum[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// only display statistics once
|
||||
si->psnr_pkt_received = 0;
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
|
||||
}
|
||||
|
||||
void vpx_svc_release(SvcContext *svc_ctx) {
|
||||
SvcInternal_t *si;
|
||||
if (svc_ctx == NULL) return;
|
||||
// do not use get_svc_internal as it will unnecessarily allocate an
|
||||
// SvcInternal_t if it was not already allocated
|
||||
si = (SvcInternal_t *)svc_ctx->internal;
|
||||
if (si != NULL) {
|
||||
free(si);
|
||||
svc_ctx->internal = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Two Pass Encoder
|
||||
// ================
|
||||
//
|
||||
// This is an example of a two pass encoder loop. It takes an input file in
|
||||
// YV12 format, passes it through the encoder twice, and writes the compressed
|
||||
// frames to disk in IVF format. It builds upon the simple_encoder example.
|
||||
//
|
||||
// Twopass Variables
|
||||
// -----------------
|
||||
// Twopass mode needs to track the current pass number and the buffer of
|
||||
// statistics packets.
|
||||
//
|
||||
// Updating The Configuration
|
||||
// ---------------------------------
|
||||
// In two pass mode, the configuration has to be updated on each pass. The
|
||||
// statistics buffer is passed on the last pass.
|
||||
//
|
||||
// Encoding A Frame
|
||||
// ----------------
|
||||
// Encoding a frame in two pass mode is identical to the simple encoder
|
||||
// example. To increase the quality while sacrificing encoding speed,
|
||||
// VPX_DL_BEST_QUALITY can be used in place of VPX_DL_GOOD_QUALITY.
|
||||
//
|
||||
// Processing Statistics Packets
|
||||
// -----------------------------
|
||||
// Each packet of type `VPX_CODEC_CX_FRAME_PKT` contains the encoded data
|
||||
// for this frame. We write a IVF frame header, followed by the raw data.
|
||||
//
|
||||
//
|
||||
// Pass Progress Reporting
|
||||
// -----------------------------
|
||||
// It's sometimes helpful to see when each pass completes.
|
||||
//
|
||||
//
|
||||
// Clean-up
|
||||
// -----------------------------
|
||||
// Destruction of the encoder instance must be done on each pass. The
|
||||
// raw image should be destroyed at the end as usual.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <codec> <width> <height> <infile> <outfile> "
|
||||
"<frame limit>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int get_frame_stats(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
|
||||
vpx_codec_pts_t pts, unsigned int duration,
|
||||
vpx_enc_frame_flags_t flags, unsigned int deadline,
|
||||
vpx_fixed_buf_t *stats) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
|
||||
if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to get frame stats.");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_STATS_PKT) {
|
||||
const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
|
||||
const size_t pkt_size = pkt->data.twopass_stats.sz;
|
||||
stats->buf = realloc(stats->buf, stats->sz + pkt_size);
|
||||
memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
|
||||
stats->sz += pkt_size;
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *ctx, const vpx_image_t *img,
|
||||
vpx_codec_pts_t pts, unsigned int duration,
|
||||
vpx_enc_frame_flags_t flags, unsigned int deadline,
|
||||
VpxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(ctx, img, pts, duration, flags, deadline);
|
||||
if (res != VPX_CODEC_OK) die_codec(ctx, "Failed to encode frame.");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts))
|
||||
die_codec(ctx, "Failed to write compressed frame.");
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
static vpx_fixed_buf_t pass0(vpx_image_t *raw, FILE *infile,
|
||||
const VpxInterface *encoder,
|
||||
const vpx_codec_enc_cfg_t *cfg, int max_frames) {
|
||||
vpx_codec_ctx_t codec;
|
||||
int frame_count = 0;
|
||||
vpx_fixed_buf_t stats = { NULL, 0 };
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Calculate frame statistics.
|
||||
while (vpx_img_read(raw, infile)) {
|
||||
++frame_count;
|
||||
get_frame_stats(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
|
||||
&stats);
|
||||
if (max_frames > 0 && frame_count >= max_frames) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (get_frame_stats(&codec, NULL, frame_count, 1, 0, VPX_DL_GOOD_QUALITY,
|
||||
&stats)) {
|
||||
}
|
||||
|
||||
printf("Pass 0 complete. Processed %d frames.\n", frame_count);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static void pass1(vpx_image_t *raw, FILE *infile, const char *outfile_name,
|
||||
const VpxInterface *encoder, const vpx_codec_enc_cfg_t *cfg,
|
||||
int max_frames) {
|
||||
VpxVideoInfo info = { encoder->fourcc,
|
||||
cfg->g_w,
|
||||
cfg->g_h,
|
||||
{ cfg->g_timebase.num, cfg->g_timebase.den } };
|
||||
VpxVideoWriter *writer = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
int frame_count = 0;
|
||||
|
||||
writer = vpx_video_writer_open(outfile_name, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing", outfile_name);
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(raw, infile)) {
|
||||
++frame_count;
|
||||
encode_frame(&codec, raw, frame_count, 1, 0, VPX_DL_GOOD_QUALITY, writer);
|
||||
|
||||
if (max_frames > 0 && frame_count >= max_frames) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 1, 0, VPX_DL_GOOD_QUALITY, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
printf("Pass 1 complete. Processed %d frames.\n", frame_count);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
int w, h;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
vpx_fixed_buf_t stats;
|
||||
|
||||
const VpxInterface *encoder = NULL;
|
||||
const int fps = 30; // TODO(dkovalev) add command line argument
|
||||
const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument
|
||||
const char *const codec_arg = argv[1];
|
||||
const char *const width_arg = argv[2];
|
||||
const char *const height_arg = argv[3];
|
||||
const char *const infile_arg = argv[4];
|
||||
const char *const outfile_arg = argv[5];
|
||||
int max_frames = 0;
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 7) die("Invalid number of arguments.");
|
||||
|
||||
max_frames = (int)strtol(argv[6], NULL, 0);
|
||||
|
||||
encoder = get_vpx_encoder_by_name(codec_arg);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
w = (int)strtol(width_arg, NULL, 0);
|
||||
h = (int)strtol(height_arg, NULL, 0);
|
||||
|
||||
if (w <= 0 || h <= 0 || (w % 2) != 0 || (h % 2) != 0)
|
||||
die("Invalid frame size: %dx%d", w, h);
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, w, h, 1))
|
||||
die("Failed to allocate image", w, h);
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
// Configuration
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = w;
|
||||
cfg.g_h = h;
|
||||
cfg.g_timebase.num = 1;
|
||||
cfg.g_timebase.den = fps;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading", infile_arg);
|
||||
|
||||
// Pass 0
|
||||
cfg.g_pass = VPX_RC_FIRST_PASS;
|
||||
stats = pass0(&raw, infile, encoder, &cfg, max_frames);
|
||||
|
||||
// Pass 1
|
||||
rewind(infile);
|
||||
cfg.g_pass = VPX_RC_LAST_PASS;
|
||||
cfg.rc_twopass_stats_in = stats;
|
||||
pass1(&raw, infile, outfile_arg, encoder, &cfg, max_frames);
|
||||
free(stats.buf);
|
||||
|
||||
vpx_img_free(&raw);
|
||||
fclose(infile);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an example demonstrating multi-resolution encoding in VP8.
|
||||
* High-resolution input video is down-sampled to lower-resolutions. The
|
||||
* encoder then encodes the video and outputs multiple bitstreams with
|
||||
* different resolutions.
|
||||
*
|
||||
* This test also allows for settings temporal layers for each spatial layer.
|
||||
* Different number of temporal layers per spatial stream may be used.
|
||||
* Currently up to 3 temporal layers per spatial stream (encoder) are supported
|
||||
* in this test.
|
||||
*/
|
||||
|
||||
#include "./vpx_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx_ports/mem_ops.h"
|
||||
#include "../tools_common.h"
|
||||
#define interface (vpx_codec_vp8_cx())
|
||||
#define fourcc 0x30385056
|
||||
|
||||
void usage_exit(void) { exit(EXIT_FAILURE); }
|
||||
|
||||
/*
|
||||
* The input video frame is downsampled several times to generate a multi-level
|
||||
* hierarchical structure. NUM_ENCODERS is defined as the number of encoding
|
||||
* levels required. For example, if the size of input video is 1280x720,
|
||||
* NUM_ENCODERS is 3, and down-sampling factor is 2, the encoder outputs 3
|
||||
* bitstreams with resolution of 1280x720(level 0), 640x360(level 1), and
|
||||
* 320x180(level 2) respectively.
|
||||
*/
|
||||
|
||||
/* Number of encoders (spatial resolutions) used in this test. */
|
||||
#define NUM_ENCODERS 3
|
||||
|
||||
/* Maximum number of temporal layers allowed for this test. */
|
||||
#define MAX_NUM_TEMPORAL_LAYERS 3
|
||||
|
||||
/* This example uses the scaler function in libyuv. */
|
||||
#include "third_party/libyuv/include/libyuv/basic_types.h"
|
||||
#include "third_party/libyuv/include/libyuv/scale.h"
|
||||
#include "third_party/libyuv/include/libyuv/cpu_id.h"
|
||||
|
||||
int (*read_frame_p)(FILE *f, vpx_image_t *img);
|
||||
|
||||
static int mulres_read_frame(FILE *f, vpx_image_t *img) {
|
||||
size_t nbytes, to_read;
|
||||
int res = 1;
|
||||
|
||||
to_read = img->w * img->h * 3 / 2;
|
||||
nbytes = fread(img->planes[0], 1, to_read, f);
|
||||
if (nbytes != to_read) {
|
||||
res = 0;
|
||||
if (nbytes > 0)
|
||||
printf("Warning: Read partial frame. Check your width & height!\n");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int mulres_read_frame_by_row(FILE *f, vpx_image_t *img) {
|
||||
size_t nbytes, to_read;
|
||||
int res = 1;
|
||||
int plane;
|
||||
|
||||
for (plane = 0; plane < 3; plane++) {
|
||||
unsigned char *ptr;
|
||||
int w = (plane ? (1 + img->d_w) / 2 : img->d_w);
|
||||
int h = (plane ? (1 + img->d_h) / 2 : img->d_h);
|
||||
int r;
|
||||
|
||||
/* Determine the correct plane based on the image format. The for-loop
|
||||
* always counts in Y,U,V order, but this may not match the order of
|
||||
* the data on disk.
|
||||
*/
|
||||
switch (plane) {
|
||||
case 1:
|
||||
ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_V
|
||||
: VPX_PLANE_U];
|
||||
break;
|
||||
case 2:
|
||||
ptr = img->planes[img->fmt == VPX_IMG_FMT_YV12 ? VPX_PLANE_U
|
||||
: VPX_PLANE_V];
|
||||
break;
|
||||
default: ptr = img->planes[plane];
|
||||
}
|
||||
|
||||
for (r = 0; r < h; r++) {
|
||||
to_read = w;
|
||||
|
||||
nbytes = fread(ptr, 1, to_read, f);
|
||||
if (nbytes != to_read) {
|
||||
res = 0;
|
||||
if (nbytes > 0)
|
||||
printf("Warning: Read partial frame. Check your width & height!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += img->stride[plane];
|
||||
}
|
||||
if (!res) break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void write_ivf_file_header(FILE *outfile, const vpx_codec_enc_cfg_t *cfg,
|
||||
int frame_cnt) {
|
||||
char header[32];
|
||||
|
||||
if (cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS) return;
|
||||
header[0] = 'D';
|
||||
header[1] = 'K';
|
||||
header[2] = 'I';
|
||||
header[3] = 'F';
|
||||
mem_put_le16(header + 4, 0); /* version */
|
||||
mem_put_le16(header + 6, 32); /* headersize */
|
||||
mem_put_le32(header + 8, fourcc); /* headersize */
|
||||
mem_put_le16(header + 12, cfg->g_w); /* width */
|
||||
mem_put_le16(header + 14, cfg->g_h); /* height */
|
||||
mem_put_le32(header + 16, cfg->g_timebase.den); /* rate */
|
||||
mem_put_le32(header + 20, cfg->g_timebase.num); /* scale */
|
||||
mem_put_le32(header + 24, frame_cnt); /* length */
|
||||
mem_put_le32(header + 28, 0); /* unused */
|
||||
|
||||
(void)fwrite(header, 1, 32, outfile);
|
||||
}
|
||||
|
||||
static void write_ivf_frame_header(FILE *outfile,
|
||||
const vpx_codec_cx_pkt_t *pkt) {
|
||||
char header[12];
|
||||
vpx_codec_pts_t pts;
|
||||
|
||||
if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) return;
|
||||
|
||||
pts = pkt->data.frame.pts;
|
||||
mem_put_le32(header, (int)pkt->data.frame.sz);
|
||||
mem_put_le32(header + 4, pts & 0xFFFFFFFF);
|
||||
mem_put_le32(header + 8, pts >> 32);
|
||||
|
||||
(void)fwrite(header, 1, 12, outfile);
|
||||
}
|
||||
|
||||
/* Temporal scaling parameters */
|
||||
/* This sets all the temporal layer parameters given |num_temporal_layers|,
|
||||
* including the target bit allocation across temporal layers. Bit allocation
|
||||
* parameters will be passed in as user parameters in another version.
|
||||
*/
|
||||
static void set_temporal_layer_pattern(int num_temporal_layers,
|
||||
vpx_codec_enc_cfg_t *cfg, int bitrate,
|
||||
int *layer_flags) {
|
||||
assert(num_temporal_layers <= MAX_NUM_TEMPORAL_LAYERS);
|
||||
switch (num_temporal_layers) {
|
||||
case 1: {
|
||||
/* 1-layer */
|
||||
cfg->ts_number_layers = 1;
|
||||
cfg->ts_periodicity = 1;
|
||||
cfg->ts_rate_decimator[0] = 1;
|
||||
cfg->ts_layer_id[0] = 0;
|
||||
cfg->ts_target_bitrate[0] = bitrate;
|
||||
|
||||
// Update L only.
|
||||
layer_flags[0] = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
/* 2-layers, with sync point at first frame of layer 1. */
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_periodicity = 2;
|
||||
cfg->ts_rate_decimator[0] = 2;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
cfg->ts_layer_id[0] = 0;
|
||||
cfg->ts_layer_id[1] = 1;
|
||||
// Use 60/40 bit allocation as example.
|
||||
cfg->ts_target_bitrate[0] = (int)(0.6f * bitrate);
|
||||
cfg->ts_target_bitrate[1] = bitrate;
|
||||
|
||||
/* 0=L, 1=GF */
|
||||
// ARF is used as predictor for all frames, and is only updated on
|
||||
// key frame. Sync point every 8 frames.
|
||||
|
||||
// Layer 0: predict from L and ARF, update L and G.
|
||||
layer_flags[0] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
|
||||
// Layer 1: sync point: predict from L and ARF, and update G.
|
||||
layer_flags[1] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
|
||||
// Layer 0, predict from L and ARF, update L.
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
|
||||
// Layer 1: predict from L, G and ARF, and update G.
|
||||
layer_flags[3] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
|
||||
VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
|
||||
// Layer 0
|
||||
layer_flags[4] = layer_flags[2];
|
||||
|
||||
// Layer 1
|
||||
layer_flags[5] = layer_flags[3];
|
||||
|
||||
// Layer 0
|
||||
layer_flags[6] = layer_flags[4];
|
||||
|
||||
// Layer 1
|
||||
layer_flags[7] = layer_flags[5];
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
default: {
|
||||
// 3-layers structure where ARF is used as predictor for all frames,
|
||||
// and is only updated on key frame.
|
||||
// Sync points for layer 1 and 2 every 8 frames.
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_periodicity = 4;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
cfg->ts_layer_id[0] = 0;
|
||||
cfg->ts_layer_id[1] = 2;
|
||||
cfg->ts_layer_id[2] = 1;
|
||||
cfg->ts_layer_id[3] = 2;
|
||||
// Use 45/20/35 bit allocation as example.
|
||||
cfg->ts_target_bitrate[0] = (int)(0.45f * bitrate);
|
||||
cfg->ts_target_bitrate[1] = (int)(0.65f * bitrate);
|
||||
cfg->ts_target_bitrate[2] = bitrate;
|
||||
|
||||
/* 0=L, 1=GF, 2=ARF */
|
||||
|
||||
// Layer 0: predict from L and ARF; update L and G.
|
||||
layer_flags[0] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF;
|
||||
|
||||
// Layer 2: sync point: predict from L and ARF; update none.
|
||||
layer_flags[1] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
|
||||
VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
|
||||
// Layer 1: sync point: predict from L and ARF; update G.
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[3] = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
|
||||
// Layer 0: predict from L and ARF; update L.
|
||||
layer_flags[4] =
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF;
|
||||
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[5] = layer_flags[3];
|
||||
|
||||
// Layer 1: predict from L, G, ARF; update G.
|
||||
layer_flags[6] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[7] = layer_flags[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The periodicity of the pattern given the number of temporal layers. */
|
||||
static int periodicity_to_num_layers[MAX_NUM_TEMPORAL_LAYERS] = { 1, 8, 8 };
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile, *outfile[NUM_ENCODERS];
|
||||
FILE *downsampled_input[NUM_ENCODERS - 1];
|
||||
char filename[50];
|
||||
vpx_codec_ctx_t codec[NUM_ENCODERS];
|
||||
vpx_codec_enc_cfg_t cfg[NUM_ENCODERS];
|
||||
int frame_cnt = 0;
|
||||
vpx_image_t raw[NUM_ENCODERS];
|
||||
vpx_codec_err_t res[NUM_ENCODERS];
|
||||
|
||||
int i;
|
||||
int width;
|
||||
int height;
|
||||
int length_frame;
|
||||
int frame_avail;
|
||||
int got_data;
|
||||
int flags = 0;
|
||||
int layer_id = 0;
|
||||
|
||||
int layer_flags[VPX_TS_MAX_PERIODICITY * NUM_ENCODERS] = { 0 };
|
||||
int flag_periodicity;
|
||||
|
||||
/*Currently, only realtime mode is supported in multi-resolution encoding.*/
|
||||
int arg_deadline = VPX_DL_REALTIME;
|
||||
|
||||
/* Set show_psnr to 1/0 to show/not show PSNR. Choose show_psnr=0 if you
|
||||
don't need to know PSNR, which will skip PSNR calculation and save
|
||||
encoding time. */
|
||||
int show_psnr = 0;
|
||||
int key_frame_insert = 0;
|
||||
uint64_t psnr_sse_total[NUM_ENCODERS] = { 0 };
|
||||
uint64_t psnr_samples_total[NUM_ENCODERS] = { 0 };
|
||||
double psnr_totals[NUM_ENCODERS][4] = { { 0, 0 } };
|
||||
int psnr_count[NUM_ENCODERS] = { 0 };
|
||||
|
||||
int64_t cx_time = 0;
|
||||
|
||||
/* Set the required target bitrates for each resolution level.
|
||||
* If target bitrate for highest-resolution level is set to 0,
|
||||
* (i.e. target_bitrate[0]=0), we skip encoding at that level.
|
||||
*/
|
||||
unsigned int target_bitrate[NUM_ENCODERS] = { 1000, 500, 100 };
|
||||
|
||||
/* Enter the frame rate of the input video */
|
||||
int framerate = 30;
|
||||
|
||||
/* Set down-sampling factor for each resolution level.
|
||||
dsf[0] controls down sampling from level 0 to level 1;
|
||||
dsf[1] controls down sampling from level 1 to level 2;
|
||||
dsf[2] is not used. */
|
||||
vpx_rational_t dsf[NUM_ENCODERS] = { { 2, 1 }, { 2, 1 }, { 1, 1 } };
|
||||
|
||||
/* Set the number of temporal layers for each encoder/resolution level,
|
||||
* starting from highest resoln down to lowest resoln. */
|
||||
unsigned int num_temporal_layers[NUM_ENCODERS] = { 3, 3, 3 };
|
||||
|
||||
if (argc != (7 + 3 * NUM_ENCODERS))
|
||||
die("Usage: %s <width> <height> <frame_rate> <infile> <outfile(s)> "
|
||||
"<rate_encoder(s)> <temporal_layer(s)> <key_frame_insert> <output "
|
||||
"psnr?> \n",
|
||||
argv[0]);
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(interface));
|
||||
|
||||
width = (int)strtol(argv[1], NULL, 0);
|
||||
height = (int)strtol(argv[2], NULL, 0);
|
||||
framerate = (int)strtol(argv[3], NULL, 0);
|
||||
|
||||
if (width < 16 || width % 2 || height < 16 || height % 2)
|
||||
die("Invalid resolution: %ldx%ld", width, height);
|
||||
|
||||
/* Open input video file for encoding */
|
||||
if (!(infile = fopen(argv[4], "rb")))
|
||||
die("Failed to open %s for reading", argv[4]);
|
||||
|
||||
/* Open output file for each encoder to output bitstreams */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
if (!target_bitrate[i]) {
|
||||
outfile[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(outfile[i] = fopen(argv[i + 5], "wb")))
|
||||
die("Failed to open %s for writing", argv[i + 4]);
|
||||
}
|
||||
|
||||
// Bitrates per spatial layer: overwrite default rates above.
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
target_bitrate[i] = (int)strtol(argv[NUM_ENCODERS + 5 + i], NULL, 0);
|
||||
}
|
||||
|
||||
// Temporal layers per spatial layers: overwrite default settings above.
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
num_temporal_layers[i] =
|
||||
(int)strtol(argv[2 * NUM_ENCODERS + 5 + i], NULL, 0);
|
||||
if (num_temporal_layers[i] < 1 || num_temporal_layers[i] > 3)
|
||||
die("Invalid temporal layers: %d, Must be 1, 2, or 3. \n",
|
||||
num_temporal_layers);
|
||||
}
|
||||
|
||||
/* Open file to write out each spatially downsampled input stream. */
|
||||
for (i = 0; i < NUM_ENCODERS - 1; i++) {
|
||||
// Highest resoln is encoder 0.
|
||||
if (sprintf(filename, "ds%d.yuv", NUM_ENCODERS - i) < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
downsampled_input[i] = fopen(filename, "wb");
|
||||
}
|
||||
|
||||
key_frame_insert = (int)strtol(argv[3 * NUM_ENCODERS + 5], NULL, 0);
|
||||
|
||||
show_psnr = (int)strtol(argv[3 * NUM_ENCODERS + 6], NULL, 0);
|
||||
|
||||
/* Populate default encoder configuration */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
res[i] = vpx_codec_enc_config_default(interface, &cfg[i], 0);
|
||||
if (res[i]) {
|
||||
printf("Failed to get config: %s\n", vpx_codec_err_to_string(res[i]));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the default configuration according to needs of the application.
|
||||
*/
|
||||
/* Highest-resolution encoder settings */
|
||||
cfg[0].g_w = width;
|
||||
cfg[0].g_h = height;
|
||||
cfg[0].rc_dropframe_thresh = 0;
|
||||
cfg[0].rc_end_usage = VPX_CBR;
|
||||
cfg[0].rc_resize_allowed = 0;
|
||||
cfg[0].rc_min_quantizer = 2;
|
||||
cfg[0].rc_max_quantizer = 56;
|
||||
cfg[0].rc_undershoot_pct = 100;
|
||||
cfg[0].rc_overshoot_pct = 15;
|
||||
cfg[0].rc_buf_initial_sz = 500;
|
||||
cfg[0].rc_buf_optimal_sz = 600;
|
||||
cfg[0].rc_buf_sz = 1000;
|
||||
cfg[0].g_error_resilient = 1; /* Enable error resilient mode */
|
||||
cfg[0].g_lag_in_frames = 0;
|
||||
|
||||
/* Disable automatic keyframe placement */
|
||||
/* Note: These 3 settings are copied to all levels. But, except the lowest
|
||||
* resolution level, all other levels are set to VPX_KF_DISABLED internally.
|
||||
*/
|
||||
cfg[0].kf_mode = VPX_KF_AUTO;
|
||||
cfg[0].kf_min_dist = 3000;
|
||||
cfg[0].kf_max_dist = 3000;
|
||||
|
||||
cfg[0].rc_target_bitrate = target_bitrate[0]; /* Set target bitrate */
|
||||
cfg[0].g_timebase.num = 1; /* Set fps */
|
||||
cfg[0].g_timebase.den = framerate;
|
||||
|
||||
/* Other-resolution encoder settings */
|
||||
for (i = 1; i < NUM_ENCODERS; i++) {
|
||||
memcpy(&cfg[i], &cfg[0], sizeof(vpx_codec_enc_cfg_t));
|
||||
|
||||
cfg[i].rc_target_bitrate = target_bitrate[i];
|
||||
|
||||
/* Note: Width & height of other-resolution encoders are calculated
|
||||
* from the highest-resolution encoder's size and the corresponding
|
||||
* down_sampling_factor.
|
||||
*/
|
||||
{
|
||||
unsigned int iw = cfg[i - 1].g_w * dsf[i - 1].den + dsf[i - 1].num - 1;
|
||||
unsigned int ih = cfg[i - 1].g_h * dsf[i - 1].den + dsf[i - 1].num - 1;
|
||||
cfg[i].g_w = iw / dsf[i - 1].num;
|
||||
cfg[i].g_h = ih / dsf[i - 1].num;
|
||||
}
|
||||
|
||||
/* Make width & height to be multiplier of 2. */
|
||||
// Should support odd size ???
|
||||
if ((cfg[i].g_w) % 2) cfg[i].g_w++;
|
||||
if ((cfg[i].g_h) % 2) cfg[i].g_h++;
|
||||
}
|
||||
|
||||
// Set the number of threads per encode/spatial layer.
|
||||
// (1, 1, 1) means no encoder threading.
|
||||
cfg[0].g_threads = 1;
|
||||
cfg[1].g_threads = 1;
|
||||
cfg[2].g_threads = 1;
|
||||
|
||||
/* Allocate image for each encoder */
|
||||
for (i = 0; i < NUM_ENCODERS; i++)
|
||||
if (!vpx_img_alloc(&raw[i], VPX_IMG_FMT_I420, cfg[i].g_w, cfg[i].g_h, 32))
|
||||
die("Failed to allocate image", cfg[i].g_w, cfg[i].g_h);
|
||||
|
||||
if (raw[0].stride[VPX_PLANE_Y] == (int)raw[0].d_w)
|
||||
read_frame_p = mulres_read_frame;
|
||||
else
|
||||
read_frame_p = mulres_read_frame_by_row;
|
||||
|
||||
for (i = 0; i < NUM_ENCODERS; i++)
|
||||
if (outfile[i]) write_ivf_file_header(outfile[i], &cfg[i], 0);
|
||||
|
||||
/* Temporal layers settings */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
set_temporal_layer_pattern(num_temporal_layers[i], &cfg[i],
|
||||
cfg[i].rc_target_bitrate,
|
||||
&layer_flags[i * VPX_TS_MAX_PERIODICITY]);
|
||||
}
|
||||
|
||||
/* Initialize multi-encoder */
|
||||
if (vpx_codec_enc_init_multi(&codec[0], interface, &cfg[0], NUM_ENCODERS,
|
||||
(show_psnr ? VPX_CODEC_USE_PSNR : 0), &dsf[0]))
|
||||
die_codec(&codec[0], "Failed to initialize encoder");
|
||||
|
||||
/* The extra encoding configuration parameters can be set as follows. */
|
||||
/* Set encoding speed */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
int speed = -6;
|
||||
/* Lower speed for the lowest resolution. */
|
||||
if (i == NUM_ENCODERS - 1) speed = -4;
|
||||
if (vpx_codec_control(&codec[i], VP8E_SET_CPUUSED, speed))
|
||||
die_codec(&codec[i], "Failed to set cpu_used");
|
||||
}
|
||||
|
||||
/* Set static threshold = 1 for all encoders */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
if (vpx_codec_control(&codec[i], VP8E_SET_STATIC_THRESHOLD, 1))
|
||||
die_codec(&codec[i], "Failed to set static threshold");
|
||||
}
|
||||
|
||||
/* Set NOISE_SENSITIVITY to do TEMPORAL_DENOISING */
|
||||
/* Enable denoising for the highest-resolution encoder. */
|
||||
if (vpx_codec_control(&codec[0], VP8E_SET_NOISE_SENSITIVITY, 1))
|
||||
die_codec(&codec[0], "Failed to set noise_sensitivity");
|
||||
if (vpx_codec_control(&codec[1], VP8E_SET_NOISE_SENSITIVITY, 1))
|
||||
die_codec(&codec[1], "Failed to set noise_sensitivity");
|
||||
for (i = 2; i < NUM_ENCODERS; i++) {
|
||||
if (vpx_codec_control(&codec[i], VP8E_SET_NOISE_SENSITIVITY, 0))
|
||||
die_codec(&codec[i], "Failed to set noise_sensitivity");
|
||||
}
|
||||
|
||||
/* Set the number of token partitions */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
if (vpx_codec_control(&codec[i], VP8E_SET_TOKEN_PARTITIONS, 1))
|
||||
die_codec(&codec[i], "Failed to set static threshold");
|
||||
}
|
||||
|
||||
/* Set the max intra target bitrate */
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
unsigned int max_intra_size_pct =
|
||||
(int)(((double)cfg[0].rc_buf_optimal_sz * 0.5) * framerate / 10);
|
||||
if (vpx_codec_control(&codec[i], VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
||||
max_intra_size_pct))
|
||||
die_codec(&codec[i], "Failed to set static threshold");
|
||||
// printf("%d %d \n",i,max_intra_size_pct);
|
||||
}
|
||||
|
||||
frame_avail = 1;
|
||||
got_data = 0;
|
||||
|
||||
while (frame_avail || got_data) {
|
||||
struct vpx_usec_timer timer;
|
||||
vpx_codec_iter_t iter[NUM_ENCODERS] = { NULL };
|
||||
const vpx_codec_cx_pkt_t *pkt[NUM_ENCODERS];
|
||||
|
||||
flags = 0;
|
||||
frame_avail = read_frame_p(infile, &raw[0]);
|
||||
|
||||
if (frame_avail) {
|
||||
for (i = 1; i < NUM_ENCODERS; i++) {
|
||||
/*Scale the image down a number of times by downsampling factor*/
|
||||
/* FilterMode 1 or 2 give better psnr than FilterMode 0. */
|
||||
I420Scale(
|
||||
raw[i - 1].planes[VPX_PLANE_Y], raw[i - 1].stride[VPX_PLANE_Y],
|
||||
raw[i - 1].planes[VPX_PLANE_U], raw[i - 1].stride[VPX_PLANE_U],
|
||||
raw[i - 1].planes[VPX_PLANE_V], raw[i - 1].stride[VPX_PLANE_V],
|
||||
raw[i - 1].d_w, raw[i - 1].d_h, raw[i].planes[VPX_PLANE_Y],
|
||||
raw[i].stride[VPX_PLANE_Y], raw[i].planes[VPX_PLANE_U],
|
||||
raw[i].stride[VPX_PLANE_U], raw[i].planes[VPX_PLANE_V],
|
||||
raw[i].stride[VPX_PLANE_V], raw[i].d_w, raw[i].d_h, 1);
|
||||
/* Write out down-sampled input. */
|
||||
length_frame = cfg[i].g_w * cfg[i].g_h * 3 / 2;
|
||||
if (fwrite(raw[i].planes[0], 1, length_frame,
|
||||
downsampled_input[NUM_ENCODERS - i - 1]) !=
|
||||
(unsigned int)length_frame) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the flags (reference and update) for all the encoders.*/
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
layer_id = cfg[i].ts_layer_id[frame_cnt % cfg[i].ts_periodicity];
|
||||
flags = 0;
|
||||
flag_periodicity = periodicity_to_num_layers[num_temporal_layers[i] - 1];
|
||||
flags = layer_flags[i * VPX_TS_MAX_PERIODICITY +
|
||||
frame_cnt % flag_periodicity];
|
||||
// Key frame flag for first frame.
|
||||
if (frame_cnt == 0) {
|
||||
flags |= VPX_EFLAG_FORCE_KF;
|
||||
}
|
||||
if (frame_cnt > 0 && frame_cnt == key_frame_insert) {
|
||||
flags = VPX_EFLAG_FORCE_KF;
|
||||
}
|
||||
|
||||
vpx_codec_control(&codec[i], VP8E_SET_FRAME_FLAGS, flags);
|
||||
vpx_codec_control(&codec[i], VP8E_SET_TEMPORAL_LAYER_ID, layer_id);
|
||||
}
|
||||
|
||||
/* Encode each frame at multi-levels */
|
||||
/* Note the flags must be set to 0 in the encode call if they are set
|
||||
for each frame with the vpx_codec_control(), as done above. */
|
||||
vpx_usec_timer_start(&timer);
|
||||
if (vpx_codec_encode(&codec[0], frame_avail ? &raw[0] : NULL, frame_cnt, 1,
|
||||
0, arg_deadline)) {
|
||||
die_codec(&codec[0], "Failed to encode frame");
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
cx_time += vpx_usec_timer_elapsed(&timer);
|
||||
|
||||
for (i = NUM_ENCODERS - 1; i >= 0; i--) {
|
||||
got_data = 0;
|
||||
while ((pkt[i] = vpx_codec_get_cx_data(&codec[i], &iter[i]))) {
|
||||
got_data = 1;
|
||||
switch (pkt[i]->kind) {
|
||||
case VPX_CODEC_CX_FRAME_PKT:
|
||||
write_ivf_frame_header(outfile[i], pkt[i]);
|
||||
(void)fwrite(pkt[i]->data.frame.buf, 1, pkt[i]->data.frame.sz,
|
||||
outfile[i]);
|
||||
break;
|
||||
case VPX_CODEC_PSNR_PKT:
|
||||
if (show_psnr) {
|
||||
int j;
|
||||
|
||||
psnr_sse_total[i] += pkt[i]->data.psnr.sse[0];
|
||||
psnr_samples_total[i] += pkt[i]->data.psnr.samples[0];
|
||||
for (j = 0; j < 4; j++) {
|
||||
psnr_totals[i][j] += pkt[i]->data.psnr.psnr[j];
|
||||
}
|
||||
psnr_count[i]++;
|
||||
}
|
||||
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
frame_cnt++;
|
||||
}
|
||||
printf("\n");
|
||||
printf("Frame cnt and encoding time/FPS stats for encoding: %d %f %f \n",
|
||||
frame_cnt, 1000 * (float)cx_time / (double)(frame_cnt * 1000000),
|
||||
1000000 * (double)frame_cnt / (double)cx_time);
|
||||
|
||||
fclose(infile);
|
||||
|
||||
printf("Processed %ld frames.\n", (long int)frame_cnt - 1);
|
||||
for (i = 0; i < NUM_ENCODERS; i++) {
|
||||
/* Calculate PSNR and print it out */
|
||||
if ((show_psnr) && (psnr_count[i] > 0)) {
|
||||
int j;
|
||||
double ovpsnr =
|
||||
sse_to_psnr(psnr_samples_total[i], 255.0, psnr_sse_total[i]);
|
||||
|
||||
fprintf(stderr, "\n ENC%d PSNR (Overall/Avg/Y/U/V)", i);
|
||||
|
||||
fprintf(stderr, " %.3lf", ovpsnr);
|
||||
for (j = 0; j < 4; j++) {
|
||||
fprintf(stderr, " %.3lf", psnr_totals[i][j] / psnr_count[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (vpx_codec_destroy(&codec[i]))
|
||||
die_codec(&codec[i], "Failed to destroy codec");
|
||||
|
||||
vpx_img_free(&raw[i]);
|
||||
|
||||
if (!outfile[i]) continue;
|
||||
|
||||
/* Try to rewrite the file header with the actual frame count */
|
||||
if (!fseek(outfile[i], 0, SEEK_SET))
|
||||
write_ivf_file_header(outfile[i], &cfg[i], frame_cnt - 1);
|
||||
fclose(outfile[i]);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// VP8 Set Reference Frame
|
||||
// =======================
|
||||
//
|
||||
// This is an example demonstrating how to overwrite the VP8 encoder's
|
||||
// internal reference frame. In the sample we set the last frame to the
|
||||
// current frame. If this is done at a cut scene it will avoid a keyframe.
|
||||
// This technique could be used to bounce between two cameras.
|
||||
//
|
||||
// Note that the decoder would also have to set the reference frame to the
|
||||
// same value on the same frame, or the video will become corrupt.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// This example adds a single argument to the `simple_encoder` example,
|
||||
// which specifies the frame number to update the reference frame on.
|
||||
// The parameter is parsed as follows:
|
||||
//
|
||||
//
|
||||
// Extra Variables
|
||||
// ---------------
|
||||
// This example maintains the frame number passed on the command line
|
||||
// in the `update_frame_num` variable.
|
||||
//
|
||||
//
|
||||
// Configuration
|
||||
// -------------
|
||||
//
|
||||
// The reference frame is updated on the frame specified on the command
|
||||
// line.
|
||||
//
|
||||
// Observing The Effects
|
||||
// ---------------------
|
||||
// Use the `simple_encoder` example to encode a sample with a cut scene.
|
||||
// Determine the frame number of the cut scene by looking for a generated
|
||||
// key-frame (indicated by a 'K'). Supply that frame number as an argument
|
||||
// to this example, and observe that no key-frame is generated.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vp8/common/common.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr, "Usage: %s <width> <height> <infile> <outfile> <frame>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
|
||||
int frame_index, VpxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(codec, img, frame_index, 1, 0, VPX_DL_GOOD_QUALITY);
|
||||
if (res != VPX_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoInfo info;
|
||||
VpxVideoWriter *writer = NULL;
|
||||
const VpxInterface *encoder = NULL;
|
||||
int update_frame_num = 0;
|
||||
const int fps = 30; // TODO(dkovalev) add command line argument
|
||||
const int bitrate = 200; // kbit/s TODO(dkovalev) add command line argument
|
||||
|
||||
vp8_zero(codec);
|
||||
vp8_zero(cfg);
|
||||
vp8_zero(info);
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc != 6) die("Invalid number of arguments");
|
||||
|
||||
// TODO(dkovalev): add vp9 support and rename the file accordingly
|
||||
encoder = get_vpx_encoder_by_name("vp8");
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
update_frame_num = atoi(argv[5]);
|
||||
if (!update_frame_num) die("Couldn't parse frame number '%s'\n", argv[5]);
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(argv[1], NULL, 0);
|
||||
info.frame_height = (int)strtol(argv[2], NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
|
||||
writer = vpx_video_writer_open(argv[4], kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", argv[4]);
|
||||
|
||||
if (!(infile = fopen(argv[3], "rb")))
|
||||
die("Failed to open %s for reading.", argv[3]);
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(&raw, infile)) {
|
||||
if (frame_count + 1 == update_frame_num) {
|
||||
vpx_ref_frame_t ref;
|
||||
ref.frame_type = VP8_LAST_FRAME;
|
||||
ref.img = raw;
|
||||
if (vpx_codec_control(&codec, VP8_SET_REFERENCE, &ref))
|
||||
die_codec(&codec, "Failed to set reference frame");
|
||||
}
|
||||
|
||||
encode_frame(&codec, &raw, frame_count++, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
vpx_img_free(&raw);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vp9/common/vp9_common.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) {
|
||||
fprintf(stderr,
|
||||
"vp9_lossless_encoder: Example demonstrating VP9 lossless "
|
||||
"encoding feature. Supports raw input only.\n");
|
||||
fprintf(stderr, "Usage: %s <width> <height> <infile> <outfile>\n", exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *codec, vpx_image_t *img,
|
||||
int frame_index, int flags, VpxVideoWriter *writer) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(codec, img, frame_index, 1, flags, VPX_DL_GOOD_QUALITY);
|
||||
if (res != VPX_CODEC_OK) die_codec(codec, "Failed to encode frame");
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(codec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int frame_count = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoInfo info;
|
||||
VpxVideoWriter *writer = NULL;
|
||||
const VpxInterface *encoder = NULL;
|
||||
const int fps = 30;
|
||||
|
||||
vp9_zero(info);
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc < 5) die("Invalid number of arguments");
|
||||
|
||||
encoder = get_vpx_encoder_by_name("vp9");
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(argv[1], NULL, 0);
|
||||
info.frame_height = (int)strtol(argv[2], NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&codec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
|
||||
writer = vpx_video_writer_open(argv[4], kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", argv[4]);
|
||||
|
||||
if (!(infile = fopen(argv[3], "rb")))
|
||||
die("Failed to open %s for reading.", argv[3]);
|
||||
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
if (vpx_codec_control_(&codec, VP9E_SET_LOSSLESS, 1))
|
||||
die_codec(&codec, "Failed to use lossless mode");
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(&raw, infile)) {
|
||||
encode_frame(&codec, &raw, frame_count++, 0, writer);
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
while (encode_frame(&codec, NULL, -1, 0, writer)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_count);
|
||||
|
||||
vpx_img_free(&raw);
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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.
|
||||
*/
|
||||
|
||||
// VP9 Set Reference Frame
|
||||
// ============================
|
||||
//
|
||||
// This is an example demonstrating how to overwrite the VP9 encoder's
|
||||
// internal reference frame. In the sample we set the last frame to the
|
||||
// current frame. This technique could be used to bounce between two cameras.
|
||||
//
|
||||
// The decoder would also have to set the reference frame to the same value
|
||||
// on the same frame, or the video will become corrupt. The 'test_decode'
|
||||
// variable is set to 1 in this example that tests if the encoder and decoder
|
||||
// results are matching.
|
||||
//
|
||||
// Usage
|
||||
// -----
|
||||
// This example encodes a raw video. And the last argument passed in specifies
|
||||
// the frame number to update the reference frame on. For example, run
|
||||
// examples/vp9cx_set_ref 352 288 in.yuv out.ivf 4 30
|
||||
// The parameter is parsed as follows:
|
||||
//
|
||||
//
|
||||
// Extra Variables
|
||||
// ---------------
|
||||
// This example maintains the frame number passed on the command line
|
||||
// in the `update_frame_num` variable.
|
||||
//
|
||||
//
|
||||
// Configuration
|
||||
// -------------
|
||||
//
|
||||
// The reference frame is updated on the frame specified on the command
|
||||
// line.
|
||||
//
|
||||
// Observing The Effects
|
||||
// ---------------------
|
||||
// The encoder and decoder results should be matching when the same reference
|
||||
// frame setting operation is done in both encoder and decoder. Otherwise,
|
||||
// the encoder/decoder mismatch would be seen.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vp9/common/vp9_common.h"
|
||||
|
||||
#include "./tools_common.h"
|
||||
#include "./video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit() {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <width> <height> <infile> <outfile> "
|
||||
"<frame> <limit(optional)>\n",
|
||||
exec_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void testing_decode(vpx_codec_ctx_t *encoder, vpx_codec_ctx_t *decoder,
|
||||
unsigned int frame_out, int *mismatch_seen) {
|
||||
vpx_image_t enc_img, dec_img;
|
||||
struct vp9_ref_frame ref_enc, ref_dec;
|
||||
|
||||
if (*mismatch_seen) return;
|
||||
|
||||
ref_enc.idx = 0;
|
||||
ref_dec.idx = 0;
|
||||
if (vpx_codec_control(encoder, VP9_GET_REFERENCE, &ref_enc))
|
||||
die_codec(encoder, "Failed to get encoder reference frame");
|
||||
enc_img = ref_enc.img;
|
||||
if (vpx_codec_control(decoder, VP9_GET_REFERENCE, &ref_dec))
|
||||
die_codec(decoder, "Failed to get decoder reference frame");
|
||||
dec_img = ref_dec.img;
|
||||
|
||||
if (!compare_img(&enc_img, &dec_img)) {
|
||||
int y[4], u[4], v[4];
|
||||
|
||||
*mismatch_seen = 1;
|
||||
|
||||
find_mismatch(&enc_img, &dec_img, y, u, v);
|
||||
printf(
|
||||
"Encode/decode mismatch on frame %d at"
|
||||
" Y[%d, %d] {%d/%d},"
|
||||
" U[%d, %d] {%d/%d},"
|
||||
" V[%d, %d] {%d/%d}",
|
||||
frame_out, y[0], y[1], y[2], y[3], u[0], u[1], u[2], u[3], v[0], v[1],
|
||||
v[2], v[3]);
|
||||
}
|
||||
|
||||
vpx_img_free(&enc_img);
|
||||
vpx_img_free(&dec_img);
|
||||
}
|
||||
|
||||
static int encode_frame(vpx_codec_ctx_t *ecodec, vpx_image_t *img,
|
||||
unsigned int frame_in, VpxVideoWriter *writer,
|
||||
int test_decode, vpx_codec_ctx_t *dcodec,
|
||||
unsigned int *frame_out, int *mismatch_seen) {
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
int got_data;
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(ecodec, img, frame_in, 1, 0, VPX_DL_GOOD_QUALITY);
|
||||
if (res != VPX_CODEC_OK) die_codec(ecodec, "Failed to encode frame");
|
||||
|
||||
got_data = 0;
|
||||
|
||||
while ((pkt = vpx_codec_get_cx_data(ecodec, &iter)) != NULL) {
|
||||
got_pkts = 1;
|
||||
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
const int keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
|
||||
|
||||
if (!(pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT)) {
|
||||
*frame_out += 1;
|
||||
}
|
||||
|
||||
if (!vpx_video_writer_write_frame(writer, pkt->data.frame.buf,
|
||||
pkt->data.frame.sz,
|
||||
pkt->data.frame.pts)) {
|
||||
die_codec(ecodec, "Failed to write compressed frame");
|
||||
}
|
||||
printf(keyframe ? "K" : ".");
|
||||
fflush(stdout);
|
||||
got_data = 1;
|
||||
|
||||
// Decode 1 frame.
|
||||
if (test_decode) {
|
||||
if (vpx_codec_decode(dcodec, pkt->data.frame.buf,
|
||||
(unsigned int)pkt->data.frame.sz, NULL, 0))
|
||||
die_codec(dcodec, "Failed to decode frame.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mismatch checking
|
||||
if (got_data && test_decode) {
|
||||
testing_decode(ecodec, dcodec, *frame_out, mismatch_seen);
|
||||
}
|
||||
|
||||
return got_pkts;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *infile = NULL;
|
||||
// Encoder
|
||||
vpx_codec_ctx_t ecodec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
unsigned int frame_in = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
VpxVideoInfo info;
|
||||
VpxVideoWriter *writer = NULL;
|
||||
const VpxInterface *encoder = NULL;
|
||||
|
||||
// Test encoder/decoder mismatch.
|
||||
int test_decode = 1;
|
||||
// Decoder
|
||||
vpx_codec_ctx_t dcodec;
|
||||
unsigned int frame_out = 0;
|
||||
|
||||
// The frame number to set reference frame on
|
||||
unsigned int update_frame_num = 0;
|
||||
int mismatch_seen = 0;
|
||||
|
||||
const int fps = 30;
|
||||
const int bitrate = 500;
|
||||
|
||||
const char *width_arg = NULL;
|
||||
const char *height_arg = NULL;
|
||||
const char *infile_arg = NULL;
|
||||
const char *outfile_arg = NULL;
|
||||
const char *update_frame_num_arg = NULL;
|
||||
unsigned int limit = 0;
|
||||
|
||||
vp9_zero(ecodec);
|
||||
vp9_zero(cfg);
|
||||
vp9_zero(info);
|
||||
|
||||
exec_name = argv[0];
|
||||
|
||||
if (argc < 6) die("Invalid number of arguments");
|
||||
|
||||
width_arg = argv[1];
|
||||
height_arg = argv[2];
|
||||
infile_arg = argv[3];
|
||||
outfile_arg = argv[4];
|
||||
update_frame_num_arg = argv[5];
|
||||
|
||||
encoder = get_vpx_encoder_by_name("vp9");
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
update_frame_num = (unsigned int)strtoul(update_frame_num_arg, NULL, 0);
|
||||
// In VP9, the reference buffers (cm->buffer_pool->frame_bufs[i].buf) are
|
||||
// allocated while calling vpx_codec_encode(), thus, setting reference for
|
||||
// 1st frame isn't supported.
|
||||
if (update_frame_num <= 1) {
|
||||
die("Couldn't parse frame number '%s'\n", update_frame_num_arg);
|
||||
}
|
||||
|
||||
if (argc > 6) {
|
||||
limit = (unsigned int)strtoul(argv[6], NULL, 0);
|
||||
if (update_frame_num > limit)
|
||||
die("Update frame number couldn't larger than limit\n");
|
||||
}
|
||||
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = (int)strtol(width_arg, NULL, 0);
|
||||
info.frame_height = (int)strtol(height_arg, NULL, 0);
|
||||
info.time_base.numerator = 1;
|
||||
info.time_base.denominator = fps;
|
||||
|
||||
if (info.frame_width <= 0 || info.frame_height <= 0 ||
|
||||
(info.frame_width % 2) != 0 || (info.frame_height % 2) != 0) {
|
||||
die("Invalid frame size: %dx%d", info.frame_width, info.frame_height);
|
||||
}
|
||||
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, info.frame_width,
|
||||
info.frame_height, 1)) {
|
||||
die("Failed to allocate image.");
|
||||
}
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) die_codec(&ecodec, "Failed to get default codec config.");
|
||||
|
||||
cfg.g_w = info.frame_width;
|
||||
cfg.g_h = info.frame_height;
|
||||
cfg.g_timebase.num = info.time_base.numerator;
|
||||
cfg.g_timebase.den = info.time_base.denominator;
|
||||
cfg.rc_target_bitrate = bitrate;
|
||||
cfg.g_lag_in_frames = 3;
|
||||
|
||||
writer = vpx_video_writer_open(outfile_arg, kContainerIVF, &info);
|
||||
if (!writer) die("Failed to open %s for writing.", outfile_arg);
|
||||
|
||||
if (!(infile = fopen(infile_arg, "rb")))
|
||||
die("Failed to open %s for reading.", infile_arg);
|
||||
|
||||
if (vpx_codec_enc_init(&ecodec, encoder->codec_interface(), &cfg, 0))
|
||||
die_codec(&ecodec, "Failed to initialize encoder");
|
||||
|
||||
// Disable alt_ref.
|
||||
if (vpx_codec_control(&ecodec, VP8E_SET_ENABLEAUTOALTREF, 0))
|
||||
die_codec(&ecodec, "Failed to set enable auto alt ref");
|
||||
|
||||
if (test_decode) {
|
||||
const VpxInterface *decoder = get_vpx_decoder_by_name("vp9");
|
||||
if (vpx_codec_dec_init(&dcodec, decoder->codec_interface(), NULL, 0))
|
||||
die_codec(&dcodec, "Failed to initialize decoder.");
|
||||
}
|
||||
|
||||
// Encode frames.
|
||||
while (vpx_img_read(&raw, infile)) {
|
||||
if (limit && frame_in >= limit) break;
|
||||
if (update_frame_num > 1 && frame_out + 1 == update_frame_num) {
|
||||
vpx_ref_frame_t ref;
|
||||
ref.frame_type = VP8_LAST_FRAME;
|
||||
ref.img = raw;
|
||||
// Set reference frame in encoder.
|
||||
if (vpx_codec_control(&ecodec, VP8_SET_REFERENCE, &ref))
|
||||
die_codec(&ecodec, "Failed to set reference frame");
|
||||
printf(" <SET_REF>");
|
||||
|
||||
// If set_reference in decoder is commented out, the enc/dec mismatch
|
||||
// would be seen.
|
||||
if (test_decode) {
|
||||
if (vpx_codec_control(&dcodec, VP8_SET_REFERENCE, &ref))
|
||||
die_codec(&dcodec, "Failed to set reference frame");
|
||||
}
|
||||
}
|
||||
|
||||
encode_frame(&ecodec, &raw, frame_in, writer, test_decode, &dcodec,
|
||||
&frame_out, &mismatch_seen);
|
||||
frame_in++;
|
||||
if (mismatch_seen) break;
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
if (!mismatch_seen)
|
||||
while (encode_frame(&ecodec, NULL, frame_in, writer, test_decode, &dcodec,
|
||||
&frame_out, &mismatch_seen)) {
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
fclose(infile);
|
||||
printf("Processed %d frames.\n", frame_out);
|
||||
|
||||
if (test_decode) {
|
||||
if (!mismatch_seen)
|
||||
printf("Encoder/decoder results are matching.\n");
|
||||
else
|
||||
printf("Encoder/decoder results are NOT matching.\n");
|
||||
}
|
||||
|
||||
if (test_decode)
|
||||
if (vpx_codec_destroy(&dcodec))
|
||||
die_codec(&dcodec, "Failed to destroy decoder");
|
||||
|
||||
vpx_img_free(&raw);
|
||||
if (vpx_codec_destroy(&ecodec))
|
||||
die_codec(&ecodec, "Failed to destroy encoder.");
|
||||
|
||||
vpx_video_writer_close(writer);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fuzzer for libvpx decoders
|
||||
* ==========================
|
||||
* Requirements
|
||||
* --------------
|
||||
* Requires Clang 6.0 or above as -fsanitize=fuzzer is used as a linker
|
||||
* option.
|
||||
|
||||
* Steps to build
|
||||
* --------------
|
||||
* Clone libvpx repository
|
||||
$git clone https://chromium.googlesource.com/webm/libvpx
|
||||
|
||||
* Create a directory in parallel to libvpx and change directory
|
||||
$mkdir vpx_dec_fuzzer
|
||||
$cd vpx_dec_fuzzer/
|
||||
|
||||
* Enable sanitizers (Supported: address integer memory thread undefined)
|
||||
$source ../libvpx/tools/set_analyzer_env.sh address
|
||||
|
||||
* Configure libvpx.
|
||||
* Note --size-limit and VPX_MAX_ALLOCABLE_MEMORY are defined to avoid
|
||||
* Out of memory errors when running generated fuzzer binary
|
||||
$../libvpx/configure --disable-unit-tests --size-limit=12288x12288 \
|
||||
--extra-cflags="-fsanitize=fuzzer-no-link \
|
||||
-DVPX_MAX_ALLOCABLE_MEMORY=1073741824" \
|
||||
--disable-webm-io --enable-debug --disable-vp8-encoder \
|
||||
--disable-vp9-encoder --disable-examples
|
||||
|
||||
* Build libvpx
|
||||
$make -j32
|
||||
|
||||
* Build vp9 fuzzer
|
||||
$ $CXX $CXXFLAGS -std=c++11 -DDECODER=vp9 \
|
||||
-fsanitize=fuzzer -I../libvpx -I. -Wl,--start-group \
|
||||
../libvpx/examples/vpx_dec_fuzzer.cc -o ./vpx_dec_fuzzer_vp9 \
|
||||
./libvpx.a -Wl,--end-group
|
||||
|
||||
* DECODER should be defined as vp9 or vp8 to enable vp9/vp8
|
||||
*
|
||||
* create a corpus directory and copy some ivf files there.
|
||||
* Based on which codec (vp8/vp9) is being tested, it is recommended to
|
||||
* have corresponding ivf files in corpus directory
|
||||
* Empty corpus directoy also is acceptable, though not recommended
|
||||
$mkdir CORPUS && cp some-files CORPUS
|
||||
|
||||
* Run fuzzing:
|
||||
$./vpx_dec_fuzzer_vp9 CORPUS
|
||||
|
||||
* References:
|
||||
* http://llvm.org/docs/LibFuzzer.html
|
||||
* https://github.com/google/oss-fuzz
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx_ports/mem_ops.h"
|
||||
|
||||
#define IVF_FRAME_HDR_SZ (4 + 8) /* 4 byte size + 8 byte timestamp */
|
||||
#define IVF_FILE_HDR_SZ 32
|
||||
|
||||
#define VPXD_INTERFACE(name) VPXD_INTERFACE_(name)
|
||||
#define VPXD_INTERFACE_(name) vpx_codec_##name##_dx()
|
||||
|
||||
extern "C" void usage_exit(void) { exit(EXIT_FAILURE); }
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
if (size <= IVF_FILE_HDR_SZ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vpx_codec_ctx_t codec;
|
||||
// Set thread count in the range [1, 64].
|
||||
const unsigned int threads = (data[IVF_FILE_HDR_SZ] & 0x3f) + 1;
|
||||
vpx_codec_dec_cfg_t cfg = { threads, 0, 0 };
|
||||
if (vpx_codec_dec_init(&codec, VPXD_INTERFACE(DECODER), &cfg, 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data += IVF_FILE_HDR_SZ;
|
||||
size -= IVF_FILE_HDR_SZ;
|
||||
|
||||
while (size > IVF_FRAME_HDR_SZ) {
|
||||
size_t frame_size = mem_get_le32(data);
|
||||
size -= IVF_FRAME_HDR_SZ;
|
||||
data += IVF_FRAME_HDR_SZ;
|
||||
frame_size = std::min(size, frame_size);
|
||||
|
||||
const vpx_codec_err_t err =
|
||||
vpx_codec_decode(&codec, data, frame_size, nullptr, 0);
|
||||
static_cast<void>(err);
|
||||
vpx_codec_iter_t iter = nullptr;
|
||||
vpx_image_t *img = nullptr;
|
||||
while ((img = vpx_codec_get_frame(&codec, &iter)) != nullptr) {
|
||||
}
|
||||
data += frame_size;
|
||||
size -= frame_size;
|
||||
}
|
||||
vpx_codec_destroy(&codec);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,982 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This is an example demonstrating how to implement a multi-layer VPx
|
||||
// encoding scheme based on temporal scalability for video applications
|
||||
// that benefit from a scalable bitstream.
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "./y4minput.h"
|
||||
#include "../vpx_ports/vpx_timer.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx_ports/bitops.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
#define ROI_MAP 0
|
||||
|
||||
#define zero(Dest) memset(&(Dest), 0, sizeof(Dest));
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) { exit(EXIT_FAILURE); }
|
||||
|
||||
// Denoiser states for vp8, for temporal denoising.
|
||||
enum denoiserStateVp8 {
|
||||
kVp8DenoiserOff,
|
||||
kVp8DenoiserOnYOnly,
|
||||
kVp8DenoiserOnYUV,
|
||||
kVp8DenoiserOnYUVAggressive,
|
||||
kVp8DenoiserOnAdaptive
|
||||
};
|
||||
|
||||
// Denoiser states for vp9, for temporal denoising.
|
||||
enum denoiserStateVp9 {
|
||||
kVp9DenoiserOff,
|
||||
kVp9DenoiserOnYOnly,
|
||||
// For SVC: denoise the top two spatial layers.
|
||||
kVp9DenoiserOnYTwoSpatialLayers
|
||||
};
|
||||
|
||||
static int mode_to_num_layers[13] = { 1, 2, 2, 3, 3, 3, 3, 5, 2, 3, 3, 3, 3 };
|
||||
|
||||
// For rate control encoding stats.
|
||||
struct RateControlMetrics {
|
||||
// Number of input frames per layer.
|
||||
int layer_input_frames[VPX_TS_MAX_LAYERS];
|
||||
// Total (cumulative) number of encoded frames per layer.
|
||||
int layer_tot_enc_frames[VPX_TS_MAX_LAYERS];
|
||||
// Number of encoded non-key frames per layer.
|
||||
int layer_enc_frames[VPX_TS_MAX_LAYERS];
|
||||
// Framerate per layer layer (cumulative).
|
||||
double layer_framerate[VPX_TS_MAX_LAYERS];
|
||||
// Target average frame size per layer (per-frame-bandwidth per layer).
|
||||
double layer_pfb[VPX_TS_MAX_LAYERS];
|
||||
// Actual average frame size per layer.
|
||||
double layer_avg_frame_size[VPX_TS_MAX_LAYERS];
|
||||
// Average rate mismatch per layer (|target - actual| / target).
|
||||
double layer_avg_rate_mismatch[VPX_TS_MAX_LAYERS];
|
||||
// Actual encoding bitrate per layer (cumulative).
|
||||
double layer_encoding_bitrate[VPX_TS_MAX_LAYERS];
|
||||
// Average of the short-time encoder actual bitrate.
|
||||
// TODO(marpan): Should we add these short-time stats for each layer?
|
||||
double avg_st_encoding_bitrate;
|
||||
// Variance of the short-time encoder actual bitrate.
|
||||
double variance_st_encoding_bitrate;
|
||||
// Window (number of frames) for computing short-timee encoding bitrate.
|
||||
int window_size;
|
||||
// Number of window measurements.
|
||||
int window_count;
|
||||
int layer_target_bitrate[VPX_MAX_LAYERS];
|
||||
};
|
||||
|
||||
// Note: these rate control metrics assume only 1 key frame in the
|
||||
// sequence (i.e., first frame only). So for temporal pattern# 7
|
||||
// (which has key frame for every frame on base layer), the metrics
|
||||
// computation will be off/wrong.
|
||||
// TODO(marpan): Update these metrics to account for multiple key frames
|
||||
// in the stream.
|
||||
static void set_rate_control_metrics(struct RateControlMetrics *rc,
|
||||
vpx_codec_enc_cfg_t *cfg) {
|
||||
int i = 0;
|
||||
// Set the layer (cumulative) framerate and the target layer (non-cumulative)
|
||||
// per-frame-bandwidth, for the rate control encoding stats below.
|
||||
const double framerate = cfg->g_timebase.den / cfg->g_timebase.num;
|
||||
const int ts_number_layers = cfg->ts_number_layers;
|
||||
rc->layer_framerate[0] = framerate / cfg->ts_rate_decimator[0];
|
||||
rc->layer_pfb[0] =
|
||||
1000.0 * rc->layer_target_bitrate[0] / rc->layer_framerate[0];
|
||||
for (i = 0; i < ts_number_layers; ++i) {
|
||||
if (i > 0) {
|
||||
rc->layer_framerate[i] = framerate / cfg->ts_rate_decimator[i];
|
||||
rc->layer_pfb[i] =
|
||||
1000.0 *
|
||||
(rc->layer_target_bitrate[i] - rc->layer_target_bitrate[i - 1]) /
|
||||
(rc->layer_framerate[i] - rc->layer_framerate[i - 1]);
|
||||
}
|
||||
rc->layer_input_frames[i] = 0;
|
||||
rc->layer_enc_frames[i] = 0;
|
||||
rc->layer_tot_enc_frames[i] = 0;
|
||||
rc->layer_encoding_bitrate[i] = 0.0;
|
||||
rc->layer_avg_frame_size[i] = 0.0;
|
||||
rc->layer_avg_rate_mismatch[i] = 0.0;
|
||||
}
|
||||
rc->window_count = 0;
|
||||
rc->window_size = 15;
|
||||
rc->avg_st_encoding_bitrate = 0.0;
|
||||
rc->variance_st_encoding_bitrate = 0.0;
|
||||
// Target bandwidth for the whole stream.
|
||||
// Set to layer_target_bitrate for highest layer (total bitrate).
|
||||
cfg->rc_target_bitrate = rc->layer_target_bitrate[ts_number_layers - 1];
|
||||
}
|
||||
|
||||
static void printout_rate_control_summary(struct RateControlMetrics *rc,
|
||||
vpx_codec_enc_cfg_t *cfg,
|
||||
int frame_cnt) {
|
||||
unsigned int i = 0;
|
||||
int tot_num_frames = 0;
|
||||
double perc_fluctuation = 0.0;
|
||||
printf("Total number of processed frames: %d\n\n", frame_cnt - 1);
|
||||
printf("Rate control layer stats for %d layer(s):\n\n",
|
||||
cfg->ts_number_layers);
|
||||
for (i = 0; i < cfg->ts_number_layers; ++i) {
|
||||
const int num_dropped =
|
||||
(i > 0) ? (rc->layer_input_frames[i] - rc->layer_enc_frames[i])
|
||||
: (rc->layer_input_frames[i] - rc->layer_enc_frames[i] - 1);
|
||||
tot_num_frames += rc->layer_input_frames[i];
|
||||
rc->layer_encoding_bitrate[i] = 0.001 * rc->layer_framerate[i] *
|
||||
rc->layer_encoding_bitrate[i] /
|
||||
tot_num_frames;
|
||||
rc->layer_avg_frame_size[i] =
|
||||
rc->layer_avg_frame_size[i] / rc->layer_enc_frames[i];
|
||||
rc->layer_avg_rate_mismatch[i] =
|
||||
100.0 * rc->layer_avg_rate_mismatch[i] / rc->layer_enc_frames[i];
|
||||
printf("For layer#: %d \n", i);
|
||||
printf("Bitrate (target vs actual): %d %f \n", rc->layer_target_bitrate[i],
|
||||
rc->layer_encoding_bitrate[i]);
|
||||
printf("Average frame size (target vs actual): %f %f \n", rc->layer_pfb[i],
|
||||
rc->layer_avg_frame_size[i]);
|
||||
printf("Average rate_mismatch: %f \n", rc->layer_avg_rate_mismatch[i]);
|
||||
printf(
|
||||
"Number of input frames, encoded (non-key) frames, "
|
||||
"and perc dropped frames: %d %d %f \n",
|
||||
rc->layer_input_frames[i], rc->layer_enc_frames[i],
|
||||
100.0 * num_dropped / rc->layer_input_frames[i]);
|
||||
printf("\n");
|
||||
}
|
||||
rc->avg_st_encoding_bitrate = rc->avg_st_encoding_bitrate / rc->window_count;
|
||||
rc->variance_st_encoding_bitrate =
|
||||
rc->variance_st_encoding_bitrate / rc->window_count -
|
||||
(rc->avg_st_encoding_bitrate * rc->avg_st_encoding_bitrate);
|
||||
perc_fluctuation = 100.0 * sqrt(rc->variance_st_encoding_bitrate) /
|
||||
rc->avg_st_encoding_bitrate;
|
||||
printf("Short-time stats, for window of %d frames: \n", rc->window_size);
|
||||
printf("Average, rms-variance, and percent-fluct: %f %f %f \n",
|
||||
rc->avg_st_encoding_bitrate, sqrt(rc->variance_st_encoding_bitrate),
|
||||
perc_fluctuation);
|
||||
if ((frame_cnt - 1) != tot_num_frames)
|
||||
die("Error: Number of input frames not equal to output! \n");
|
||||
}
|
||||
|
||||
#if ROI_MAP
|
||||
static void set_roi_map(const char *enc_name, vpx_codec_enc_cfg_t *cfg,
|
||||
vpx_roi_map_t *roi) {
|
||||
unsigned int i, j;
|
||||
int block_size = 0;
|
||||
uint8_t is_vp8 = strncmp(enc_name, "vp8", 3) == 0 ? 1 : 0;
|
||||
uint8_t is_vp9 = strncmp(enc_name, "vp9", 3) == 0 ? 1 : 0;
|
||||
if (!is_vp8 && !is_vp9) {
|
||||
die("unsupported codec.");
|
||||
}
|
||||
zero(*roi);
|
||||
|
||||
block_size = is_vp9 && !is_vp8 ? 8 : 16;
|
||||
|
||||
// ROI is based on the segments (4 for vp8, 8 for vp9), smallest unit for
|
||||
// segment is 16x16 for vp8, 8x8 for vp9.
|
||||
roi->rows = (cfg->g_h + block_size - 1) / block_size;
|
||||
roi->cols = (cfg->g_w + block_size - 1) / block_size;
|
||||
|
||||
// Applies delta QP on the segment blocks, varies from -63 to 63.
|
||||
// Setting to negative means lower QP (better quality).
|
||||
// Below we set delta_q to the extreme (-63) to show strong effect.
|
||||
// VP8 uses the first 4 segments. VP9 uses all 8 segments.
|
||||
zero(roi->delta_q);
|
||||
roi->delta_q[1] = -63;
|
||||
|
||||
// Applies delta loopfilter strength on the segment blocks, varies from -63 to
|
||||
// 63. Setting to positive means stronger loopfilter. VP8 uses the first 4
|
||||
// segments. VP9 uses all 8 segments.
|
||||
zero(roi->delta_lf);
|
||||
|
||||
if (is_vp8) {
|
||||
// Applies skip encoding threshold on the segment blocks, varies from 0 to
|
||||
// UINT_MAX. Larger value means more skipping of encoding is possible.
|
||||
// This skip threshold only applies on delta frames.
|
||||
zero(roi->static_threshold);
|
||||
}
|
||||
|
||||
if (is_vp9) {
|
||||
// Apply skip segment. Setting to 1 means this block will be copied from
|
||||
// previous frame.
|
||||
zero(roi->skip);
|
||||
}
|
||||
|
||||
if (is_vp9) {
|
||||
// Apply ref frame segment.
|
||||
// -1 : Do not apply this segment.
|
||||
// 0 : Froce using intra.
|
||||
// 1 : Force using last.
|
||||
// 2 : Force using golden.
|
||||
// 3 : Force using alfref but not used in non-rd pickmode for 0 lag.
|
||||
memset(roi->ref_frame, -1, sizeof(roi->ref_frame));
|
||||
roi->ref_frame[1] = 1;
|
||||
}
|
||||
|
||||
// Use 2 states: 1 is center square, 0 is the rest.
|
||||
roi->roi_map =
|
||||
(uint8_t *)calloc(roi->rows * roi->cols, sizeof(*roi->roi_map));
|
||||
for (i = 0; i < roi->rows; ++i) {
|
||||
for (j = 0; j < roi->cols; ++j) {
|
||||
if (i > (roi->rows >> 2) && i < ((roi->rows * 3) >> 2) &&
|
||||
j > (roi->cols >> 2) && j < ((roi->cols * 3) >> 2)) {
|
||||
roi->roi_map[i * roi->cols + j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Temporal scaling parameters:
|
||||
// NOTE: The 3 prediction frames cannot be used interchangeably due to
|
||||
// differences in the way they are handled throughout the code. The
|
||||
// frames should be allocated to layers in the order LAST, GF, ARF.
|
||||
// Other combinations work, but may produce slightly inferior results.
|
||||
static void set_temporal_layer_pattern(int layering_mode,
|
||||
vpx_codec_enc_cfg_t *cfg,
|
||||
int *layer_flags,
|
||||
int *flag_periodicity) {
|
||||
switch (layering_mode) {
|
||||
case 0: {
|
||||
// 1-layer.
|
||||
int ids[1] = { 0 };
|
||||
cfg->ts_periodicity = 1;
|
||||
*flag_periodicity = 1;
|
||||
cfg->ts_number_layers = 1;
|
||||
cfg->ts_rate_decimator[0] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// Update L only.
|
||||
layer_flags[0] =
|
||||
VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// 2-layers, 2-frame period.
|
||||
int ids[2] = { 0, 1 };
|
||||
cfg->ts_periodicity = 2;
|
||||
*flag_periodicity = 2;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 2;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
#if 1
|
||||
// 0=L, 1=GF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF;
|
||||
layer_flags[1] =
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_REF_ARF;
|
||||
#else
|
||||
// 0=L, 1=GF, Intra-layer prediction disabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF;
|
||||
layer_flags[1] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_LAST;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// 2-layers, 3-frame period.
|
||||
int ids[3] = { 0, 1, 1 };
|
||||
cfg->ts_periodicity = 3;
|
||||
*flag_periodicity = 3;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 3;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// 3-layers, 6-frame period.
|
||||
int ids[6] = { 0, 2, 2, 1, 2, 2 };
|
||||
cfg->ts_periodicity = 6;
|
||||
*flag_periodicity = 6;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 6;
|
||||
cfg->ts_rate_decimator[1] = 3;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[3] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[1] = layer_flags[2] = layer_flags[4] = layer_flags[5] =
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction disabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled in layer 1, disabled
|
||||
// in layer 2.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
// NOTE: Probably of academic interest only.
|
||||
// 5-layers, 16-frame period.
|
||||
int ids[16] = { 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4 };
|
||||
cfg->ts_periodicity = 16;
|
||||
*flag_periodicity = 16;
|
||||
cfg->ts_number_layers = 5;
|
||||
cfg->ts_rate_decimator[0] = 16;
|
||||
cfg->ts_rate_decimator[1] = 8;
|
||||
cfg->ts_rate_decimator[2] = 4;
|
||||
cfg->ts_rate_decimator[3] = 2;
|
||||
cfg->ts_rate_decimator[4] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF;
|
||||
layer_flags[1] = layer_flags[3] = layer_flags[5] = layer_flags[7] =
|
||||
layer_flags[9] = layer_flags[11] = layer_flags[13] = layer_flags[15] =
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] = layer_flags[6] = layer_flags[10] = layer_flags[14] =
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF;
|
||||
layer_flags[4] = layer_flags[12] =
|
||||
VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[8] = VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_GF;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
// 2-layers, with sync point at first frame of layer 1.
|
||||
int ids[2] = { 0, 1 };
|
||||
cfg->ts_periodicity = 2;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 2;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF.
|
||||
// ARF is used as predictor for all frames, and is only updated on
|
||||
// key frame. Sync point every 8 frames.
|
||||
|
||||
// Layer 0: predict from L and ARF, update L and G.
|
||||
layer_flags[0] =
|
||||
VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
// Layer 1: sync point: predict from L and ARF, and update G.
|
||||
layer_flags[1] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
// Layer 0, predict from L and ARF, update L.
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
// Layer 1: predict from L, G and ARF, and update G.
|
||||
layer_flags[3] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
|
||||
VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 0.
|
||||
layer_flags[4] = layer_flags[2];
|
||||
// Layer 1.
|
||||
layer_flags[5] = layer_flags[3];
|
||||
// Layer 0.
|
||||
layer_flags[6] = layer_flags[4];
|
||||
// Layer 1.
|
||||
layer_flags[7] = layer_flags[5];
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
// 3-layers: Sync points for layer 1 and 2 every 8 frames.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
layer_flags[0] = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_REF_GF |
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
|
||||
layer_flags[2] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[3] = layer_flags[5] =
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
|
||||
layer_flags[4] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[6] =
|
||||
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[7] = VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
// 3-layers structure where ARF is used as predictor for all frames,
|
||||
// and is only updated on key frame.
|
||||
// Sync points for layer 1 and 2 every 8 frames.
|
||||
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
// Layer 0: predict from L and ARF; update L and G.
|
||||
layer_flags[0] =
|
||||
VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF;
|
||||
// Layer 2: sync point: predict from L and ARF; update none.
|
||||
layer_flags[1] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
|
||||
VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 1: sync point: predict from L and ARF; update G.
|
||||
layer_flags[2] =
|
||||
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[3] = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 0: predict from L and ARF; update L.
|
||||
layer_flags[4] =
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[5] = layer_flags[3];
|
||||
// Layer 1: predict from L, G, ARF; update G.
|
||||
layer_flags[6] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[7] = layer_flags[3];
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
// 3-layers structure with one reference frame.
|
||||
// This works same as temporal_layering_mode 3.
|
||||
// This was added to compare with vp9_spatial_svc_encoder.
|
||||
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction disabled.
|
||||
layer_flags[0] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[1] = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
|
||||
layer_flags[3] = VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF;
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
default: {
|
||||
// 3-layers structure as in case 10, but no sync/refresh points for
|
||||
// layer 1 and 2.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
// Layer 0: predict from L and ARF; update L.
|
||||
layer_flags[0] =
|
||||
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF;
|
||||
layer_flags[4] = layer_flags[0];
|
||||
// Layer 1: predict from L, G, ARF; update G.
|
||||
layer_flags[2] = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[6] = layer_flags[2];
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[1] = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
|
||||
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY;
|
||||
layer_flags[3] = layer_flags[1];
|
||||
layer_flags[5] = layer_flags[1];
|
||||
layer_flags[7] = layer_flags[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
VpxVideoWriter *outfile[VPX_TS_MAX_LAYERS] = { NULL };
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
int frame_cnt = 0;
|
||||
vpx_image_t raw;
|
||||
vpx_codec_err_t res;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
uint32_t error_resilient = 0;
|
||||
int speed;
|
||||
int frame_avail;
|
||||
int got_data;
|
||||
int flags = 0;
|
||||
unsigned int i;
|
||||
int pts = 0; // PTS starts at 0.
|
||||
int frame_duration = 1; // 1 timebase tick per frame.
|
||||
int layering_mode = 0;
|
||||
int layer_flags[VPX_TS_MAX_PERIODICITY] = { 0 };
|
||||
int flag_periodicity = 1;
|
||||
#if ROI_MAP
|
||||
vpx_roi_map_t roi;
|
||||
#endif
|
||||
vpx_svc_layer_id_t layer_id;
|
||||
const VpxInterface *encoder = NULL;
|
||||
struct VpxInputContext input_ctx;
|
||||
struct RateControlMetrics rc;
|
||||
int64_t cx_time = 0;
|
||||
const int min_args_base = 13;
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
vpx_bit_depth_t bit_depth = VPX_BITS_8;
|
||||
int input_bit_depth = 8;
|
||||
const int min_args = min_args_base + 1;
|
||||
#else
|
||||
const int min_args = min_args_base;
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
double sum_bitrate = 0.0;
|
||||
double sum_bitrate2 = 0.0;
|
||||
double framerate = 30.0;
|
||||
|
||||
zero(rc.layer_target_bitrate);
|
||||
memset(&layer_id, 0, sizeof(vpx_svc_layer_id_t));
|
||||
memset(&input_ctx, 0, sizeof(input_ctx));
|
||||
/* Setup default input stream settings */
|
||||
input_ctx.framerate.numerator = 30;
|
||||
input_ctx.framerate.denominator = 1;
|
||||
input_ctx.only_i420 = 1;
|
||||
input_ctx.bit_depth = 0;
|
||||
|
||||
exec_name = argv[0];
|
||||
// Check usage and arguments.
|
||||
if (argc < min_args) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
die("Usage: %s <infile> <outfile> <codec_type(vp8/vp9)> <width> <height> "
|
||||
"<rate_num> <rate_den> <speed> <frame_drop_threshold> "
|
||||
"<error_resilient> <threads> <mode> "
|
||||
"<Rate_0> ... <Rate_nlayers-1> <bit-depth> \n",
|
||||
argv[0]);
|
||||
#else
|
||||
die("Usage: %s <infile> <outfile> <codec_type(vp8/vp9)> <width> <height> "
|
||||
"<rate_num> <rate_den> <speed> <frame_drop_threshold> "
|
||||
"<error_resilient> <threads> <mode> "
|
||||
"<Rate_0> ... <Rate_nlayers-1> \n",
|
||||
argv[0]);
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
}
|
||||
|
||||
encoder = get_vpx_encoder_by_name(argv[3]);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
printf("Using %s\n", vpx_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
width = (unsigned int)strtoul(argv[4], NULL, 0);
|
||||
height = (unsigned int)strtoul(argv[5], NULL, 0);
|
||||
if (width < 16 || width % 2 || height < 16 || height % 2) {
|
||||
die("Invalid resolution: %d x %d", width, height);
|
||||
}
|
||||
|
||||
layering_mode = (int)strtol(argv[12], NULL, 0);
|
||||
if (layering_mode < 0 || layering_mode > 13) {
|
||||
die("Invalid layering mode (0..12) %s", argv[12]);
|
||||
}
|
||||
|
||||
if (argc != min_args + mode_to_num_layers[layering_mode]) {
|
||||
die("Invalid number of arguments");
|
||||
}
|
||||
|
||||
input_ctx.filename = argv[1];
|
||||
open_input_file(&input_ctx);
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
switch (strtol(argv[argc - 1], NULL, 0)) {
|
||||
case 8:
|
||||
bit_depth = VPX_BITS_8;
|
||||
input_bit_depth = 8;
|
||||
break;
|
||||
case 10:
|
||||
bit_depth = VPX_BITS_10;
|
||||
input_bit_depth = 10;
|
||||
break;
|
||||
case 12:
|
||||
bit_depth = VPX_BITS_12;
|
||||
input_bit_depth = 12;
|
||||
break;
|
||||
default: die("Invalid bit depth (8, 10, 12) %s", argv[argc - 1]);
|
||||
}
|
||||
|
||||
// Y4M reader has its own allocation.
|
||||
if (input_ctx.file_type != FILE_TYPE_Y4M) {
|
||||
if (!vpx_img_alloc(
|
||||
&raw,
|
||||
bit_depth == VPX_BITS_8 ? VPX_IMG_FMT_I420 : VPX_IMG_FMT_I42016,
|
||||
width, height, 32)) {
|
||||
die("Failed to allocate image", width, height);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Y4M reader has its own allocation.
|
||||
if (input_ctx.file_type != FILE_TYPE_Y4M) {
|
||||
if (!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 32)) {
|
||||
die("Failed to allocate image", width, height);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
// Populate encoder configuration.
|
||||
res = vpx_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) {
|
||||
printf("Failed to get config: %s\n", vpx_codec_err_to_string(res));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Update the default configuration with our settings.
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
if (bit_depth != VPX_BITS_8) {
|
||||
cfg.g_bit_depth = bit_depth;
|
||||
cfg.g_input_bit_depth = input_bit_depth;
|
||||
cfg.g_profile = 2;
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
// Timebase format e.g. 30fps: numerator=1, demoninator = 30.
|
||||
cfg.g_timebase.num = (int)strtol(argv[6], NULL, 0);
|
||||
cfg.g_timebase.den = (int)strtol(argv[7], NULL, 0);
|
||||
|
||||
speed = (int)strtol(argv[8], NULL, 0);
|
||||
if (speed < 0) {
|
||||
die("Invalid speed setting: must be positive");
|
||||
}
|
||||
if (strncmp(encoder->name, "vp9", 3) == 0 && speed > 9) {
|
||||
warn("Mapping speed %d to speed 9.\n", speed);
|
||||
}
|
||||
|
||||
for (i = min_args_base;
|
||||
(int)i < min_args_base + mode_to_num_layers[layering_mode]; ++i) {
|
||||
rc.layer_target_bitrate[i - 13] = (int)strtol(argv[i], NULL, 0);
|
||||
if (strncmp(encoder->name, "vp8", 3) == 0)
|
||||
cfg.ts_target_bitrate[i - 13] = rc.layer_target_bitrate[i - 13];
|
||||
else if (strncmp(encoder->name, "vp9", 3) == 0)
|
||||
cfg.layer_target_bitrate[i - 13] = rc.layer_target_bitrate[i - 13];
|
||||
}
|
||||
|
||||
// Real time parameters.
|
||||
cfg.rc_dropframe_thresh = (unsigned int)strtoul(argv[9], NULL, 0);
|
||||
cfg.rc_end_usage = VPX_CBR;
|
||||
cfg.rc_min_quantizer = 2;
|
||||
cfg.rc_max_quantizer = 56;
|
||||
if (strncmp(encoder->name, "vp9", 3) == 0) cfg.rc_max_quantizer = 52;
|
||||
cfg.rc_undershoot_pct = 50;
|
||||
cfg.rc_overshoot_pct = 50;
|
||||
cfg.rc_buf_initial_sz = 600;
|
||||
cfg.rc_buf_optimal_sz = 600;
|
||||
cfg.rc_buf_sz = 1000;
|
||||
|
||||
// Disable dynamic resizing by default.
|
||||
cfg.rc_resize_allowed = 0;
|
||||
|
||||
// Use 1 thread as default.
|
||||
cfg.g_threads = (unsigned int)strtoul(argv[11], NULL, 0);
|
||||
|
||||
error_resilient = (uint32_t)strtoul(argv[10], NULL, 0);
|
||||
if (error_resilient != 0 && error_resilient != 1) {
|
||||
die("Invalid value for error resilient (0, 1): %d.", error_resilient);
|
||||
}
|
||||
// Enable error resilient mode.
|
||||
cfg.g_error_resilient = error_resilient;
|
||||
cfg.g_lag_in_frames = 0;
|
||||
cfg.kf_mode = VPX_KF_AUTO;
|
||||
|
||||
// Disable automatic keyframe placement.
|
||||
cfg.kf_min_dist = cfg.kf_max_dist = 3000;
|
||||
|
||||
cfg.temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS;
|
||||
|
||||
set_temporal_layer_pattern(layering_mode, &cfg, layer_flags,
|
||||
&flag_periodicity);
|
||||
|
||||
set_rate_control_metrics(&rc, &cfg);
|
||||
|
||||
if (input_ctx.file_type == FILE_TYPE_Y4M) {
|
||||
if (input_ctx.width != cfg.g_w || input_ctx.height != cfg.g_h) {
|
||||
die("Incorrect width or height: %d x %d", cfg.g_w, cfg.g_h);
|
||||
}
|
||||
if (input_ctx.framerate.numerator != cfg.g_timebase.den ||
|
||||
input_ctx.framerate.denominator != cfg.g_timebase.num) {
|
||||
die("Incorrect framerate: numerator %d denominator %d",
|
||||
cfg.g_timebase.num, cfg.g_timebase.den);
|
||||
}
|
||||
}
|
||||
|
||||
framerate = cfg.g_timebase.den / cfg.g_timebase.num;
|
||||
// Open an output file for each stream.
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) {
|
||||
char file_name[PATH_MAX];
|
||||
VpxVideoInfo info;
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = cfg.g_w;
|
||||
info.frame_height = cfg.g_h;
|
||||
info.time_base.numerator = cfg.g_timebase.num;
|
||||
info.time_base.denominator = cfg.g_timebase.den;
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "%s_%d.ivf", argv[2], i);
|
||||
outfile[i] = vpx_video_writer_open(file_name, kContainerIVF, &info);
|
||||
if (!outfile[i]) die("Failed to open %s for writing", file_name);
|
||||
|
||||
assert(outfile[i] != NULL);
|
||||
}
|
||||
// No spatial layers in this encoder.
|
||||
cfg.ss_number_layers = 1;
|
||||
|
||||
// Initialize codec.
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
if (vpx_codec_enc_init(
|
||||
&codec, encoder->codec_interface(), &cfg,
|
||||
bit_depth == VPX_BITS_8 ? 0 : VPX_CODEC_USE_HIGHBITDEPTH))
|
||||
#else
|
||||
if (vpx_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
if (strncmp(encoder->name, "vp8", 3) == 0) {
|
||||
vpx_codec_control(&codec, VP8E_SET_CPUUSED, -speed);
|
||||
vpx_codec_control(&codec, VP8E_SET_NOISE_SENSITIVITY, kVp8DenoiserOff);
|
||||
vpx_codec_control(&codec, VP8E_SET_STATIC_THRESHOLD, 1);
|
||||
vpx_codec_control(&codec, VP8E_SET_GF_CBR_BOOST_PCT, 0);
|
||||
#if ROI_MAP
|
||||
set_roi_map(encoder->name, &cfg, &roi);
|
||||
if (vpx_codec_control(&codec, VP8E_SET_ROI_MAP, &roi))
|
||||
die_codec(&codec, "Failed to set ROI map");
|
||||
#endif
|
||||
|
||||
} else if (strncmp(encoder->name, "vp9", 3) == 0) {
|
||||
vpx_svc_extra_cfg_t svc_params;
|
||||
memset(&svc_params, 0, sizeof(svc_params));
|
||||
vpx_codec_control(&codec, VP8E_SET_CPUUSED, speed);
|
||||
vpx_codec_control(&codec, VP9E_SET_AQ_MODE, 3);
|
||||
vpx_codec_control(&codec, VP9E_SET_GF_CBR_BOOST_PCT, 0);
|
||||
vpx_codec_control(&codec, VP9E_SET_FRAME_PARALLEL_DECODING, 0);
|
||||
vpx_codec_control(&codec, VP9E_SET_FRAME_PERIODIC_BOOST, 0);
|
||||
vpx_codec_control(&codec, VP9E_SET_NOISE_SENSITIVITY, kVp9DenoiserOff);
|
||||
vpx_codec_control(&codec, VP8E_SET_STATIC_THRESHOLD, 1);
|
||||
vpx_codec_control(&codec, VP9E_SET_TUNE_CONTENT, 0);
|
||||
vpx_codec_control(&codec, VP9E_SET_TILE_COLUMNS, get_msb(cfg.g_threads));
|
||||
#if ROI_MAP
|
||||
set_roi_map(encoder->name, &cfg, &roi);
|
||||
if (vpx_codec_control(&codec, VP9E_SET_ROI_MAP, &roi))
|
||||
die_codec(&codec, "Failed to set ROI map");
|
||||
vpx_codec_control(&codec, VP9E_SET_AQ_MODE, 0);
|
||||
#endif
|
||||
if (cfg.g_threads > 1)
|
||||
vpx_codec_control(&codec, VP9E_SET_ROW_MT, 1);
|
||||
else
|
||||
vpx_codec_control(&codec, VP9E_SET_ROW_MT, 0);
|
||||
if (vpx_codec_control(&codec, VP9E_SET_SVC, layering_mode > 0 ? 1 : 0))
|
||||
die_codec(&codec, "Failed to set SVC");
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) {
|
||||
svc_params.max_quantizers[i] = cfg.rc_max_quantizer;
|
||||
svc_params.min_quantizers[i] = cfg.rc_min_quantizer;
|
||||
}
|
||||
svc_params.scaling_factor_num[0] = cfg.g_h;
|
||||
svc_params.scaling_factor_den[0] = cfg.g_h;
|
||||
vpx_codec_control(&codec, VP9E_SET_SVC_PARAMETERS, &svc_params);
|
||||
}
|
||||
if (strncmp(encoder->name, "vp8", 3) == 0) {
|
||||
vpx_codec_control(&codec, VP8E_SET_SCREEN_CONTENT_MODE, 0);
|
||||
}
|
||||
vpx_codec_control(&codec, VP8E_SET_TOKEN_PARTITIONS, 1);
|
||||
// This controls the maximum target size of the key frame.
|
||||
// For generating smaller key frames, use a smaller max_intra_size_pct
|
||||
// value, like 100 or 200.
|
||||
{
|
||||
const int max_intra_size_pct = 1000;
|
||||
vpx_codec_control(&codec, VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
||||
max_intra_size_pct);
|
||||
}
|
||||
|
||||
frame_avail = 1;
|
||||
while (frame_avail || got_data) {
|
||||
struct vpx_usec_timer timer;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt;
|
||||
// Update the temporal layer_id. No spatial layers in this test.
|
||||
layer_id.spatial_layer_id = 0;
|
||||
layer_id.temporal_layer_id =
|
||||
cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity];
|
||||
layer_id.temporal_layer_id_per_spatial[0] = layer_id.temporal_layer_id;
|
||||
if (strncmp(encoder->name, "vp9", 3) == 0) {
|
||||
vpx_codec_control(&codec, VP9E_SET_SVC_LAYER_ID, &layer_id);
|
||||
} else if (strncmp(encoder->name, "vp8", 3) == 0) {
|
||||
vpx_codec_control(&codec, VP8E_SET_TEMPORAL_LAYER_ID,
|
||||
layer_id.temporal_layer_id);
|
||||
}
|
||||
flags = layer_flags[frame_cnt % flag_periodicity];
|
||||
if (layering_mode == 0) flags = 0;
|
||||
frame_avail = read_frame(&input_ctx, &raw);
|
||||
if (frame_avail) ++rc.layer_input_frames[layer_id.temporal_layer_id];
|
||||
vpx_usec_timer_start(&timer);
|
||||
if (vpx_codec_encode(&codec, frame_avail ? &raw : NULL, pts, 1, flags,
|
||||
VPX_DL_REALTIME)) {
|
||||
die_codec(&codec, "Failed to encode frame");
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
cx_time += vpx_usec_timer_elapsed(&timer);
|
||||
// Reset KF flag.
|
||||
if (layering_mode != 7) {
|
||||
layer_flags[0] &= ~VPX_EFLAG_FORCE_KF;
|
||||
}
|
||||
got_data = 0;
|
||||
while ((pkt = vpx_codec_get_cx_data(&codec, &iter))) {
|
||||
got_data = 1;
|
||||
switch (pkt->kind) {
|
||||
case VPX_CODEC_CX_FRAME_PKT:
|
||||
for (i = cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity];
|
||||
i < cfg.ts_number_layers; ++i) {
|
||||
vpx_video_writer_write_frame(outfile[i], pkt->data.frame.buf,
|
||||
pkt->data.frame.sz, pts);
|
||||
++rc.layer_tot_enc_frames[i];
|
||||
rc.layer_encoding_bitrate[i] += 8.0 * pkt->data.frame.sz;
|
||||
// Keep count of rate control stats per layer (for non-key frames).
|
||||
if (i == cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity] &&
|
||||
!(pkt->data.frame.flags & VPX_FRAME_IS_KEY)) {
|
||||
rc.layer_avg_frame_size[i] += 8.0 * pkt->data.frame.sz;
|
||||
rc.layer_avg_rate_mismatch[i] +=
|
||||
fabs(8.0 * pkt->data.frame.sz - rc.layer_pfb[i]) /
|
||||
rc.layer_pfb[i];
|
||||
++rc.layer_enc_frames[i];
|
||||
}
|
||||
}
|
||||
// Update for short-time encoding bitrate states, for moving window
|
||||
// of size rc->window, shifted by rc->window / 2.
|
||||
// Ignore first window segment, due to key frame.
|
||||
if (frame_cnt > rc.window_size) {
|
||||
sum_bitrate += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
|
||||
if (frame_cnt % rc.window_size == 0) {
|
||||
rc.window_count += 1;
|
||||
rc.avg_st_encoding_bitrate += sum_bitrate / rc.window_size;
|
||||
rc.variance_st_encoding_bitrate +=
|
||||
(sum_bitrate / rc.window_size) *
|
||||
(sum_bitrate / rc.window_size);
|
||||
sum_bitrate = 0.0;
|
||||
}
|
||||
}
|
||||
// Second shifted window.
|
||||
if (frame_cnt > rc.window_size + rc.window_size / 2) {
|
||||
sum_bitrate2 += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
|
||||
if (frame_cnt > 2 * rc.window_size &&
|
||||
frame_cnt % rc.window_size == 0) {
|
||||
rc.window_count += 1;
|
||||
rc.avg_st_encoding_bitrate += sum_bitrate2 / rc.window_size;
|
||||
rc.variance_st_encoding_bitrate +=
|
||||
(sum_bitrate2 / rc.window_size) *
|
||||
(sum_bitrate2 / rc.window_size);
|
||||
sum_bitrate2 = 0.0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
++frame_cnt;
|
||||
pts += frame_duration;
|
||||
}
|
||||
close_input_file(&input_ctx);
|
||||
printout_rate_control_summary(&rc, &cfg, frame_cnt);
|
||||
printf("\n");
|
||||
printf("Frame cnt and encoding time/FPS stats for encoding: %d %f %f \n",
|
||||
frame_cnt, 1000 * (float)cx_time / (double)(frame_cnt * 1000000),
|
||||
1000000 * (double)frame_cnt / (double)cx_time);
|
||||
|
||||
if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
|
||||
// Try to rewrite the output file headers with the actual frame count.
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) vpx_video_writer_close(outfile[i]);
|
||||
|
||||
if (input_ctx.file_type != FILE_TYPE_Y4M) {
|
||||
vpx_img_free(&raw);
|
||||
}
|
||||
|
||||
#if ROI_MAP
|
||||
free(roi.roi_map);
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx_ports/mem_ops.h"
|
||||
|
||||
#include "./ivfdec.h"
|
||||
|
||||
static const char *IVF_SIGNATURE = "DKIF";
|
||||
|
||||
static void fix_framerate(int *num, int *den) {
|
||||
// Some versions of vpxenc used 1/(2*fps) for the timebase, so
|
||||
// we can guess the framerate using only the timebase in this
|
||||
// case. Other files would require reading ahead to guess the
|
||||
// timebase, like we do for webm.
|
||||
if (*den > 0 && *den < 1000000000 && *num > 0 && *num < 1000) {
|
||||
// Correct for the factor of 2 applied to the timebase in the encoder.
|
||||
if (*num & 1)
|
||||
*den *= 2;
|
||||
else
|
||||
*num /= 2;
|
||||
} else {
|
||||
// Don't know FPS for sure, and don't have readahead code
|
||||
// (yet?), so just default to 30fps.
|
||||
*num = 30;
|
||||
*den = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int file_is_ivf(struct VpxInputContext *input_ctx) {
|
||||
char raw_hdr[32];
|
||||
int is_ivf = 0;
|
||||
|
||||
if (fread(raw_hdr, 1, 32, input_ctx->file) == 32) {
|
||||
if (memcmp(IVF_SIGNATURE, raw_hdr, 4) == 0) {
|
||||
is_ivf = 1;
|
||||
|
||||
if (mem_get_le16(raw_hdr + 4) != 0) {
|
||||
fprintf(stderr,
|
||||
"Error: Unrecognized IVF version! This file may not"
|
||||
" decode properly.");
|
||||
}
|
||||
|
||||
input_ctx->fourcc = mem_get_le32(raw_hdr + 8);
|
||||
input_ctx->width = mem_get_le16(raw_hdr + 12);
|
||||
input_ctx->height = mem_get_le16(raw_hdr + 14);
|
||||
input_ctx->framerate.numerator = mem_get_le32(raw_hdr + 16);
|
||||
input_ctx->framerate.denominator = mem_get_le32(raw_hdr + 20);
|
||||
fix_framerate(&input_ctx->framerate.numerator,
|
||||
&input_ctx->framerate.denominator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_ivf) {
|
||||
rewind(input_ctx->file);
|
||||
input_ctx->detect.buf_read = 0;
|
||||
} else {
|
||||
input_ctx->detect.position = 4;
|
||||
}
|
||||
return is_ivf;
|
||||
}
|
||||
|
||||
int ivf_read_frame(FILE *infile, uint8_t **buffer, size_t *bytes_read,
|
||||
size_t *buffer_size) {
|
||||
char raw_header[IVF_FRAME_HDR_SZ] = { 0 };
|
||||
size_t frame_size = 0;
|
||||
|
||||
if (fread(raw_header, IVF_FRAME_HDR_SZ, 1, infile) != 1) {
|
||||
if (!feof(infile)) warn("Failed to read frame size");
|
||||
} else {
|
||||
frame_size = mem_get_le32(raw_header);
|
||||
|
||||
if (frame_size > 256 * 1024 * 1024) {
|
||||
warn("Read invalid frame size (%u)", (unsigned int)frame_size);
|
||||
frame_size = 0;
|
||||
}
|
||||
|
||||
if (frame_size > *buffer_size) {
|
||||
uint8_t *new_buffer = realloc(*buffer, 2 * frame_size);
|
||||
|
||||
if (new_buffer) {
|
||||
*buffer = new_buffer;
|
||||
*buffer_size = 2 * frame_size;
|
||||
} else {
|
||||
warn("Failed to allocate compressed data buffer");
|
||||
frame_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!feof(infile)) {
|
||||
if (fread(*buffer, 1, frame_size, infile) != frame_size) {
|
||||
warn("Failed to read full frame");
|
||||
return 1;
|
||||
}
|
||||
|
||||
*bytes_read = frame_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef VPX_IVFDEC_H_
|
||||
#define VPX_IVFDEC_H_
|
||||
|
||||
#include "./tools_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int file_is_ivf(struct VpxInputContext *input);
|
||||
|
||||
int ivf_read_frame(FILE *infile, uint8_t **buffer, size_t *bytes_read,
|
||||
size_t *buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif // VPX_IVFDEC_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 "./ivfenc.h"
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx_ports/mem_ops.h"
|
||||
|
||||
void ivf_write_file_header(FILE *outfile, const struct vpx_codec_enc_cfg *cfg,
|
||||
unsigned int fourcc, int frame_cnt) {
|
||||
char header[32];
|
||||
|
||||
header[0] = 'D';
|
||||
header[1] = 'K';
|
||||
header[2] = 'I';
|
||||
header[3] = 'F';
|
||||
mem_put_le16(header + 4, 0); // version
|
||||
mem_put_le16(header + 6, 32); // header size
|
||||
mem_put_le32(header + 8, fourcc); // fourcc
|
||||
mem_put_le16(header + 12, cfg->g_w); // width
|
||||
mem_put_le16(header + 14, cfg->g_h); // height
|
||||
mem_put_le32(header + 16, cfg->g_timebase.den); // rate
|
||||
mem_put_le32(header + 20, cfg->g_timebase.num); // scale
|
||||
mem_put_le32(header + 24, frame_cnt); // length
|
||||
mem_put_le32(header + 28, 0); // unused
|
||||
|
||||
fwrite(header, 1, 32, outfile);
|
||||
}
|
||||
|
||||
void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size) {
|
||||
char header[12];
|
||||
|
||||
mem_put_le32(header, (int)frame_size);
|
||||
mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF));
|
||||
mem_put_le32(header + 8, (int)(pts >> 32));
|
||||
fwrite(header, 1, 12, outfile);
|
||||
}
|
||||
|
||||
void ivf_write_frame_size(FILE *outfile, size_t frame_size) {
|
||||
char header[4];
|
||||
|
||||
mem_put_le32(header, (int)frame_size);
|
||||
fwrite(header, 1, 4, outfile);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef VPX_IVFENC_H_
|
||||
#define VPX_IVFENC_H_
|
||||
|
||||
#include "./tools_common.h"
|
||||
|
||||
struct vpx_codec_enc_cfg;
|
||||
struct vpx_codec_cx_pkt;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ivf_write_file_header(FILE *outfile, const struct vpx_codec_enc_cfg *cfg,
|
||||
uint32_t fourcc, int frame_cnt);
|
||||
|
||||
void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size);
|
||||
|
||||
void ivf_write_frame_size(FILE *outfile, size_t frame_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif // VPX_IVFENC_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/*!\page rfc2119 RFC2119 Keywords
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
|
||||
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
|
||||
"OPTIONAL" in this document are to be interpreted as described in
|
||||
<a href="http://www.ietf.org/rfc/rfc2119.txt">RFC 2119.</a>
|
||||
|
||||
Specifically, the following definitions are used:
|
||||
|
||||
\section MUST
|
||||
\anchor REQUIRED
|
||||
\anchor SHALL
|
||||
This word, or the terms "REQUIRED" or "SHALL", mean that the
|
||||
definition is an absolute requirement of the specification.
|
||||
|
||||
\section MUSTNOT MUST NOT
|
||||
\anchor SHALLNOT
|
||||
This phrase, or the phrase "SHALL NOT", mean that the
|
||||
definition is an absolute prohibition of the specification.
|
||||
|
||||
\section SHOULD
|
||||
\anchor RECOMMENDED
|
||||
This word, or the adjective "RECOMMENDED", mean that there
|
||||
may exist valid reasons in particular circumstances to ignore a
|
||||
particular item, but the full implications must be understood and
|
||||
carefully weighed before choosing a different course.
|
||||
|
||||
\section SHOULDNOT SHOULD NOT
|
||||
\anchor NOTRECOMMENDED
|
||||
This phrase, or the phrase "NOT RECOMMENDED" mean that
|
||||
there may exist valid reasons in particular circumstances when the
|
||||
particular behavior is acceptable or even useful, but the full
|
||||
implications should be understood and the case carefully weighed
|
||||
before implementing any behavior described with this label.
|
||||
|
||||
\section MAY
|
||||
\anchor OPTIONAL
|
||||
This word, or the adjective "OPTIONAL", mean that an item is
|
||||
truly optional. One vendor may choose to include the item because a
|
||||
particular marketplace requires it or because the vendor feels that
|
||||
it enhances the product while another vendor may omit the same item.
|
||||
An implementation which does not include a particular option \ref MUST be
|
||||
prepared to interoperate with another implementation which does
|
||||
include the option, though perhaps with reduced functionality. In the
|
||||
same vein an implementation which does include a particular option
|
||||
\ref MUST be prepared to interoperate with another implementation which
|
||||
does not include the option (except, of course, for the feature the
|
||||
option provides.)
|
||||
|
||||
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,626 @@
|
||||
##
|
||||
## Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
|
||||
|
||||
# ARM assembly files are written in RVCT-style. We use some make magic to
|
||||
# filter those files to allow GCC compilation
|
||||
ifeq ($(VPX_ARCH_ARM),yes)
|
||||
ASM:=$(if $(filter yes,$(CONFIG_GCC)$(CONFIG_MSVS)),.asm.S,.asm)
|
||||
else
|
||||
ASM:=.asm
|
||||
endif
|
||||
|
||||
#
|
||||
# Rule to generate runtime cpu detection files
|
||||
#
|
||||
define rtcd_h_template
|
||||
$$(BUILD_PFX)$(1).h: $$(SRC_PATH_BARE)/$(2)
|
||||
@echo " [CREATE] $$@"
|
||||
$$(qexec)$$(SRC_PATH_BARE)/build/make/rtcd.pl --arch=$$(TGT_ISA) \
|
||||
--sym=$(1) \
|
||||
--config=$$(CONFIG_DIR)$$(target)-$$(TOOLCHAIN).mk \
|
||||
$$(RTCD_OPTIONS) $$^ > $$@
|
||||
CLEAN-OBJS += $$(BUILD_PFX)$(1).h
|
||||
RTCD += $$(BUILD_PFX)$(1).h
|
||||
endef
|
||||
|
||||
CODEC_SRCS-yes += CHANGELOG
|
||||
CODEC_SRCS-yes += libs.mk
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx/vpx_codec.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx/,$(call enabled,API_SRCS))
|
||||
CODEC_DOC_SRCS += $(addprefix vpx/,$(call enabled,API_DOC_SRCS))
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx_mem/vpx_mem.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx_mem/,$(call enabled,MEM_SRCS))
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx_scale/vpx_scale.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx_scale/,$(call enabled,SCALE_SRCS))
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx_ports/vpx_ports.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx_ports/,$(call enabled,PORTS_SRCS))
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx_dsp/vpx_dsp.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx_dsp/,$(call enabled,DSP_SRCS))
|
||||
|
||||
include $(SRC_PATH_BARE)/vpx_util/vpx_util.mk
|
||||
CODEC_SRCS-yes += $(addprefix vpx_util/,$(call enabled,UTIL_SRCS))
|
||||
|
||||
ifeq ($(CONFIG_VP8),yes)
|
||||
VP8_PREFIX=vp8/
|
||||
include $(SRC_PATH_BARE)/$(VP8_PREFIX)vp8_common.mk
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VP8_ENCODER),yes)
|
||||
include $(SRC_PATH_BARE)/$(VP8_PREFIX)vp8cx.mk
|
||||
CODEC_SRCS-yes += $(addprefix $(VP8_PREFIX),$(call enabled,VP8_CX_SRCS))
|
||||
CODEC_EXPORTS-yes += $(addprefix $(VP8_PREFIX),$(VP8_CX_EXPORTS))
|
||||
INSTALL-LIBS-yes += include/vpx/vp8.h include/vpx/vp8cx.h
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/$(VP8_PREFIX)/%
|
||||
CODEC_DOC_SECTIONS += vp8 vp8_encoder
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VP8_DECODER),yes)
|
||||
include $(SRC_PATH_BARE)/$(VP8_PREFIX)vp8dx.mk
|
||||
CODEC_SRCS-yes += $(addprefix $(VP8_PREFIX),$(call enabled,VP8_DX_SRCS))
|
||||
CODEC_EXPORTS-yes += $(addprefix $(VP8_PREFIX),$(VP8_DX_EXPORTS))
|
||||
INSTALL-LIBS-yes += include/vpx/vp8.h include/vpx/vp8dx.h
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/$(VP8_PREFIX)/%
|
||||
CODEC_DOC_SECTIONS += vp8 vp8_decoder
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VP9),yes)
|
||||
VP9_PREFIX=vp9/
|
||||
include $(SRC_PATH_BARE)/$(VP9_PREFIX)vp9_common.mk
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VP9_ENCODER),yes)
|
||||
VP9_PREFIX=vp9/
|
||||
include $(SRC_PATH_BARE)/$(VP9_PREFIX)vp9cx.mk
|
||||
CODEC_SRCS-yes += $(addprefix $(VP9_PREFIX),$(call enabled,VP9_CX_SRCS))
|
||||
CODEC_EXPORTS-yes += $(addprefix $(VP9_PREFIX),$(VP9_CX_EXPORTS))
|
||||
CODEC_SRCS-yes += $(VP9_PREFIX)vp9cx.mk vpx/vp8.h vpx/vp8cx.h
|
||||
INSTALL-LIBS-yes += include/vpx/vp8.h include/vpx/vp8cx.h
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/$(VP9_PREFIX)/%
|
||||
CODEC_DOC_SRCS += vpx/vp8.h vpx/vp8cx.h
|
||||
CODEC_DOC_SECTIONS += vp9 vp9_encoder
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_VP9_DECODER),yes)
|
||||
VP9_PREFIX=vp9/
|
||||
include $(SRC_PATH_BARE)/$(VP9_PREFIX)vp9dx.mk
|
||||
CODEC_SRCS-yes += $(addprefix $(VP9_PREFIX),$(call enabled,VP9_DX_SRCS))
|
||||
CODEC_EXPORTS-yes += $(addprefix $(VP9_PREFIX),$(VP9_DX_EXPORTS))
|
||||
CODEC_SRCS-yes += $(VP9_PREFIX)vp9dx.mk vpx/vp8.h vpx/vp8dx.h
|
||||
INSTALL-LIBS-yes += include/vpx/vp8.h include/vpx/vp8dx.h
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/$(VP9_PREFIX)/%
|
||||
CODEC_DOC_SRCS += vpx/vp8.h vpx/vp8dx.h
|
||||
CODEC_DOC_SECTIONS += vp9 vp9_decoder
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ENCODERS),yes)
|
||||
CODEC_DOC_SECTIONS += encoder
|
||||
endif
|
||||
ifeq ($(CONFIG_DECODERS),yes)
|
||||
CODEC_DOC_SECTIONS += decoder
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
CODEC_LIB=$(if $(CONFIG_STATIC_MSVCRT),vpxmt,vpxmd)
|
||||
GTEST_LIB=$(if $(CONFIG_STATIC_MSVCRT),gtestmt,gtestmd)
|
||||
# This variable uses deferred expansion intentionally, since the results of
|
||||
# $(wildcard) may change during the course of the Make.
|
||||
VS_PLATFORMS = $(foreach d,$(wildcard */Release/$(CODEC_LIB).lib),$(word 1,$(subst /, ,$(d))))
|
||||
endif
|
||||
|
||||
# The following pairs define a mapping of locations in the distribution
|
||||
# tree to locations in the source/build trees.
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/vpx/%
|
||||
INSTALL_MAPS += include/vpx/% $(SRC_PATH_BARE)/vpx_ports/%
|
||||
INSTALL_MAPS += $(LIBSUBDIR)/% %
|
||||
INSTALL_MAPS += src/% $(SRC_PATH_BARE)/%
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
INSTALL_MAPS += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/% $(p)/Release/%)
|
||||
INSTALL_MAPS += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/% $(p)/Debug/%)
|
||||
endif
|
||||
|
||||
CODEC_SRCS-yes += build/make/version.sh
|
||||
CODEC_SRCS-yes += build/make/rtcd.pl
|
||||
CODEC_SRCS-yes += vpx_ports/emmintrin_compat.h
|
||||
CODEC_SRCS-yes += vpx_ports/mem_ops.h
|
||||
CODEC_SRCS-yes += vpx_ports/mem_ops_aligned.h
|
||||
CODEC_SRCS-yes += vpx_ports/vpx_once.h
|
||||
CODEC_SRCS-yes += $(BUILD_PFX)vpx_config.c
|
||||
INSTALL-SRCS-no += $(BUILD_PFX)vpx_config.c
|
||||
ifeq ($(VPX_ARCH_X86)$(VPX_ARCH_X86_64),yes)
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += third_party/x86inc/x86inc.asm
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += vpx_dsp/x86/bitdepth_conversion_sse2.asm
|
||||
endif
|
||||
CODEC_EXPORTS-yes += vpx/exports_com
|
||||
CODEC_EXPORTS-$(CONFIG_ENCODERS) += vpx/exports_enc
|
||||
CODEC_EXPORTS-$(CONFIG_DECODERS) += vpx/exports_dec
|
||||
|
||||
INSTALL-LIBS-yes += include/vpx/vpx_codec.h
|
||||
INSTALL-LIBS-yes += include/vpx/vpx_frame_buffer.h
|
||||
INSTALL-LIBS-yes += include/vpx/vpx_image.h
|
||||
INSTALL-LIBS-yes += include/vpx/vpx_integer.h
|
||||
INSTALL-LIBS-$(CONFIG_DECODERS) += include/vpx/vpx_decoder.h
|
||||
INSTALL-LIBS-$(CONFIG_ENCODERS) += include/vpx/vpx_encoder.h
|
||||
ifeq ($(CONFIG_EXTERNAL_BUILD),yes)
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
INSTALL-LIBS-yes += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/$(CODEC_LIB).lib)
|
||||
INSTALL-LIBS-$(CONFIG_DEBUG_LIBS) += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/$(CODEC_LIB)d.lib)
|
||||
INSTALL-LIBS-$(CONFIG_SHARED) += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/vpx.dll)
|
||||
INSTALL-LIBS-$(CONFIG_SHARED) += $(foreach p,$(VS_PLATFORMS),$(LIBSUBDIR)/$(p)/vpx.exp)
|
||||
endif
|
||||
else
|
||||
INSTALL-LIBS-$(CONFIG_STATIC) += $(LIBSUBDIR)/libvpx.a
|
||||
INSTALL-LIBS-$(CONFIG_DEBUG_LIBS) += $(LIBSUBDIR)/libvpx_g.a
|
||||
endif
|
||||
|
||||
CODEC_SRCS=$(call enabled,CODEC_SRCS)
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(CODEC_SRCS)
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(call enabled,CODEC_EXPORTS)
|
||||
|
||||
|
||||
# Generate a list of all enabled sources, in particular for exporting to gyp
|
||||
# based build systems.
|
||||
libvpx_srcs.txt:
|
||||
@echo " [CREATE] $@"
|
||||
@echo $(CODEC_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@
|
||||
CLEAN-OBJS += libvpx_srcs.txt
|
||||
|
||||
# Assembly files that are included, but don't define symbols themselves.
|
||||
# Filtered out to avoid Windows build warnings.
|
||||
ASM_INCLUDES := \
|
||||
third_party/x86inc/x86inc.asm \
|
||||
vpx_config.asm \
|
||||
vpx_ports/x86_abi_support.asm \
|
||||
vpx_dsp/x86/bitdepth_conversion_sse2.asm \
|
||||
|
||||
ifeq ($(CONFIG_EXTERNAL_BUILD),yes)
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
|
||||
vpx.def: $(call enabled,CODEC_EXPORTS)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)$(SRC_PATH_BARE)/build/make/gen_msvs_def.sh\
|
||||
--name=vpx\
|
||||
--out=$@ $^
|
||||
CLEAN-OBJS += vpx.def
|
||||
|
||||
vpx.$(VCPROJ_SFX): VCPROJ_SRCS=$(filter-out $(addprefix %, $(ASM_INCLUDES)), $^)
|
||||
|
||||
vpx.$(VCPROJ_SFX): $(CODEC_SRCS) vpx.def
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)$(GEN_VCPROJ) \
|
||||
$(if $(CONFIG_SHARED),--dll,--lib) \
|
||||
--target=$(TOOLCHAIN) \
|
||||
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
|
||||
--name=vpx \
|
||||
--proj-guid=DCE19DAF-69AC-46DB-B14A-39F0FAA5DB74 \
|
||||
--module-def=vpx.def \
|
||||
--ver=$(CONFIG_VS_VERSION) \
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
--out=$@ $(CFLAGS) \
|
||||
$(filter $(SRC_PATH_BARE)/vp8/%.c, $(VCPROJ_SRCS)) \
|
||||
$(filter $(SRC_PATH_BARE)/vp8/%.h, $(VCPROJ_SRCS)) \
|
||||
$(filter $(SRC_PATH_BARE)/vp9/%.c, $(VCPROJ_SRCS)) \
|
||||
$(filter $(SRC_PATH_BARE)/vp9/%.h, $(VCPROJ_SRCS)) \
|
||||
$(filter $(SRC_PATH_BARE)/vpx/%, $(VCPROJ_SRCS)) \
|
||||
$(filter $(SRC_PATH_BARE)/vpx_dsp/%, $(VCPROJ_SRCS)) \
|
||||
$(filter-out $(addprefix $(SRC_PATH_BARE)/, \
|
||||
vp8/%.c vp8/%.h vp9/%.c vp9/%.h vpx/% vpx_dsp/%), \
|
||||
$(VCPROJ_SRCS)) \
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
|
||||
PROJECTS-yes += vpx.$(VCPROJ_SFX)
|
||||
|
||||
vpx.$(VCPROJ_SFX): vpx_config.asm
|
||||
vpx.$(VCPROJ_SFX): $(RTCD)
|
||||
|
||||
endif
|
||||
else
|
||||
LIBVPX_OBJS=$(call objs, $(filter-out $(ASM_INCLUDES), $(CODEC_SRCS)))
|
||||
OBJS-yes += $(LIBVPX_OBJS)
|
||||
LIBS-$(if yes,$(CONFIG_STATIC)) += $(BUILD_PFX)libvpx.a $(BUILD_PFX)libvpx_g.a
|
||||
$(BUILD_PFX)libvpx_g.a: $(LIBVPX_OBJS)
|
||||
|
||||
SO_VERSION_MAJOR := 6
|
||||
SO_VERSION_MINOR := 1
|
||||
SO_VERSION_PATCH := 0
|
||||
ifeq ($(filter darwin%,$(TGT_OS)),$(TGT_OS))
|
||||
LIBVPX_SO := libvpx.$(SO_VERSION_MAJOR).dylib
|
||||
SHARED_LIB_SUF := .dylib
|
||||
EXPORT_FILE := libvpx.syms
|
||||
LIBVPX_SO_SYMLINKS := $(addprefix $(LIBSUBDIR)/, \
|
||||
libvpx.dylib )
|
||||
else
|
||||
ifeq ($(filter iphonesimulator%,$(TGT_OS)),$(TGT_OS))
|
||||
LIBVPX_SO := libvpx.$(SO_VERSION_MAJOR).dylib
|
||||
SHARED_LIB_SUF := .dylib
|
||||
EXPORT_FILE := libvpx.syms
|
||||
LIBVPX_SO_SYMLINKS := $(addprefix $(LIBSUBDIR)/, libvpx.dylib)
|
||||
else
|
||||
ifeq ($(filter os2%,$(TGT_OS)),$(TGT_OS))
|
||||
LIBVPX_SO := libvpx$(SO_VERSION_MAJOR).dll
|
||||
SHARED_LIB_SUF := _dll.a
|
||||
EXPORT_FILE := libvpx.def
|
||||
LIBVPX_SO_SYMLINKS :=
|
||||
LIBVPX_SO_IMPLIB := libvpx_dll.a
|
||||
else
|
||||
LIBVPX_SO := libvpx.so.$(SO_VERSION_MAJOR).$(SO_VERSION_MINOR).$(SO_VERSION_PATCH)
|
||||
SHARED_LIB_SUF := .so
|
||||
EXPORT_FILE := libvpx.ver
|
||||
LIBVPX_SO_SYMLINKS := $(addprefix $(LIBSUBDIR)/, \
|
||||
libvpx.so libvpx.so.$(SO_VERSION_MAJOR) \
|
||||
libvpx.so.$(SO_VERSION_MAJOR).$(SO_VERSION_MINOR))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
LIBS-$(CONFIG_SHARED) += $(BUILD_PFX)$(LIBVPX_SO)\
|
||||
$(notdir $(LIBVPX_SO_SYMLINKS)) \
|
||||
$(if $(LIBVPX_SO_IMPLIB), $(BUILD_PFX)$(LIBVPX_SO_IMPLIB))
|
||||
$(BUILD_PFX)$(LIBVPX_SO): $(LIBVPX_OBJS) $(EXPORT_FILE)
|
||||
$(BUILD_PFX)$(LIBVPX_SO): extralibs += -lm
|
||||
$(BUILD_PFX)$(LIBVPX_SO): SONAME = libvpx.so.$(SO_VERSION_MAJOR)
|
||||
$(BUILD_PFX)$(LIBVPX_SO): EXPORTS_FILE = $(EXPORT_FILE)
|
||||
|
||||
libvpx.def: $(call enabled,CODEC_EXPORTS)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)echo LIBRARY $(LIBVPX_SO:.dll=) INITINSTANCE TERMINSTANCE > $@
|
||||
$(qexec)echo "DATA MULTIPLE NONSHARED" >> $@
|
||||
$(qexec)echo "EXPORTS" >> $@
|
||||
$(qexec)awk '!/vpx_svc_*/ {print "_"$$2}' $^ >>$@
|
||||
CLEAN-OBJS += libvpx.def
|
||||
|
||||
libvpx_dll.a: $(LIBVPX_SO)
|
||||
@echo " [IMPLIB] $@"
|
||||
$(qexec)emximp -o $@ $<
|
||||
CLEAN-OBJS += libvpx_dll.a
|
||||
|
||||
define libvpx_symlink_template
|
||||
$(1): $(2)
|
||||
@echo " [LN] $(2) $$@"
|
||||
$(qexec)mkdir -p $$(dir $$@)
|
||||
$(qexec)ln -sf $(2) $$@
|
||||
endef
|
||||
|
||||
$(eval $(call libvpx_symlink_template,\
|
||||
$(addprefix $(BUILD_PFX),$(notdir $(LIBVPX_SO_SYMLINKS))),\
|
||||
$(BUILD_PFX)$(LIBVPX_SO)))
|
||||
$(eval $(call libvpx_symlink_template,\
|
||||
$(addprefix $(DIST_DIR)/,$(LIBVPX_SO_SYMLINKS)),\
|
||||
$(LIBVPX_SO)))
|
||||
|
||||
|
||||
INSTALL-LIBS-$(CONFIG_SHARED) += $(LIBVPX_SO_SYMLINKS)
|
||||
INSTALL-LIBS-$(CONFIG_SHARED) += $(LIBSUBDIR)/$(LIBVPX_SO)
|
||||
INSTALL-LIBS-$(CONFIG_SHARED) += $(if $(LIBVPX_SO_IMPLIB),$(LIBSUBDIR)/$(LIBVPX_SO_IMPLIB))
|
||||
|
||||
|
||||
LIBS-yes += vpx.pc
|
||||
vpx.pc: config.mk libs.mk
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)echo '# pkg-config file from libvpx $(VERSION_STRING)' > $@
|
||||
$(qexec)echo 'prefix=$(PREFIX)' >> $@
|
||||
$(qexec)echo 'exec_prefix=$${prefix}' >> $@
|
||||
$(qexec)echo 'libdir=$${prefix}/$(LIBSUBDIR)' >> $@
|
||||
$(qexec)echo 'includedir=$${prefix}/include' >> $@
|
||||
$(qexec)echo '' >> $@
|
||||
$(qexec)echo 'Name: vpx' >> $@
|
||||
$(qexec)echo 'Description: WebM Project VPx codec implementation' >> $@
|
||||
$(qexec)echo 'Version: $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)' >> $@
|
||||
$(qexec)echo 'Requires:' >> $@
|
||||
$(qexec)echo 'Conflicts:' >> $@
|
||||
$(qexec)echo 'Libs: -L$${libdir} -lvpx -lm' >> $@
|
||||
ifeq ($(HAVE_PTHREAD_H),yes)
|
||||
$(qexec)echo 'Libs.private: -lm -lpthread' >> $@
|
||||
else
|
||||
$(qexec)echo 'Libs.private: -lm' >> $@
|
||||
endif
|
||||
$(qexec)echo 'Cflags: -I$${includedir}' >> $@
|
||||
INSTALL-LIBS-yes += $(LIBSUBDIR)/pkgconfig/vpx.pc
|
||||
INSTALL_MAPS += $(LIBSUBDIR)/pkgconfig/%.pc %.pc
|
||||
CLEAN-OBJS += vpx.pc
|
||||
endif
|
||||
|
||||
libvpx.ver: $(call enabled,CODEC_EXPORTS)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)echo "{ global:" > $@
|
||||
$(qexec)for f in $?; do awk '{print $$2";"}' < $$f >>$@; done
|
||||
$(qexec)echo "local: *; };" >> $@
|
||||
CLEAN-OBJS += libvpx.ver
|
||||
|
||||
libvpx.syms: $(call enabled,CODEC_EXPORTS)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)awk '{print "_"$$2}' $^ >$@
|
||||
CLEAN-OBJS += libvpx.syms
|
||||
|
||||
#
|
||||
# Rule to make assembler configuration file from C configuration file
|
||||
#
|
||||
ifeq ($(VPX_ARCH_X86)$(VPX_ARCH_X86_64),yes)
|
||||
# YASM
|
||||
$(BUILD_PFX)vpx_config.asm: $(BUILD_PFX)vpx_config.h
|
||||
@echo " [CREATE] $@"
|
||||
@egrep "#define [A-Z0-9_]+ [01]" $< \
|
||||
| awk '{print $$2 " equ " $$3}' > $@
|
||||
else
|
||||
ADS2GAS=$(if $(filter yes,$(CONFIG_GCC)),| $(ASM_CONVERSION))
|
||||
$(BUILD_PFX)vpx_config.asm: $(BUILD_PFX)vpx_config.h
|
||||
@echo " [CREATE] $@"
|
||||
@egrep "#define [A-Z0-9_]+ [01]" $< \
|
||||
| awk '{print $$2 " EQU " $$3}' $(ADS2GAS) > $@
|
||||
@echo " END" $(ADS2GAS) >> $@
|
||||
CLEAN-OBJS += $(BUILD_PFX)vpx_config.asm
|
||||
endif
|
||||
|
||||
#
|
||||
# Add assembler dependencies for configuration.
|
||||
#
|
||||
$(filter %.S.o,$(OBJS-yes)): $(BUILD_PFX)vpx_config.asm
|
||||
$(filter %$(ASM).o,$(OBJS-yes)): $(BUILD_PFX)vpx_config.asm
|
||||
|
||||
|
||||
$(shell $(SRC_PATH_BARE)/build/make/version.sh "$(SRC_PATH_BARE)" $(BUILD_PFX)vpx_version.h)
|
||||
CLEAN-OBJS += $(BUILD_PFX)vpx_version.h
|
||||
|
||||
#
|
||||
# Add include path for libwebm sources.
|
||||
#
|
||||
ifeq ($(CONFIG_WEBM_IO),yes)
|
||||
CXXFLAGS += -I$(SRC_PATH_BARE)/third_party/libwebm
|
||||
endif
|
||||
|
||||
##
|
||||
## libvpx test directives
|
||||
##
|
||||
ifeq ($(CONFIG_UNIT_TESTS),yes)
|
||||
LIBVPX_TEST_DATA_PATH ?= .
|
||||
|
||||
include $(SRC_PATH_BARE)/test/test.mk
|
||||
LIBVPX_TEST_SRCS=$(addprefix test/,$(call enabled,LIBVPX_TEST_SRCS))
|
||||
LIBVPX_TEST_BIN=./test_libvpx$(EXE_SFX)
|
||||
LIBVPX_TEST_DATA=$(addprefix $(LIBVPX_TEST_DATA_PATH)/,\
|
||||
$(call enabled,LIBVPX_TEST_DATA))
|
||||
libvpx_test_data_url=https://storage.googleapis.com/downloads.webmproject.org/test_data/libvpx/$(1)
|
||||
|
||||
TEST_INTRA_PRED_SPEED_BIN=./test_intra_pred_speed$(EXE_SFX)
|
||||
TEST_INTRA_PRED_SPEED_SRCS=$(addprefix test/,$(call enabled,TEST_INTRA_PRED_SPEED_SRCS))
|
||||
TEST_INTRA_PRED_SPEED_OBJS := $(sort $(call objs,$(TEST_INTRA_PRED_SPEED_SRCS)))
|
||||
|
||||
libvpx_test_srcs.txt:
|
||||
@echo " [CREATE] $@"
|
||||
@echo $(LIBVPX_TEST_SRCS) | xargs -n1 echo | LC_ALL=C sort -u > $@
|
||||
CLEAN-OBJS += libvpx_test_srcs.txt
|
||||
|
||||
$(LIBVPX_TEST_DATA): $(SRC_PATH_BARE)/test/test-data.sha1
|
||||
@echo " [DOWNLOAD] $@"
|
||||
# Attempt to download the file using curl, retrying once if it fails for a
|
||||
# partial file (18).
|
||||
$(qexec)( \
|
||||
trap 'rm -f $@' INT TERM; \
|
||||
curl="curl --retry 1 -L -o $@ $(call libvpx_test_data_url,$(@F))"; \
|
||||
$$curl; \
|
||||
case "$$?" in \
|
||||
18) $$curl -C -;; \
|
||||
esac \
|
||||
)
|
||||
|
||||
testdata:: $(LIBVPX_TEST_DATA)
|
||||
$(qexec)[ -x "$$(which sha1sum)" ] && sha1sum=sha1sum;\
|
||||
[ -x "$$(which shasum)" ] && sha1sum=shasum;\
|
||||
[ -x "$$(which sha1)" ] && sha1sum=sha1;\
|
||||
if [ -n "$${sha1sum}" ]; then\
|
||||
set -e;\
|
||||
echo "Checking test data:";\
|
||||
for f in $(call enabled,LIBVPX_TEST_DATA); do\
|
||||
grep $$f $(SRC_PATH_BARE)/test/test-data.sha1 |\
|
||||
(cd $(LIBVPX_TEST_DATA_PATH); $${sha1sum} -c);\
|
||||
done; \
|
||||
else\
|
||||
echo "Skipping test data integrity check, sha1sum not found.";\
|
||||
fi
|
||||
|
||||
ifeq ($(CONFIG_EXTERNAL_BUILD),yes)
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
|
||||
gtest.$(VCPROJ_SFX): $(SRC_PATH_BARE)/third_party/googletest/src/src/gtest-all.cc
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)$(GEN_VCPROJ) \
|
||||
--lib \
|
||||
--target=$(TOOLCHAIN) \
|
||||
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
|
||||
--name=gtest \
|
||||
--proj-guid=EC00E1EC-AF68-4D92-A255-181690D1C9B1 \
|
||||
--ver=$(CONFIG_VS_VERSION) \
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
-D_VARIADIC_MAX=10 \
|
||||
--out=gtest.$(VCPROJ_SFX) $(SRC_PATH_BARE)/third_party/googletest/src/src/gtest-all.cc \
|
||||
-I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" -I"$(SRC_PATH_BARE)/third_party/googletest/src"
|
||||
|
||||
PROJECTS-$(CONFIG_MSVS) += gtest.$(VCPROJ_SFX)
|
||||
|
||||
test_libvpx.$(VCPROJ_SFX): $(LIBVPX_TEST_SRCS) vpx.$(VCPROJ_SFX) gtest.$(VCPROJ_SFX)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)$(GEN_VCPROJ) \
|
||||
--exe \
|
||||
--target=$(TOOLCHAIN) \
|
||||
--name=test_libvpx \
|
||||
-D_VARIADIC_MAX=10 \
|
||||
--proj-guid=CD837F5F-52D8-4314-A370-895D614166A7 \
|
||||
--ver=$(CONFIG_VS_VERSION) \
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
|
||||
--out=$@ $(INTERNAL_CFLAGS) $(CFLAGS) \
|
||||
-I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
|
||||
$(if $(CONFIG_WEBM_IO),-I"$(SRC_PATH_BARE)/third_party/libwebm") \
|
||||
-L. -l$(CODEC_LIB) -l$(GTEST_LIB) $^
|
||||
|
||||
PROJECTS-$(CONFIG_MSVS) += test_libvpx.$(VCPROJ_SFX)
|
||||
|
||||
LIBVPX_TEST_BIN := $(addprefix $(TGT_OS:win64=x64)/Release/,$(notdir $(LIBVPX_TEST_BIN)))
|
||||
|
||||
ifneq ($(strip $(TEST_INTRA_PRED_SPEED_OBJS)),)
|
||||
PROJECTS-$(CONFIG_MSVS) += test_intra_pred_speed.$(VCPROJ_SFX)
|
||||
test_intra_pred_speed.$(VCPROJ_SFX): $(TEST_INTRA_PRED_SPEED_SRCS) vpx.$(VCPROJ_SFX) gtest.$(VCPROJ_SFX)
|
||||
@echo " [CREATE] $@"
|
||||
$(qexec)$(GEN_VCPROJ) \
|
||||
--exe \
|
||||
--target=$(TOOLCHAIN) \
|
||||
--name=test_intra_pred_speed \
|
||||
-D_VARIADIC_MAX=10 \
|
||||
--proj-guid=CD837F5F-52D8-4314-A370-895D614166A7 \
|
||||
--ver=$(CONFIG_VS_VERSION) \
|
||||
--src-path-bare="$(SRC_PATH_BARE)" \
|
||||
$(if $(CONFIG_STATIC_MSVCRT),--static-crt) \
|
||||
--out=$@ $(INTERNAL_CFLAGS) $(CFLAGS) \
|
||||
-I. -I"$(SRC_PATH_BARE)/third_party/googletest/src/include" \
|
||||
-L. -l$(CODEC_LIB) -l$(GTEST_LIB) $^
|
||||
endif # TEST_INTRA_PRED_SPEED
|
||||
endif
|
||||
else
|
||||
|
||||
include $(SRC_PATH_BARE)/third_party/googletest/gtest.mk
|
||||
GTEST_SRCS := $(addprefix third_party/googletest/src/,$(call enabled,GTEST_SRCS))
|
||||
GTEST_OBJS=$(call objs,$(GTEST_SRCS))
|
||||
ifeq ($(filter win%,$(TGT_OS)),$(TGT_OS))
|
||||
# Disabling pthreads globally will cause issues on darwin and possibly elsewhere
|
||||
$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CXXFLAGS += -DGTEST_HAS_PTHREAD=0
|
||||
endif
|
||||
GTEST_INCLUDES := -I$(SRC_PATH_BARE)/third_party/googletest/src
|
||||
GTEST_INCLUDES += -I$(SRC_PATH_BARE)/third_party/googletest/src/include
|
||||
$(GTEST_OBJS) $(GTEST_OBJS:.o=.d): CXXFLAGS += $(GTEST_INCLUDES)
|
||||
OBJS-yes += $(GTEST_OBJS)
|
||||
LIBS-yes += $(BUILD_PFX)libgtest.a $(BUILD_PFX)libgtest_g.a
|
||||
$(BUILD_PFX)libgtest_g.a: $(GTEST_OBJS)
|
||||
|
||||
LIBVPX_TEST_OBJS=$(sort $(call objs,$(LIBVPX_TEST_SRCS)))
|
||||
$(LIBVPX_TEST_OBJS) $(LIBVPX_TEST_OBJS:.o=.d): CXXFLAGS += $(GTEST_INCLUDES)
|
||||
OBJS-yes += $(LIBVPX_TEST_OBJS)
|
||||
BINS-yes += $(LIBVPX_TEST_BIN)
|
||||
|
||||
CODEC_LIB=$(if $(CONFIG_DEBUG_LIBS),vpx_g,vpx)
|
||||
CODEC_LIB_SUF=$(if $(CONFIG_SHARED),$(SHARED_LIB_SUF),.a)
|
||||
TEST_LIBS := lib$(CODEC_LIB)$(CODEC_LIB_SUF) libgtest.a
|
||||
$(LIBVPX_TEST_BIN): $(TEST_LIBS)
|
||||
$(eval $(call linkerxx_template,$(LIBVPX_TEST_BIN), \
|
||||
$(LIBVPX_TEST_OBJS) \
|
||||
-L. -lvpx -lgtest $(extralibs) -lm))
|
||||
|
||||
ifneq ($(strip $(TEST_INTRA_PRED_SPEED_OBJS)),)
|
||||
$(TEST_INTRA_PRED_SPEED_OBJS) $(TEST_INTRA_PRED_SPEED_OBJS:.o=.d): CXXFLAGS += $(GTEST_INCLUDES)
|
||||
OBJS-yes += $(TEST_INTRA_PRED_SPEED_OBJS)
|
||||
BINS-yes += $(TEST_INTRA_PRED_SPEED_BIN)
|
||||
|
||||
$(TEST_INTRA_PRED_SPEED_BIN): $(TEST_LIBS)
|
||||
$(eval $(call linkerxx_template,$(TEST_INTRA_PRED_SPEED_BIN), \
|
||||
$(TEST_INTRA_PRED_SPEED_OBJS) \
|
||||
-L. -lvpx -lgtest $(extralibs) -lm))
|
||||
endif # TEST_INTRA_PRED_SPEED
|
||||
|
||||
endif # CONFIG_UNIT_TESTS
|
||||
|
||||
# Install test sources only if codec source is included
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(patsubst $(SRC_PATH_BARE)/%,%,\
|
||||
$(shell find $(SRC_PATH_BARE)/third_party/googletest -type f))
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(LIBVPX_TEST_SRCS)
|
||||
INSTALL-SRCS-$(CONFIG_CODEC_SRCS) += $(TEST_INTRA_PRED_SPEED_SRCS)
|
||||
|
||||
define test_shard_template
|
||||
test:: test_shard.$(1)
|
||||
test-no-data-check:: test_shard_ndc.$(1)
|
||||
test_shard.$(1) test_shard_ndc.$(1): $(LIBVPX_TEST_BIN)
|
||||
@set -e; \
|
||||
export GTEST_SHARD_INDEX=$(1); \
|
||||
export GTEST_TOTAL_SHARDS=$(2); \
|
||||
$(LIBVPX_TEST_BIN)
|
||||
test_shard.$(1): testdata
|
||||
.PHONY: test_shard.$(1)
|
||||
endef
|
||||
|
||||
NUM_SHARDS := 10
|
||||
SHARDS := 0 1 2 3 4 5 6 7 8 9
|
||||
$(foreach s,$(SHARDS),$(eval $(call test_shard_template,$(s),$(NUM_SHARDS))))
|
||||
|
||||
endif
|
||||
|
||||
##
|
||||
## documentation directives
|
||||
##
|
||||
CLEAN-OBJS += libs.doxy
|
||||
DOCS-yes += libs.doxy
|
||||
libs.doxy: $(CODEC_DOC_SRCS)
|
||||
@echo " [CREATE] $@"
|
||||
@rm -f $@
|
||||
@echo "INPUT += $^" >> $@
|
||||
@echo "INCLUDE_PATH += ." >> $@;
|
||||
@echo "ENABLED_SECTIONS += $(sort $(CODEC_DOC_SECTIONS))" >> $@
|
||||
|
||||
## Generate rtcd.h for all objects
|
||||
ifeq ($(CONFIG_DEPENDENCY_TRACKING),yes)
|
||||
$(OBJS-yes:.o=.d): $(RTCD)
|
||||
else
|
||||
$(OBJS-yes): $(RTCD)
|
||||
endif
|
||||
|
||||
## Update the global src list
|
||||
SRCS += $(CODEC_SRCS) $(LIBVPX_TEST_SRCS) $(GTEST_SRCS)
|
||||
|
||||
##
|
||||
## vpxdec/vpxenc tests.
|
||||
##
|
||||
ifeq ($(CONFIG_UNIT_TESTS),yes)
|
||||
TEST_BIN_PATH = .
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
# MSVC will build both Debug and Release configurations of tools in a
|
||||
# sub directory named for the current target. Assume the user wants to
|
||||
# run the Release tools, and assign TEST_BIN_PATH accordingly.
|
||||
# TODO(tomfinegan): Is this adequate for ARM?
|
||||
# TODO(tomfinegan): Support running the debug versions of tools?
|
||||
TEST_BIN_PATH := $(addsuffix /$(TGT_OS:win64=x64)/Release, $(TEST_BIN_PATH))
|
||||
endif
|
||||
utiltest utiltest-no-data-check:
|
||||
$(qexec)$(SRC_PATH_BARE)/test/vpxdec.sh \
|
||||
--test-data-path $(LIBVPX_TEST_DATA_PATH) \
|
||||
--bin-path $(TEST_BIN_PATH)
|
||||
$(qexec)$(SRC_PATH_BARE)/test/vpxenc.sh \
|
||||
--test-data-path $(LIBVPX_TEST_DATA_PATH) \
|
||||
--bin-path $(TEST_BIN_PATH)
|
||||
utiltest: testdata
|
||||
else
|
||||
utiltest utiltest-no-data-check:
|
||||
@echo Unit tests must be enabled to make the utiltest target.
|
||||
endif
|
||||
|
||||
##
|
||||
## Example tests.
|
||||
##
|
||||
ifeq ($(CONFIG_UNIT_TESTS),yes)
|
||||
# All non-MSVC targets output example targets in a sub dir named examples.
|
||||
EXAMPLES_BIN_PATH = examples
|
||||
ifeq ($(CONFIG_MSVS),yes)
|
||||
# MSVC will build both Debug and Release configurations of the examples in a
|
||||
# sub directory named for the current target. Assume the user wants to
|
||||
# run the Release tools, and assign EXAMPLES_BIN_PATH accordingly.
|
||||
# TODO(tomfinegan): Is this adequate for ARM?
|
||||
# TODO(tomfinegan): Support running the debug versions of tools?
|
||||
EXAMPLES_BIN_PATH := $(TGT_OS:win64=x64)/Release
|
||||
endif
|
||||
exampletest exampletest-no-data-check: examples
|
||||
$(qexec)$(SRC_PATH_BARE)/test/examples.sh \
|
||||
--test-data-path $(LIBVPX_TEST_DATA_PATH) \
|
||||
--bin-path $(EXAMPLES_BIN_PATH)
|
||||
exampletest: testdata
|
||||
else
|
||||
exampletest exampletest-no-data-check:
|
||||
@echo Unit tests must be enabled to make the exampletest target.
|
||||
endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/*!\mainpage WebM Codec SDK
|
||||
|
||||
\section main_contents Page Contents
|
||||
- \ref main_intro
|
||||
- \ref main_startpoints
|
||||
- \ref main_support
|
||||
|
||||
\section main_intro Introduction
|
||||
Welcome to the WebM Codec SDK. This SDK allows you to integrate your
|
||||
applications with the VP8 and VP9 video codecs, high quality, royalty free,
|
||||
open source codecs deployed on billions of computers and devices worldwide.
|
||||
|
||||
This distribution of the WebM Codec SDK includes the following support:
|
||||
|
||||
\if vp8_encoder
|
||||
- \ref vp8_encoder
|
||||
\endif
|
||||
\if vp8_decoder
|
||||
- \ref vp8_decoder
|
||||
\endif
|
||||
|
||||
|
||||
\section main_startpoints Starting Points
|
||||
- Consult the \ref changelog for a complete list of improvements in this
|
||||
release.
|
||||
- The \ref readme contains instructions on recompiling the sample applications.
|
||||
- Read the \ref usage "usage" for a narrative on codec usage.
|
||||
\if samples
|
||||
- Read the \ref samples "sample code" for examples of how to interact with the
|
||||
codec.
|
||||
\endif
|
||||
- \ref codec reference
|
||||
\if encoder
|
||||
- \ref encoder reference
|
||||
\endif
|
||||
\if decoder
|
||||
- \ref decoder reference
|
||||
\endif
|
||||
|
||||
\section main_support Support Options & FAQ
|
||||
The WebM project is an open source project supported by its community. For
|
||||
questions about this SDK, please mail the apps-devel@webmproject.org list.
|
||||
To contribute, see http://www.webmproject.org/code/contribute and mail
|
||||
codec-devel@webmproject.org.
|
||||
*/
|
||||
|
||||
/*!\page changelog CHANGELOG
|
||||
\verbinclude CHANGELOG
|
||||
*/
|
||||
|
||||
/*!\page readme README
|
||||
\verbinclude README
|
||||
*/
|
||||
|
||||
/*!\defgroup codecs Supported Codecs */
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*
|
||||
* Changed so as no longer to depend on Colin Plumb's `usual.h' header
|
||||
* definitions
|
||||
* - Ian Jackson <ian@chiark.greenend.org.uk>.
|
||||
* Still in the public domain.
|
||||
*/
|
||||
|
||||
#include <string.h> /* for memcpy() */
|
||||
|
||||
#include "md5_utils.h"
|
||||
|
||||
static void byteSwap(UWORD32 *buf, unsigned words) {
|
||||
md5byte *p;
|
||||
|
||||
/* Only swap bytes for big endian machines */
|
||||
int i = 1;
|
||||
|
||||
if (*(char *)&i == 1) return;
|
||||
|
||||
p = (md5byte *)buf;
|
||||
|
||||
do {
|
||||
*buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 |
|
||||
((unsigned)p[1] << 8 | p[0]);
|
||||
p += 4;
|
||||
} while (--words);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init(struct MD5Context *ctx) {
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bytes[0] = 0;
|
||||
ctx->bytes[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) {
|
||||
UWORD32 t;
|
||||
|
||||
/* Update byte count */
|
||||
|
||||
t = ctx->bytes[0];
|
||||
|
||||
if ((ctx->bytes[0] = t + len) < t)
|
||||
ctx->bytes[1]++; /* Carry from low to high */
|
||||
|
||||
t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
|
||||
|
||||
if (t > len) {
|
||||
memcpy((md5byte *)ctx->in + 64 - t, buf, len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* First chunk is an odd size */
|
||||
memcpy((md5byte *)ctx->in + 64 - t, buf, t);
|
||||
byteSwap(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
|
||||
/* Process data in 64-byte chunks */
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteSwap(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final(md5byte digest[16], struct MD5Context *ctx) {
|
||||
int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
|
||||
md5byte *p = (md5byte *)ctx->in + count;
|
||||
|
||||
/* Set the first char of padding to 0x80. There is always room. */
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 56 bytes (-8..55) */
|
||||
count = 56 - 1 - count;
|
||||
|
||||
if (count < 0) { /* Padding forces an extra block */
|
||||
memset(p, 0, count + 8);
|
||||
byteSwap(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, ctx->in);
|
||||
p = (md5byte *)ctx->in;
|
||||
count = 56;
|
||||
}
|
||||
|
||||
memset(p, 0, count);
|
||||
byteSwap(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
ctx->in[14] = ctx->bytes[0] << 3;
|
||||
ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
|
||||
MD5Transform(ctx->buf, ctx->in);
|
||||
|
||||
byteSwap(ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
#ifndef ASM_MD5
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, in, s) \
|
||||
(w += f(x, y, z) + in, w = (w << s | w >> (32 - s)) + x)
|
||||
|
||||
#if defined(__clang__) && defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
#define VPX_NO_UNSIGNED_OVERFLOW_CHECK \
|
||||
__attribute__((no_sanitize("unsigned-integer-overflow")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VPX_NO_UNSIGNED_OVERFLOW_CHECK
|
||||
#define VPX_NO_UNSIGNED_OVERFLOW_CHECK
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
VPX_NO_UNSIGNED_OVERFLOW_CHECK void MD5Transform(UWORD32 buf[4],
|
||||
UWORD32 const in[16]) {
|
||||
UWORD32 a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
#undef VPX_NO_UNSIGNED_OVERFLOW_CHECK
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This is the header file for the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*
|
||||
* Changed so as no longer to depend on Colin Plumb's `usual.h'
|
||||
* header definitions
|
||||
* - Ian Jackson <ian@chiark.greenend.org.uk>.
|
||||
* Still in the public domain.
|
||||
*/
|
||||
|
||||
#ifndef VPX_MD5_UTILS_H_
|
||||
#define VPX_MD5_UTILS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define md5byte unsigned char
|
||||
#define UWORD32 unsigned int
|
||||
|
||||
typedef struct MD5Context MD5Context;
|
||||
struct MD5Context {
|
||||
UWORD32 buf[4];
|
||||
UWORD32 bytes[2];
|
||||
UWORD32 in[16];
|
||||
};
|
||||
|
||||
void MD5Init(struct MD5Context *context);
|
||||
void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
||||
void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // VPX_MD5_UTILS_H_
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "./rate_hist.h"
|
||||
|
||||
#define RATE_BINS 100
|
||||
#define HIST_BAR_MAX 40
|
||||
|
||||
struct hist_bucket {
|
||||
int low;
|
||||
int high;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct rate_hist {
|
||||
int64_t *pts;
|
||||
int *sz;
|
||||
int samples;
|
||||
int frames;
|
||||
struct hist_bucket bucket[RATE_BINS];
|
||||
int total;
|
||||
};
|
||||
|
||||
struct rate_hist *init_rate_histogram(const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_rational_t *fps) {
|
||||
int i;
|
||||
struct rate_hist *hist = calloc(1, sizeof(*hist));
|
||||
|
||||
if (hist == NULL || cfg == NULL || fps == NULL || fps->num == 0 ||
|
||||
fps->den == 0) {
|
||||
destroy_rate_histogram(hist);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Determine the number of samples in the buffer. Use the file's framerate
|
||||
// to determine the number of frames in rc_buf_sz milliseconds, with an
|
||||
// adjustment (5/4) to account for alt-refs
|
||||
hist->samples = cfg->rc_buf_sz * 5 / 4 * fps->num / fps->den / 1000;
|
||||
|
||||
// prevent division by zero
|
||||
if (hist->samples == 0) hist->samples = 1;
|
||||
|
||||
hist->frames = 0;
|
||||
hist->total = 0;
|
||||
|
||||
hist->pts = calloc(hist->samples, sizeof(*hist->pts));
|
||||
hist->sz = calloc(hist->samples, sizeof(*hist->sz));
|
||||
for (i = 0; i < RATE_BINS; i++) {
|
||||
hist->bucket[i].low = INT_MAX;
|
||||
hist->bucket[i].high = 0;
|
||||
hist->bucket[i].count = 0;
|
||||
}
|
||||
|
||||
return hist;
|
||||
}
|
||||
|
||||
void destroy_rate_histogram(struct rate_hist *hist) {
|
||||
if (hist) {
|
||||
free(hist->pts);
|
||||
free(hist->sz);
|
||||
free(hist);
|
||||
}
|
||||
}
|
||||
|
||||
void update_rate_histogram(struct rate_hist *hist,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_codec_cx_pkt_t *pkt) {
|
||||
int i;
|
||||
int64_t then = 0;
|
||||
int64_t avg_bitrate = 0;
|
||||
int64_t sum_sz = 0;
|
||||
const int64_t now = pkt->data.frame.pts * 1000 *
|
||||
(uint64_t)cfg->g_timebase.num /
|
||||
(uint64_t)cfg->g_timebase.den;
|
||||
|
||||
int idx;
|
||||
|
||||
if (hist == NULL || cfg == NULL || pkt == NULL) return;
|
||||
|
||||
idx = hist->frames++ % hist->samples;
|
||||
hist->pts[idx] = now;
|
||||
hist->sz[idx] = (int)pkt->data.frame.sz;
|
||||
|
||||
if (now < cfg->rc_buf_initial_sz) return;
|
||||
|
||||
if (!cfg->rc_target_bitrate) return;
|
||||
|
||||
then = now;
|
||||
|
||||
/* Sum the size over the past rc_buf_sz ms */
|
||||
for (i = hist->frames; i > 0 && hist->frames - i < hist->samples; i--) {
|
||||
const int i_idx = (i - 1) % hist->samples;
|
||||
|
||||
then = hist->pts[i_idx];
|
||||
if (now - then > cfg->rc_buf_sz) break;
|
||||
sum_sz += hist->sz[i_idx];
|
||||
}
|
||||
|
||||
if (now == then) return;
|
||||
|
||||
avg_bitrate = sum_sz * 8 * 1000 / (now - then);
|
||||
idx = (int)(avg_bitrate * (RATE_BINS / 2) / (cfg->rc_target_bitrate * 1000));
|
||||
if (idx < 0) idx = 0;
|
||||
if (idx > RATE_BINS - 1) idx = RATE_BINS - 1;
|
||||
if (hist->bucket[idx].low > avg_bitrate)
|
||||
hist->bucket[idx].low = (int)avg_bitrate;
|
||||
if (hist->bucket[idx].high < avg_bitrate)
|
||||
hist->bucket[idx].high = (int)avg_bitrate;
|
||||
hist->bucket[idx].count++;
|
||||
hist->total++;
|
||||
}
|
||||
|
||||
static int merge_hist_buckets(struct hist_bucket *bucket, int max_buckets,
|
||||
int *num_buckets) {
|
||||
int small_bucket = 0, merge_bucket = INT_MAX, big_bucket = 0;
|
||||
int buckets;
|
||||
int i;
|
||||
|
||||
assert(bucket != NULL);
|
||||
assert(num_buckets != NULL);
|
||||
|
||||
buckets = *num_buckets;
|
||||
|
||||
/* Find the extrema for this list of buckets */
|
||||
big_bucket = small_bucket = 0;
|
||||
for (i = 0; i < buckets; i++) {
|
||||
if (bucket[i].count < bucket[small_bucket].count) small_bucket = i;
|
||||
if (bucket[i].count > bucket[big_bucket].count) big_bucket = i;
|
||||
}
|
||||
|
||||
/* If we have too many buckets, merge the smallest with an adjacent
|
||||
* bucket.
|
||||
*/
|
||||
while (buckets > max_buckets) {
|
||||
int last_bucket = buckets - 1;
|
||||
|
||||
/* merge the small bucket with an adjacent one. */
|
||||
if (small_bucket == 0)
|
||||
merge_bucket = 1;
|
||||
else if (small_bucket == last_bucket)
|
||||
merge_bucket = last_bucket - 1;
|
||||
else if (bucket[small_bucket - 1].count < bucket[small_bucket + 1].count)
|
||||
merge_bucket = small_bucket - 1;
|
||||
else
|
||||
merge_bucket = small_bucket + 1;
|
||||
|
||||
assert(abs(merge_bucket - small_bucket) <= 1);
|
||||
assert(small_bucket < buckets);
|
||||
assert(big_bucket < buckets);
|
||||
assert(merge_bucket < buckets);
|
||||
|
||||
if (merge_bucket < small_bucket) {
|
||||
bucket[merge_bucket].high = bucket[small_bucket].high;
|
||||
bucket[merge_bucket].count += bucket[small_bucket].count;
|
||||
} else {
|
||||
bucket[small_bucket].high = bucket[merge_bucket].high;
|
||||
bucket[small_bucket].count += bucket[merge_bucket].count;
|
||||
merge_bucket = small_bucket;
|
||||
}
|
||||
|
||||
assert(bucket[merge_bucket].low != bucket[merge_bucket].high);
|
||||
|
||||
buckets--;
|
||||
|
||||
/* Remove the merge_bucket from the list, and find the new small
|
||||
* and big buckets while we're at it
|
||||
*/
|
||||
big_bucket = small_bucket = 0;
|
||||
for (i = 0; i < buckets; i++) {
|
||||
if (i > merge_bucket) bucket[i] = bucket[i + 1];
|
||||
|
||||
if (bucket[i].count < bucket[small_bucket].count) small_bucket = i;
|
||||
if (bucket[i].count > bucket[big_bucket].count) big_bucket = i;
|
||||
}
|
||||
}
|
||||
|
||||
*num_buckets = buckets;
|
||||
return bucket[big_bucket].count;
|
||||
}
|
||||
|
||||
static void show_histogram(const struct hist_bucket *bucket, int buckets,
|
||||
int total, int scale) {
|
||||
const char *pat1, *pat2;
|
||||
int i;
|
||||
|
||||
assert(bucket != NULL);
|
||||
|
||||
switch ((int)(log(bucket[buckets - 1].high) / log(10)) + 1) {
|
||||
case 1:
|
||||
case 2:
|
||||
pat1 = "%4d %2s: ";
|
||||
pat2 = "%4d-%2d: ";
|
||||
break;
|
||||
case 3:
|
||||
pat1 = "%5d %3s: ";
|
||||
pat2 = "%5d-%3d: ";
|
||||
break;
|
||||
case 4:
|
||||
pat1 = "%6d %4s: ";
|
||||
pat2 = "%6d-%4d: ";
|
||||
break;
|
||||
case 5:
|
||||
pat1 = "%7d %5s: ";
|
||||
pat2 = "%7d-%5d: ";
|
||||
break;
|
||||
case 6:
|
||||
pat1 = "%8d %6s: ";
|
||||
pat2 = "%8d-%6d: ";
|
||||
break;
|
||||
case 7:
|
||||
pat1 = "%9d %7s: ";
|
||||
pat2 = "%9d-%7d: ";
|
||||
break;
|
||||
default:
|
||||
pat1 = "%12d %10s: ";
|
||||
pat2 = "%12d-%10d: ";
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < buckets; i++) {
|
||||
int len;
|
||||
int j;
|
||||
float pct;
|
||||
|
||||
pct = (float)(100.0 * bucket[i].count / total);
|
||||
len = HIST_BAR_MAX * bucket[i].count / scale;
|
||||
if (len < 1) len = 1;
|
||||
assert(len <= HIST_BAR_MAX);
|
||||
|
||||
if (bucket[i].low == bucket[i].high)
|
||||
fprintf(stderr, pat1, bucket[i].low, "");
|
||||
else
|
||||
fprintf(stderr, pat2, bucket[i].low, bucket[i].high);
|
||||
|
||||
for (j = 0; j < HIST_BAR_MAX; j++) fprintf(stderr, j < len ? "=" : " ");
|
||||
fprintf(stderr, "\t%5d (%6.2f%%)\n", bucket[i].count, pct);
|
||||
}
|
||||
}
|
||||
|
||||
void show_q_histogram(const int counts[64], int max_buckets) {
|
||||
struct hist_bucket bucket[64];
|
||||
int buckets = 0;
|
||||
int total = 0;
|
||||
int scale;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (counts[i]) {
|
||||
bucket[buckets].low = bucket[buckets].high = i;
|
||||
bucket[buckets].count = counts[i];
|
||||
buckets++;
|
||||
total += counts[i];
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nQuantizer Selection:\n");
|
||||
scale = merge_hist_buckets(bucket, max_buckets, &buckets);
|
||||
show_histogram(bucket, buckets, total, scale);
|
||||
}
|
||||
|
||||
void show_rate_histogram(struct rate_hist *hist, const vpx_codec_enc_cfg_t *cfg,
|
||||
int max_buckets) {
|
||||
int i, scale;
|
||||
int buckets = 0;
|
||||
|
||||
if (hist == NULL || cfg == NULL) return;
|
||||
|
||||
for (i = 0; i < RATE_BINS; i++) {
|
||||
if (hist->bucket[i].low == INT_MAX) continue;
|
||||
hist->bucket[buckets++] = hist->bucket[i];
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nRate (over %dms window):\n", cfg->rc_buf_sz);
|
||||
scale = merge_hist_buckets(hist->bucket, max_buckets, &buckets);
|
||||
show_histogram(hist->bucket, buckets, hist->total, scale);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef VPX_RATE_HIST_H_
|
||||
#define VPX_RATE_HIST_H_
|
||||
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct rate_hist;
|
||||
|
||||
struct rate_hist *init_rate_histogram(const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_rational_t *fps);
|
||||
|
||||
void destroy_rate_histogram(struct rate_hist *hist);
|
||||
|
||||
void update_rate_histogram(struct rate_hist *hist,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
const vpx_codec_cx_pkt_t *pkt);
|
||||
|
||||
void show_q_histogram(const int counts[64], int max_buckets);
|
||||
|
||||
void show_rate_histogram(struct rate_hist *hist, const vpx_codec_enc_cfg_t *cfg,
|
||||
int max_buckets);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // VPX_RATE_HIST_H_
|
||||
@@ -0,0 +1,31 @@
|
||||
##
|
||||
## Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
##
|
||||
## Use of this source code is governed by a BSD-style license
|
||||
## that can be found in the LICENSE file in the root of the source
|
||||
## tree. An additional intellectual property rights grant can be found
|
||||
## in the file PATENTS. All contributing project authors may
|
||||
## be found in the AUTHORS file in the root of the source tree.
|
||||
##
|
||||
|
||||
# libvpx reverse dependencies (targets that depend on libvpx)
|
||||
VPX_NONDEPS=$(addsuffix .$(VCPROJ_SFX),vpx gtest)
|
||||
VPX_RDEPS=$(foreach vcp,\
|
||||
$(filter-out $(VPX_NONDEPS),$^), --dep=$(vcp:.$(VCPROJ_SFX)=):vpx)
|
||||
|
||||
vpx.sln: $(wildcard *.$(VCPROJ_SFX))
|
||||
@echo " [CREATE] $@"
|
||||
$(SRC_PATH_BARE)/build/make/gen_msvs_sln.sh \
|
||||
$(if $(filter vpx.$(VCPROJ_SFX),$^),$(VPX_RDEPS)) \
|
||||
--dep=test_libvpx:gtest \
|
||||
--ver=$(CONFIG_VS_VERSION)\
|
||||
--out=$@ $^
|
||||
vpx.sln.mk: vpx.sln
|
||||
@true
|
||||
|
||||
PROJECTS-yes += vpx.sln vpx.sln.mk
|
||||
-include vpx.sln.mk
|
||||
|
||||
# Always install this file, as it is an unconditional post-build rule.
|
||||
INSTALL_MAPS += src/% $(SRC_PATH_BARE)/%
|
||||
INSTALL-SRCS-yes += $(target).mk
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 VPX_TEST_ACM_RANDOM_H_
|
||||
#define VPX_TEST_ACM_RANDOM_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "vpx/vpx_integer.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
|
||||
class ACMRandom {
|
||||
public:
|
||||
ACMRandom() : random_(DeterministicSeed()) {}
|
||||
|
||||
explicit ACMRandom(int seed) : random_(seed) {}
|
||||
|
||||
void Reset(int seed) { random_.Reseed(seed); }
|
||||
uint16_t Rand16(void) {
|
||||
const uint32_t value =
|
||||
random_.Generate(testing::internal::Random::kMaxRange);
|
||||
return (value >> 15) & 0xffff;
|
||||
}
|
||||
|
||||
int32_t Rand20Signed(void) {
|
||||
// Use 20 bits: values between 524287 and -524288.
|
||||
const uint32_t value = random_.Generate(1048576);
|
||||
return static_cast<int32_t>(value) - 524288;
|
||||
}
|
||||
|
||||
int16_t Rand16Signed(void) {
|
||||
// Use 16 bits: values between 32767 and -32768.
|
||||
return static_cast<int16_t>(random_.Generate(65536));
|
||||
}
|
||||
|
||||
int16_t Rand13Signed(void) {
|
||||
// Use 13 bits: values between 4095 and -4096.
|
||||
const uint32_t value = random_.Generate(8192);
|
||||
return static_cast<int16_t>(value) - 4096;
|
||||
}
|
||||
|
||||
int16_t Rand9Signed(void) {
|
||||
// Use 9 bits: values between 255 (0x0FF) and -256 (0x100).
|
||||
const uint32_t value = random_.Generate(512);
|
||||
return static_cast<int16_t>(value) - 256;
|
||||
}
|
||||
|
||||
uint8_t Rand8(void) {
|
||||
const uint32_t value =
|
||||
random_.Generate(testing::internal::Random::kMaxRange);
|
||||
// There's a bit more entropy in the upper bits of this implementation.
|
||||
return (value >> 23) & 0xff;
|
||||
}
|
||||
|
||||
uint8_t Rand8Extremes(void) {
|
||||
// Returns a random value near 0 or near 255, to better exercise
|
||||
// saturation behavior.
|
||||
const uint8_t r = Rand8();
|
||||
return static_cast<uint8_t>((r < 128) ? r << 4 : r >> 4);
|
||||
}
|
||||
|
||||
uint32_t RandRange(const uint32_t range) {
|
||||
// testing::internal::Random::Generate provides values in the range
|
||||
// testing::internal::Random::kMaxRange.
|
||||
assert(range <= testing::internal::Random::kMaxRange);
|
||||
return random_.Generate(range);
|
||||
}
|
||||
|
||||
int PseudoUniform(int range) { return random_.Generate(range); }
|
||||
|
||||
int operator()(int n) { return PseudoUniform(n); }
|
||||
|
||||
static int DeterministicSeed(void) { return 0xbaba; }
|
||||
|
||||
private:
|
||||
testing::internal::Random random_;
|
||||
};
|
||||
|
||||
} // namespace libvpx_test
|
||||
|
||||
#endif // VPX_TEST_ACM_RANDOM_H_
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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 <algorithm>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/util.h"
|
||||
#include "test/y4m_video_source.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Check if any pixel in a 16x16 macroblock varies between frames.
|
||||
int CheckMb(const vpx_image_t ¤t, const vpx_image_t &previous, int mb_r,
|
||||
int mb_c) {
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
int r = 16 * mb_r;
|
||||
int c0 = 16 * mb_c;
|
||||
int r_top = std::min(r + 16, static_cast<int>(current.d_h));
|
||||
int c_top = std::min(c0 + 16, static_cast<int>(current.d_w));
|
||||
r = std::max(r, 0);
|
||||
c0 = std::max(c0, 0);
|
||||
if (plane > 0 && current.x_chroma_shift) {
|
||||
c_top = (c_top + 1) >> 1;
|
||||
c0 >>= 1;
|
||||
}
|
||||
if (plane > 0 && current.y_chroma_shift) {
|
||||
r_top = (r_top + 1) >> 1;
|
||||
r >>= 1;
|
||||
}
|
||||
for (; r < r_top; ++r) {
|
||||
for (int c = c0; c < c_top; ++c) {
|
||||
if (current.planes[plane][current.stride[plane] * r + c] !=
|
||||
previous.planes[plane][previous.stride[plane] * r + c]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GenerateMap(int mb_rows, int mb_cols, const vpx_image_t ¤t,
|
||||
const vpx_image_t &previous, uint8_t *map) {
|
||||
for (int mb_r = 0; mb_r < mb_rows; ++mb_r) {
|
||||
for (int mb_c = 0; mb_c < mb_cols; ++mb_c) {
|
||||
map[mb_r * mb_cols + mb_c] = CheckMb(current, previous, mb_r, mb_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int kAqModeCyclicRefresh = 3;
|
||||
|
||||
class ActiveMapRefreshTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
ActiveMapRefreshTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
virtual ~ActiveMapRefreshTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
cpu_used_ = GET_PARAM(2);
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
::libvpx_test::Y4mVideoSource *y4m_video =
|
||||
static_cast<libvpx_test::Y4mVideoSource *>(video);
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, cpu_used_);
|
||||
encoder->Control(VP9E_SET_AQ_MODE, kAqModeCyclicRefresh);
|
||||
} else if (video->frame() >= 2 && video->img()) {
|
||||
vpx_image_t *current = video->img();
|
||||
vpx_image_t *previous = y4m_holder_->img();
|
||||
ASSERT_TRUE(previous != NULL);
|
||||
vpx_active_map_t map = vpx_active_map_t();
|
||||
const int width = static_cast<int>(current->d_w);
|
||||
const int height = static_cast<int>(current->d_h);
|
||||
const int mb_width = (width + 15) / 16;
|
||||
const int mb_height = (height + 15) / 16;
|
||||
uint8_t *active_map = new uint8_t[mb_width * mb_height];
|
||||
GenerateMap(mb_height, mb_width, *current, *previous, active_map);
|
||||
map.cols = mb_width;
|
||||
map.rows = mb_height;
|
||||
map.active_map = active_map;
|
||||
encoder->Control(VP8E_SET_ACTIVEMAP, &map);
|
||||
delete[] active_map;
|
||||
}
|
||||
if (video->img()) {
|
||||
y4m_video->SwapBuffers(y4m_holder_);
|
||||
}
|
||||
}
|
||||
|
||||
int cpu_used_;
|
||||
::libvpx_test::Y4mVideoSource *y4m_holder_;
|
||||
};
|
||||
|
||||
TEST_P(ActiveMapRefreshTest, Test) {
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.g_profile = 1;
|
||||
cfg_.rc_target_bitrate = 600;
|
||||
cfg_.rc_resize_allowed = 0;
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 30;
|
||||
cfg_.g_pass = VPX_RC_ONE_PASS;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.kf_max_dist = 90000;
|
||||
|
||||
::libvpx_test::Y4mVideoSource video("desktop_credits.y4m", 0, 30);
|
||||
::libvpx_test::Y4mVideoSource video_holder("desktop_credits.y4m", 0, 30);
|
||||
video_holder.Begin();
|
||||
y4m_holder_ = &video_holder;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(ActiveMapRefreshTest,
|
||||
::testing::Values(::libvpx_test::kRealTime),
|
||||
::testing::Range(5, 6));
|
||||
} // namespace
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <climits>
|
||||
#include <vector>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class ActiveMapTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
static const int kWidth = 208;
|
||||
static const int kHeight = 144;
|
||||
|
||||
ActiveMapTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
virtual ~ActiveMapTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
cpu_used_ = GET_PARAM(2);
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, cpu_used_);
|
||||
} else if (video->frame() == 3) {
|
||||
vpx_active_map_t map = vpx_active_map_t();
|
||||
/* clang-format off */
|
||||
uint8_t active_map[9 * 13] = {
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1,
|
||||
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
|
||||
};
|
||||
/* clang-format on */
|
||||
map.cols = (kWidth + 15) / 16;
|
||||
map.rows = (kHeight + 15) / 16;
|
||||
ASSERT_EQ(map.cols, 13u);
|
||||
ASSERT_EQ(map.rows, 9u);
|
||||
map.active_map = active_map;
|
||||
encoder->Control(VP8E_SET_ACTIVEMAP, &map);
|
||||
} else if (video->frame() == 15) {
|
||||
vpx_active_map_t map = vpx_active_map_t();
|
||||
map.cols = (kWidth + 15) / 16;
|
||||
map.rows = (kHeight + 15) / 16;
|
||||
map.active_map = NULL;
|
||||
encoder->Control(VP8E_SET_ACTIVEMAP, &map);
|
||||
}
|
||||
}
|
||||
|
||||
int cpu_used_;
|
||||
};
|
||||
|
||||
TEST_P(ActiveMapTest, Test) {
|
||||
// Validate that this non multiple of 64 wide clip encodes
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_target_bitrate = 400;
|
||||
cfg_.rc_resize_allowed = 0;
|
||||
cfg_.g_pass = VPX_RC_ONE_PASS;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.kf_max_dist = 90000;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", kWidth, kHeight, 30, 1,
|
||||
0, 20);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(ActiveMapTest,
|
||||
::testing::Values(::libvpx_test::kRealTime),
|
||||
::testing::Range(0, 9));
|
||||
} // namespace
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 <math.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_dsp/postproc.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
|
||||
namespace {
|
||||
|
||||
static const int kNoiseSize = 3072;
|
||||
|
||||
// TODO(jimbankoski): make width and height integers not unsigned.
|
||||
typedef void (*AddNoiseFunc)(uint8_t *start, const int8_t *noise,
|
||||
int blackclamp, int whiteclamp, int width,
|
||||
int height, int pitch);
|
||||
|
||||
typedef std::tuple<double, AddNoiseFunc> AddNoiseTestFPParam;
|
||||
|
||||
class AddNoiseTest : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<AddNoiseTestFPParam> {
|
||||
public:
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
virtual ~AddNoiseTest() {}
|
||||
};
|
||||
|
||||
double stddev6(char a, char b, char c, char d, char e, char f) {
|
||||
const double n = (a + b + c + d + e + f) / 6.0;
|
||||
const double v = ((a - n) * (a - n) + (b - n) * (b - n) + (c - n) * (c - n) +
|
||||
(d - n) * (d - n) + (e - n) * (e - n) + (f - n) * (f - n)) /
|
||||
6.0;
|
||||
return sqrt(v);
|
||||
}
|
||||
|
||||
TEST_P(AddNoiseTest, CheckNoiseAdded) {
|
||||
const int width = 64;
|
||||
const int height = 64;
|
||||
const int image_size = width * height;
|
||||
int8_t noise[kNoiseSize];
|
||||
const int clamp = vpx_setup_noise(GET_PARAM(0), noise, kNoiseSize);
|
||||
uint8_t *const s =
|
||||
reinterpret_cast<uint8_t *>(vpx_calloc(image_size, sizeof(*s)));
|
||||
ASSERT_TRUE(s != NULL);
|
||||
memset(s, 99, image_size * sizeof(*s));
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
GET_PARAM(1)(s, noise, clamp, clamp, width, height, width));
|
||||
|
||||
// Check to make sure we don't end up having either the same or no added
|
||||
// noise either vertically or horizontally.
|
||||
for (int i = 0; i < image_size - 6 * width - 6; ++i) {
|
||||
const double hd = stddev6(s[i] - 99, s[i + 1] - 99, s[i + 2] - 99,
|
||||
s[i + 3] - 99, s[i + 4] - 99, s[i + 5] - 99);
|
||||
const double vd = stddev6(s[i] - 99, s[i + width] - 99,
|
||||
s[i + 2 * width] - 99, s[i + 3 * width] - 99,
|
||||
s[i + 4 * width] - 99, s[i + 5 * width] - 99);
|
||||
|
||||
EXPECT_NE(hd, 0);
|
||||
EXPECT_NE(vd, 0);
|
||||
}
|
||||
|
||||
// Initialize pixels in the image to 255 and check for roll over.
|
||||
memset(s, 255, image_size);
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
GET_PARAM(1)(s, noise, clamp, clamp, width, height, width));
|
||||
|
||||
// Check to make sure don't roll over.
|
||||
for (int i = 0; i < image_size; ++i) {
|
||||
EXPECT_GT(static_cast<int>(s[i]), clamp) << "i = " << i;
|
||||
}
|
||||
|
||||
// Initialize pixels in the image to 0 and check for roll under.
|
||||
memset(s, 0, image_size);
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
GET_PARAM(1)(s, noise, clamp, clamp, width, height, width));
|
||||
|
||||
// Check to make sure don't roll under.
|
||||
for (int i = 0; i < image_size; ++i) {
|
||||
EXPECT_LT(static_cast<int>(s[i]), 255 - clamp) << "i = " << i;
|
||||
}
|
||||
|
||||
vpx_free(s);
|
||||
}
|
||||
|
||||
TEST_P(AddNoiseTest, CheckCvsAssembly) {
|
||||
const int width = 64;
|
||||
const int height = 64;
|
||||
const int image_size = width * height;
|
||||
int8_t noise[kNoiseSize];
|
||||
const int clamp = vpx_setup_noise(4.4, noise, kNoiseSize);
|
||||
|
||||
uint8_t *const s = reinterpret_cast<uint8_t *>(vpx_calloc(image_size, 1));
|
||||
uint8_t *const d = reinterpret_cast<uint8_t *>(vpx_calloc(image_size, 1));
|
||||
ASSERT_TRUE(s != NULL);
|
||||
ASSERT_TRUE(d != NULL);
|
||||
|
||||
memset(s, 99, image_size);
|
||||
memset(d, 99, image_size);
|
||||
|
||||
srand(0);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
GET_PARAM(1)(s, noise, clamp, clamp, width, height, width));
|
||||
srand(0);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
vpx_plane_add_noise_c(d, noise, clamp, clamp, width, height, width));
|
||||
|
||||
for (int i = 0; i < image_size; ++i) {
|
||||
EXPECT_EQ(static_cast<int>(s[i]), static_cast<int>(d[i])) << "i = " << i;
|
||||
}
|
||||
|
||||
vpx_free(d);
|
||||
vpx_free(s);
|
||||
}
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, AddNoiseTest,
|
||||
::testing::Values(make_tuple(3.25, vpx_plane_add_noise_c),
|
||||
make_tuple(4.4, vpx_plane_add_noise_c)));
|
||||
|
||||
#if HAVE_SSE2
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, AddNoiseTest,
|
||||
::testing::Values(make_tuple(3.25, vpx_plane_add_noise_sse2),
|
||||
make_tuple(4.4, vpx_plane_add_noise_sse2)));
|
||||
#endif
|
||||
|
||||
#if HAVE_MSA
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, AddNoiseTest,
|
||||
::testing::Values(make_tuple(3.25, vpx_plane_add_noise_msa),
|
||||
make_tuple(4.4, vpx_plane_add_noise_msa)));
|
||||
#endif
|
||||
} // namespace
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class AltRefAqSegmentTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
AltRefAqSegmentTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
virtual ~AltRefAqSegmentTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
set_cpu_used_ = GET_PARAM(2);
|
||||
aq_mode_ = 0;
|
||||
alt_ref_aq_mode_ = 0;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
|
||||
encoder->Control(VP9E_SET_ALT_REF_AQ, alt_ref_aq_mode_);
|
||||
encoder->Control(VP9E_SET_AQ_MODE, aq_mode_);
|
||||
encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 100);
|
||||
}
|
||||
}
|
||||
|
||||
int set_cpu_used_;
|
||||
int aq_mode_;
|
||||
int alt_ref_aq_mode_;
|
||||
};
|
||||
|
||||
// Validate that this ALT_REF_AQ/AQ segmentation mode
|
||||
// (ALT_REF_AQ=0, AQ=0/no_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AltRefAqSegmentTest, TestNoMisMatchAltRefAQ0) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 0;
|
||||
alt_ref_aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this ALT_REF_AQ/AQ segmentation mode
|
||||
// (ALT_REF_AQ=0, AQ=1/variance_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AltRefAqSegmentTest, TestNoMisMatchAltRefAQ1) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 1;
|
||||
alt_ref_aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this ALT_REF_AQ/AQ segmentation mode
|
||||
// (ALT_REF_AQ=0, AQ=2/complexity_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AltRefAqSegmentTest, TestNoMisMatchAltRefAQ2) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 2;
|
||||
alt_ref_aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this ALT_REF_AQ/AQ segmentation mode
|
||||
// (ALT_REF_AQ=0, AQ=3/cyclicrefresh_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AltRefAqSegmentTest, TestNoMisMatchAltRefAQ3) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 3;
|
||||
alt_ref_aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this ALT_REF_AQ/AQ segmentation mode
|
||||
// (ALT_REF_AQ=0, AQ=4/equator360_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AltRefAqSegmentTest, TestNoMisMatchAltRefAQ4) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 4;
|
||||
alt_ref_aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(AltRefAqSegmentTest,
|
||||
::testing::Values(::libvpx_test::kOnePassGood,
|
||||
::libvpx_test::kTwoPassGood),
|
||||
::testing::Range(2, 5));
|
||||
} // namespace
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
namespace {
|
||||
|
||||
#if CONFIG_VP8_ENCODER
|
||||
|
||||
// lookahead range: [kLookAheadMin, kLookAheadMax).
|
||||
const int kLookAheadMin = 5;
|
||||
const int kLookAheadMax = 26;
|
||||
|
||||
class AltRefTest : public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<int> {
|
||||
protected:
|
||||
AltRefTest() : EncoderTest(GET_PARAM(0)), altref_count_(0) {}
|
||||
virtual ~AltRefTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(libvpx_test::kTwoPassGood);
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) { altref_count_ = 0; }
|
||||
|
||||
virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
|
||||
libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
|
||||
encoder->Control(VP8E_SET_CPUUSED, 3);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
if (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) ++altref_count_;
|
||||
}
|
||||
|
||||
int altref_count() const { return altref_count_; }
|
||||
|
||||
private:
|
||||
int altref_count_;
|
||||
};
|
||||
|
||||
TEST_P(AltRefTest, MonotonicTimestamps) {
|
||||
const vpx_rational timebase = { 33333333, 1000000000 };
|
||||
cfg_.g_timebase = timebase;
|
||||
cfg_.rc_target_bitrate = 1000;
|
||||
cfg_.g_lag_in_frames = GET_PARAM(1);
|
||||
|
||||
libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
timebase.den, timebase.num, 0, 30);
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
EXPECT_GE(altref_count(), 1);
|
||||
}
|
||||
|
||||
VP8_INSTANTIATE_TEST_CASE(AltRefTest,
|
||||
::testing::Range(kLookAheadMin, kLookAheadMax));
|
||||
|
||||
#endif // CONFIG_VP8_ENCODER
|
||||
|
||||
class AltRefForcedKeyTestLarge
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
AltRefForcedKeyTestLarge()
|
||||
: EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
|
||||
cpu_used_(GET_PARAM(2)), forced_kf_frame_num_(1), frame_num_(0) {}
|
||||
virtual ~AltRefForcedKeyTestLarge() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(encoding_mode_);
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.g_threads = 0;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, cpu_used_);
|
||||
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
|
||||
#if CONFIG_VP9_ENCODER
|
||||
// override test default for tile columns if necessary.
|
||||
if (GET_PARAM(0) == &libvpx_test::kVP9) {
|
||||
encoder->Control(VP9E_SET_TILE_COLUMNS, 6);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
frame_flags_ =
|
||||
(video->frame() == forced_kf_frame_num_) ? VPX_EFLAG_FORCE_KF : 0;
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
if (frame_num_ == forced_kf_frame_num_) {
|
||||
ASSERT_TRUE(!!(pkt->data.frame.flags & VPX_FRAME_IS_KEY))
|
||||
<< "Frame #" << frame_num_ << " isn't a keyframe!";
|
||||
}
|
||||
++frame_num_;
|
||||
}
|
||||
|
||||
::libvpx_test::TestMode encoding_mode_;
|
||||
int cpu_used_;
|
||||
unsigned int forced_kf_frame_num_;
|
||||
unsigned int frame_num_;
|
||||
};
|
||||
|
||||
TEST_P(AltRefForcedKeyTestLarge, Frame1IsKey) {
|
||||
const vpx_rational timebase = { 1, 30 };
|
||||
const int lag_values[] = { 3, 15, 25, -1 };
|
||||
|
||||
forced_kf_frame_num_ = 1;
|
||||
for (int i = 0; lag_values[i] != -1; ++i) {
|
||||
frame_num_ = 0;
|
||||
cfg_.g_lag_in_frames = lag_values[i];
|
||||
libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
timebase.den, timebase.num, 0, 30);
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AltRefForcedKeyTestLarge, ForcedFrameIsKey) {
|
||||
const vpx_rational timebase = { 1, 30 };
|
||||
const int lag_values[] = { 3, 15, 25, -1 };
|
||||
|
||||
for (int i = 0; lag_values[i] != -1; ++i) {
|
||||
frame_num_ = 0;
|
||||
forced_kf_frame_num_ = lag_values[i] - 1;
|
||||
cfg_.g_lag_in_frames = lag_values[i];
|
||||
libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
timebase.den, timebase.num, 0, 30);
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
}
|
||||
|
||||
VP8_INSTANTIATE_TEST_CASE(AltRefForcedKeyTestLarge,
|
||||
::testing::Values(::libvpx_test::kOnePassGood),
|
||||
::testing::Range(0, 9));
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(AltRefForcedKeyTestLarge,
|
||||
::testing::Values(::libvpx_test::kOnePassGood),
|
||||
::testing::Range(0, 9));
|
||||
} // namespace
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
#
|
||||
# This make file builds vpx_test app for android.
|
||||
# The test app itself runs on the command line through adb shell
|
||||
# The paths are really messed up as the libvpx make file
|
||||
# expects to be made from a parent directory.
|
||||
CUR_WD := $(call my-dir)
|
||||
BINDINGS_DIR := $(CUR_WD)/../../..
|
||||
LOCAL_PATH := $(CUR_WD)/../../..
|
||||
|
||||
#libwebm
|
||||
include $(CLEAR_VARS)
|
||||
include $(BINDINGS_DIR)/libvpx/third_party/libwebm/Android.mk
|
||||
LOCAL_PATH := $(CUR_WD)/../../..
|
||||
|
||||
#libvpx
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_STATIC_LIBRARIES := libwebm
|
||||
include $(BINDINGS_DIR)/libvpx/build/make/Android.mk
|
||||
LOCAL_PATH := $(CUR_WD)/../..
|
||||
|
||||
#libgtest
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
LOCAL_MODULE := gtest
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/third_party/googletest/src/
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/third_party/googletest/src/include/
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/third_party/googletest/src/include/
|
||||
LOCAL_SRC_FILES := ./third_party/googletest/src/src/gtest-all.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
#libvpx_test
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_MODULE := libvpx_test
|
||||
LOCAL_STATIC_LIBRARIES := gtest libwebm
|
||||
|
||||
ifeq ($(ENABLE_SHARED),1)
|
||||
LOCAL_SHARED_LIBRARIES := vpx
|
||||
else
|
||||
LOCAL_STATIC_LIBRARIES += vpx
|
||||
endif
|
||||
|
||||
include $(LOCAL_PATH)/test/test.mk
|
||||
LOCAL_C_INCLUDES := $(BINDINGS_DIR)
|
||||
FILTERED_SRC := $(sort $(filter %.cc %.c, $(LIBVPX_TEST_SRCS-yes)))
|
||||
LOCAL_SRC_FILES := $(addprefix ./test/, $(FILTERED_SRC))
|
||||
# some test files depend on *_rtcd.h, ensure they're generated first.
|
||||
$(eval $(call rtcd_dep_template))
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -0,0 +1,33 @@
|
||||
Android.mk will build vpx unittests on android.
|
||||
1) Configure libvpx from the parent directory:
|
||||
./libvpx/configure --target=armv7-android-gcc --enable-external-build \
|
||||
--enable-postproc --disable-install-srcs --enable-multi-res-encoding \
|
||||
--enable-temporal-denoising --disable-unit-tests --disable-install-docs \
|
||||
--disable-examples --disable-runtime-cpu-detect
|
||||
|
||||
2) From the parent directory, invoke ndk-build:
|
||||
NDK_PROJECT_PATH=. ndk-build APP_BUILD_SCRIPT=./libvpx/test/android/Android.mk \
|
||||
APP_ABI=armeabi-v7a APP_PLATFORM=android-18 APP_OPTIM=release \
|
||||
APP_STL=c++_static
|
||||
|
||||
Note: Both adb and ndk-build are available at:
|
||||
https://developer.android.com/studio#downloads
|
||||
https://developer.android.com/ndk/downloads
|
||||
|
||||
3) Run get_files.py to download the test files:
|
||||
python get_files.py -i /path/to/test-data.sha1 -o /path/to/put/files \
|
||||
-u http://downloads.webmproject.org/test_data/libvpx
|
||||
|
||||
4) Transfer files to device using adb. Ensure you have proper permissions for
|
||||
the target
|
||||
|
||||
adb push /path/to/test_files /data/local/tmp
|
||||
adb push /path/to/built_libs /data/local/tmp
|
||||
|
||||
NOTE: Built_libs defaults to parent_dir/libs/armeabi-v7a
|
||||
|
||||
5) Run tests:
|
||||
adb shell
|
||||
(on device)
|
||||
cd /data/local/tmp
|
||||
LD_LIBRARY_PATH=. ./vpx_test
|
||||
@@ -0,0 +1,118 @@
|
||||
# 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.
|
||||
#
|
||||
# This simple script pulls test files from the webm homepage
|
||||
# It is intelligent enough to only pull files if
|
||||
# 1) File / test_data folder does not exist
|
||||
# 2) SHA mismatch
|
||||
|
||||
import pycurl
|
||||
import csv
|
||||
import hashlib
|
||||
import re
|
||||
import os.path
|
||||
import time
|
||||
import itertools
|
||||
import sys
|
||||
import getopt
|
||||
|
||||
#globals
|
||||
url = ''
|
||||
file_list_path = ''
|
||||
local_resource_path = ''
|
||||
|
||||
# Helper functions:
|
||||
# A simple function which returns the sha hash of a file in hex
|
||||
def get_file_sha(filename):
|
||||
try:
|
||||
sha_hash = hashlib.sha1()
|
||||
with open(filename, 'rb') as file:
|
||||
buf = file.read(HASH_CHUNK)
|
||||
while len(buf) > 0:
|
||||
sha_hash.update(buf)
|
||||
buf = file.read(HASH_CHUNK)
|
||||
return sha_hash.hexdigest()
|
||||
except IOError:
|
||||
print "Error reading " + filename
|
||||
|
||||
# Downloads a file from a url, and then checks the sha against the passed
|
||||
# in sha
|
||||
def download_and_check_sha(url, filename, sha):
|
||||
path = os.path.join(local_resource_path, filename)
|
||||
fp = open(path, "wb")
|
||||
curl = pycurl.Curl()
|
||||
curl.setopt(pycurl.URL, url + "/" + filename)
|
||||
curl.setopt(pycurl.WRITEDATA, fp)
|
||||
curl.perform()
|
||||
curl.close()
|
||||
fp.close()
|
||||
return get_file_sha(path) == sha
|
||||
|
||||
#constants
|
||||
ftp_retries = 3
|
||||
|
||||
SHA_COL = 0
|
||||
NAME_COL = 1
|
||||
EXPECTED_COL = 2
|
||||
HASH_CHUNK = 65536
|
||||
|
||||
# Main script
|
||||
try:
|
||||
opts, args = \
|
||||
getopt.getopt(sys.argv[1:], \
|
||||
"u:i:o:", ["url=", "input_csv=", "output_dir="])
|
||||
except:
|
||||
print 'get_files.py -u <url> -i <input_csv> -o <output_dir>'
|
||||
sys.exit(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-u':
|
||||
url = arg
|
||||
elif opt in ("-i", "--input_csv"):
|
||||
file_list_path = os.path.join(arg)
|
||||
elif opt in ("-o", "--output_dir"):
|
||||
local_resource_path = os.path.join(arg)
|
||||
|
||||
if len(sys.argv) != 7:
|
||||
print "Expects two paths and a url!"
|
||||
exit(1)
|
||||
|
||||
if not os.path.isdir(local_resource_path):
|
||||
os.makedirs(local_resource_path)
|
||||
|
||||
file_list_csv = open(file_list_path, "rb")
|
||||
|
||||
# Our 'csv' file uses multiple spaces as a delimiter, python's
|
||||
# csv class only uses single character delimiters, so we convert them below
|
||||
file_list_reader = csv.reader((re.sub(' +', ' ', line) \
|
||||
for line in file_list_csv), delimiter = ' ')
|
||||
|
||||
file_shas = []
|
||||
file_names = []
|
||||
|
||||
for row in file_list_reader:
|
||||
if len(row) != EXPECTED_COL:
|
||||
continue
|
||||
file_shas.append(row[SHA_COL])
|
||||
file_names.append(row[NAME_COL])
|
||||
|
||||
file_list_csv.close()
|
||||
|
||||
# Download files, only if they don't already exist and have correct shas
|
||||
for filename, sha in itertools.izip(file_names, file_shas):
|
||||
path = os.path.join(local_resource_path, filename)
|
||||
if os.path.isfile(path) \
|
||||
and get_file_sha(path) == sha:
|
||||
print path + ' exists, skipping'
|
||||
continue
|
||||
for retry in range(0, ftp_retries):
|
||||
print "Downloading " + path
|
||||
if not download_and_check_sha(url, filename, sha):
|
||||
print "Sha does not match, retrying..."
|
||||
else:
|
||||
break
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
"""Standalone script which parses a gtest log for json.
|
||||
|
||||
Json is returned returns as an array. This script is used by the libvpx
|
||||
waterfall to gather json results mixed in with gtest logs. This is
|
||||
dubious software engineering.
|
||||
"""
|
||||
|
||||
import getopt
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print "Expects a file to write json to!"
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
opts, _ = \
|
||||
getopt.getopt(sys.argv[1:], \
|
||||
'o:', ['output-json='])
|
||||
except getopt.GetOptError:
|
||||
print 'scrape_gtest_log.py -o <output_json>'
|
||||
sys.exit(2)
|
||||
|
||||
output_json = ''
|
||||
for opt, arg in opts:
|
||||
if opt in ('-o', '--output-json'):
|
||||
output_json = os.path.join(arg)
|
||||
|
||||
blob = sys.stdin.read()
|
||||
json_string = '[' + ','.join('{' + x + '}' for x in
|
||||
re.findall(r'{([^}]*.?)}', blob)) + ']'
|
||||
print blob
|
||||
|
||||
output = json.dumps(json.loads(json_string), indent=4, sort_keys=True)
|
||||
print output
|
||||
|
||||
path = os.path.dirname(output_json)
|
||||
if path and not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
outfile = open(output_json, 'w')
|
||||
outfile.write(output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class AqSegmentTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
AqSegmentTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
virtual ~AqSegmentTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
set_cpu_used_ = GET_PARAM(2);
|
||||
aq_mode_ = 0;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
|
||||
encoder->Control(VP9E_SET_AQ_MODE, aq_mode_);
|
||||
encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 100);
|
||||
}
|
||||
}
|
||||
|
||||
int set_cpu_used_;
|
||||
int aq_mode_;
|
||||
};
|
||||
|
||||
// Validate that this AQ segmentation mode (AQ=1, variance_ap)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AqSegmentTest, TestNoMisMatchAQ1) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 1;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this AQ segmentation mode (AQ=2, complexity_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AqSegmentTest, TestNoMisMatchAQ2) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 2;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
// Validate that this AQ segmentation mode (AQ=3, cyclic_refresh_aq)
|
||||
// encodes and decodes without a mismatch.
|
||||
TEST_P(AqSegmentTest, TestNoMisMatchAQ3) {
|
||||
cfg_.rc_min_quantizer = 8;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 500;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_target_bitrate = 300;
|
||||
|
||||
aq_mode_ = 3;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 100);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(AqSegmentTest,
|
||||
::testing::Values(::libvpx_test::kRealTime,
|
||||
::libvpx_test::kOnePassGood),
|
||||
::testing::Range(3, 9));
|
||||
} // namespace
|
||||
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
* 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 <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vp9_rtcd.h"
|
||||
#include "./vpx_config.h"
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
|
||||
#include "test/acm_random.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Pixel>
|
||||
class AverageTestBase : public ::testing::Test {
|
||||
public:
|
||||
AverageTestBase(int width, int height)
|
||||
: width_(width), height_(height), source_data_(NULL), source_stride_(0),
|
||||
bit_depth_(8) {}
|
||||
|
||||
virtual void TearDown() {
|
||||
vpx_free(source_data_);
|
||||
source_data_ = NULL;
|
||||
libvpx_test::ClearSystemState();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Handle blocks up to 4 blocks 64x64 with stride up to 128
|
||||
static const int kDataAlignment = 16;
|
||||
static const int kDataBlockSize = 64 * 128;
|
||||
|
||||
virtual void SetUp() {
|
||||
source_data_ = reinterpret_cast<Pixel *>(
|
||||
vpx_memalign(kDataAlignment, kDataBlockSize * sizeof(source_data_[0])));
|
||||
ASSERT_TRUE(source_data_ != NULL);
|
||||
source_stride_ = (width_ + 31) & ~31;
|
||||
bit_depth_ = 8;
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
}
|
||||
|
||||
// Sum Pixels
|
||||
static unsigned int ReferenceAverage8x8(const Pixel *source, int pitch) {
|
||||
unsigned int average = 0;
|
||||
for (int h = 0; h < 8; ++h) {
|
||||
for (int w = 0; w < 8; ++w) average += source[h * pitch + w];
|
||||
}
|
||||
return ((average + 32) >> 6);
|
||||
}
|
||||
|
||||
static unsigned int ReferenceAverage4x4(const Pixel *source, int pitch) {
|
||||
unsigned int average = 0;
|
||||
for (int h = 0; h < 4; ++h) {
|
||||
for (int w = 0; w < 4; ++w) average += source[h * pitch + w];
|
||||
}
|
||||
return ((average + 8) >> 4);
|
||||
}
|
||||
|
||||
void FillConstant(Pixel fill_constant) {
|
||||
for (int i = 0; i < width_ * height_; ++i) {
|
||||
source_data_[i] = fill_constant;
|
||||
}
|
||||
}
|
||||
|
||||
void FillRandom() {
|
||||
for (int i = 0; i < width_ * height_; ++i) {
|
||||
source_data_[i] = rnd_.Rand16() & ((1 << bit_depth_) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int width_, height_;
|
||||
Pixel *source_data_;
|
||||
int source_stride_;
|
||||
int bit_depth_;
|
||||
|
||||
ACMRandom rnd_;
|
||||
};
|
||||
typedef unsigned int (*AverageFunction)(const uint8_t *s, int pitch);
|
||||
|
||||
typedef std::tuple<int, int, int, int, AverageFunction> AvgFunc;
|
||||
|
||||
class AverageTest : public AverageTestBase<uint8_t>,
|
||||
public ::testing::WithParamInterface<AvgFunc> {
|
||||
public:
|
||||
AverageTest() : AverageTestBase(GET_PARAM(0), GET_PARAM(1)) {}
|
||||
|
||||
protected:
|
||||
void CheckAverages() {
|
||||
const int block_size = GET_PARAM(3);
|
||||
unsigned int expected = 0;
|
||||
if (block_size == 8) {
|
||||
expected =
|
||||
ReferenceAverage8x8(source_data_ + GET_PARAM(2), source_stride_);
|
||||
} else if (block_size == 4) {
|
||||
expected =
|
||||
ReferenceAverage4x4(source_data_ + GET_PARAM(2), source_stride_);
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
GET_PARAM(4)(source_data_ + GET_PARAM(2), source_stride_));
|
||||
unsigned int actual =
|
||||
GET_PARAM(4)(source_data_ + GET_PARAM(2), source_stride_);
|
||||
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
};
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
class AverageTestHBD : public AverageTestBase<uint16_t>,
|
||||
public ::testing::WithParamInterface<AvgFunc> {
|
||||
public:
|
||||
AverageTestHBD() : AverageTestBase(GET_PARAM(0), GET_PARAM(1)) {}
|
||||
|
||||
protected:
|
||||
void CheckAverages() {
|
||||
const int block_size = GET_PARAM(3);
|
||||
unsigned int expected = 0;
|
||||
if (block_size == 8) {
|
||||
expected =
|
||||
ReferenceAverage8x8(source_data_ + GET_PARAM(2), source_stride_);
|
||||
} else if (block_size == 4) {
|
||||
expected =
|
||||
ReferenceAverage4x4(source_data_ + GET_PARAM(2), source_stride_);
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(GET_PARAM(4)(
|
||||
CONVERT_TO_BYTEPTR(source_data_ + GET_PARAM(2)), source_stride_));
|
||||
unsigned int actual = GET_PARAM(4)(
|
||||
CONVERT_TO_BYTEPTR(source_data_ + GET_PARAM(2)), source_stride_);
|
||||
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
};
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
typedef void (*IntProRowFunc)(int16_t hbuf[16], uint8_t const *ref,
|
||||
const int ref_stride, const int height);
|
||||
|
||||
typedef std::tuple<int, IntProRowFunc, IntProRowFunc> IntProRowParam;
|
||||
|
||||
class IntProRowTest : public AverageTestBase<uint8_t>,
|
||||
public ::testing::WithParamInterface<IntProRowParam> {
|
||||
public:
|
||||
IntProRowTest()
|
||||
: AverageTestBase(16, GET_PARAM(0)), hbuf_asm_(NULL), hbuf_c_(NULL) {
|
||||
asm_func_ = GET_PARAM(1);
|
||||
c_func_ = GET_PARAM(2);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
source_data_ = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBlockSize * sizeof(source_data_[0])));
|
||||
ASSERT_TRUE(source_data_ != NULL);
|
||||
|
||||
hbuf_asm_ = reinterpret_cast<int16_t *>(
|
||||
vpx_memalign(kDataAlignment, sizeof(*hbuf_asm_) * 16));
|
||||
hbuf_c_ = reinterpret_cast<int16_t *>(
|
||||
vpx_memalign(kDataAlignment, sizeof(*hbuf_c_) * 16));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
vpx_free(source_data_);
|
||||
source_data_ = NULL;
|
||||
vpx_free(hbuf_c_);
|
||||
hbuf_c_ = NULL;
|
||||
vpx_free(hbuf_asm_);
|
||||
hbuf_asm_ = NULL;
|
||||
}
|
||||
|
||||
void RunComparison() {
|
||||
ASM_REGISTER_STATE_CHECK(c_func_(hbuf_c_, source_data_, 0, height_));
|
||||
ASM_REGISTER_STATE_CHECK(asm_func_(hbuf_asm_, source_data_, 0, height_));
|
||||
EXPECT_EQ(0, memcmp(hbuf_c_, hbuf_asm_, sizeof(*hbuf_c_) * 16))
|
||||
<< "Output mismatch";
|
||||
}
|
||||
|
||||
private:
|
||||
IntProRowFunc asm_func_;
|
||||
IntProRowFunc c_func_;
|
||||
int16_t *hbuf_asm_;
|
||||
int16_t *hbuf_c_;
|
||||
};
|
||||
|
||||
typedef int16_t (*IntProColFunc)(uint8_t const *ref, const int width);
|
||||
|
||||
typedef std::tuple<int, IntProColFunc, IntProColFunc> IntProColParam;
|
||||
|
||||
class IntProColTest : public AverageTestBase<uint8_t>,
|
||||
public ::testing::WithParamInterface<IntProColParam> {
|
||||
public:
|
||||
IntProColTest() : AverageTestBase(GET_PARAM(0), 1), sum_asm_(0), sum_c_(0) {
|
||||
asm_func_ = GET_PARAM(1);
|
||||
c_func_ = GET_PARAM(2);
|
||||
}
|
||||
|
||||
protected:
|
||||
void RunComparison() {
|
||||
ASM_REGISTER_STATE_CHECK(sum_c_ = c_func_(source_data_, width_));
|
||||
ASM_REGISTER_STATE_CHECK(sum_asm_ = asm_func_(source_data_, width_));
|
||||
EXPECT_EQ(sum_c_, sum_asm_) << "Output mismatch";
|
||||
}
|
||||
|
||||
private:
|
||||
IntProColFunc asm_func_;
|
||||
IntProColFunc c_func_;
|
||||
int16_t sum_asm_;
|
||||
int16_t sum_c_;
|
||||
};
|
||||
|
||||
typedef int (*SatdFunc)(const tran_low_t *coeffs, int length);
|
||||
typedef std::tuple<int, SatdFunc> SatdTestParam;
|
||||
|
||||
class SatdTest : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<SatdTestParam> {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
satd_size_ = GET_PARAM(0);
|
||||
satd_func_ = GET_PARAM(1);
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
src_ = reinterpret_cast<tran_low_t *>(
|
||||
vpx_memalign(16, sizeof(*src_) * satd_size_));
|
||||
ASSERT_TRUE(src_ != NULL);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
libvpx_test::ClearSystemState();
|
||||
vpx_free(src_);
|
||||
}
|
||||
|
||||
void FillConstant(const tran_low_t val) {
|
||||
for (int i = 0; i < satd_size_; ++i) src_[i] = val;
|
||||
}
|
||||
|
||||
virtual void FillRandom() = 0;
|
||||
|
||||
void Check(const int expected) {
|
||||
int total;
|
||||
ASM_REGISTER_STATE_CHECK(total = satd_func_(src_, satd_size_));
|
||||
EXPECT_EQ(expected, total);
|
||||
}
|
||||
|
||||
tran_low_t *GetCoeff() const { return src_; }
|
||||
|
||||
int satd_size_;
|
||||
ACMRandom rnd_;
|
||||
tran_low_t *src_;
|
||||
|
||||
private:
|
||||
SatdFunc satd_func_;
|
||||
};
|
||||
|
||||
class SatdLowbdTest : public SatdTest {
|
||||
protected:
|
||||
virtual void FillRandom() {
|
||||
for (int i = 0; i < satd_size_; ++i) {
|
||||
const int16_t tmp = rnd_.Rand16Signed();
|
||||
src_[i] = (tran_low_t)tmp;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef int64_t (*BlockErrorFunc)(const tran_low_t *coeff,
|
||||
const tran_low_t *dqcoeff, int block_size);
|
||||
typedef std::tuple<int, BlockErrorFunc> BlockErrorTestFPParam;
|
||||
|
||||
class BlockErrorTestFP
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<BlockErrorTestFPParam> {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
txfm_size_ = GET_PARAM(0);
|
||||
block_error_func_ = GET_PARAM(1);
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
coeff_ = reinterpret_cast<tran_low_t *>(
|
||||
vpx_memalign(16, sizeof(*coeff_) * txfm_size_));
|
||||
dqcoeff_ = reinterpret_cast<tran_low_t *>(
|
||||
vpx_memalign(16, sizeof(*dqcoeff_) * txfm_size_));
|
||||
ASSERT_TRUE(coeff_ != NULL);
|
||||
ASSERT_TRUE(dqcoeff_ != NULL);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
libvpx_test::ClearSystemState();
|
||||
vpx_free(coeff_);
|
||||
vpx_free(dqcoeff_);
|
||||
}
|
||||
|
||||
void FillConstant(const tran_low_t coeff_val, const tran_low_t dqcoeff_val) {
|
||||
for (int i = 0; i < txfm_size_; ++i) coeff_[i] = coeff_val;
|
||||
for (int i = 0; i < txfm_size_; ++i) dqcoeff_[i] = dqcoeff_val;
|
||||
}
|
||||
|
||||
void FillRandom() {
|
||||
// Just two fixed seeds
|
||||
rnd_.Reset(0xb0b9);
|
||||
for (int i = 0; i < txfm_size_; ++i) coeff_[i] = rnd_.Rand16() >> 1;
|
||||
rnd_.Reset(0xb0c8);
|
||||
for (int i = 0; i < txfm_size_; ++i) dqcoeff_[i] = rnd_.Rand16() >> 1;
|
||||
}
|
||||
|
||||
void Check(const int64_t expected) {
|
||||
int64_t total;
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
total = block_error_func_(coeff_, dqcoeff_, txfm_size_));
|
||||
EXPECT_EQ(expected, total);
|
||||
}
|
||||
|
||||
tran_low_t *GetCoeff() const { return coeff_; }
|
||||
|
||||
tran_low_t *GetDQCoeff() const { return dqcoeff_; }
|
||||
|
||||
int txfm_size_;
|
||||
|
||||
private:
|
||||
tran_low_t *coeff_;
|
||||
tran_low_t *dqcoeff_;
|
||||
BlockErrorFunc block_error_func_;
|
||||
ACMRandom rnd_;
|
||||
};
|
||||
|
||||
TEST_P(AverageTest, MinValue) {
|
||||
FillConstant(0);
|
||||
CheckAverages();
|
||||
}
|
||||
|
||||
TEST_P(AverageTest, MaxValue) {
|
||||
FillConstant(255);
|
||||
CheckAverages();
|
||||
}
|
||||
|
||||
TEST_P(AverageTest, Random) {
|
||||
// The reference frame, but not the source frame, may be unaligned for
|
||||
// certain types of searches.
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
FillRandom();
|
||||
CheckAverages();
|
||||
}
|
||||
}
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
TEST_P(AverageTestHBD, MinValue) {
|
||||
FillConstant(0);
|
||||
CheckAverages();
|
||||
}
|
||||
|
||||
TEST_P(AverageTestHBD, MaxValue) {
|
||||
FillConstant((1 << VPX_BITS_12) - 1);
|
||||
CheckAverages();
|
||||
}
|
||||
|
||||
TEST_P(AverageTestHBD, Random) {
|
||||
bit_depth_ = VPX_BITS_12;
|
||||
// The reference frame, but not the source frame, may be unaligned for
|
||||
// certain types of searches.
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
FillRandom();
|
||||
CheckAverages();
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
TEST_P(IntProRowTest, MinValue) {
|
||||
FillConstant(0);
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(IntProRowTest, MaxValue) {
|
||||
FillConstant(255);
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(IntProRowTest, Random) {
|
||||
FillRandom();
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(IntProColTest, MinValue) {
|
||||
FillConstant(0);
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(IntProColTest, MaxValue) {
|
||||
FillConstant(255);
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(IntProColTest, Random) {
|
||||
FillRandom();
|
||||
RunComparison();
|
||||
}
|
||||
|
||||
TEST_P(SatdLowbdTest, MinValue) {
|
||||
const int kMin = -32640;
|
||||
const int expected = -kMin * satd_size_;
|
||||
FillConstant(kMin);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdLowbdTest, MaxValue) {
|
||||
const int kMax = 32640;
|
||||
const int expected = kMax * satd_size_;
|
||||
FillConstant(kMax);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdLowbdTest, Random) {
|
||||
int expected;
|
||||
switch (satd_size_) {
|
||||
case 16: expected = 261036; break;
|
||||
case 64: expected = 991732; break;
|
||||
case 256: expected = 4136358; break;
|
||||
case 1024: expected = 16677592; break;
|
||||
default:
|
||||
FAIL() << "Invalid satd size (" << satd_size_
|
||||
<< ") valid: 16/64/256/1024";
|
||||
}
|
||||
FillRandom();
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdLowbdTest, DISABLED_Speed) {
|
||||
const int kCountSpeedTestBlock = 20000;
|
||||
vpx_usec_timer timer;
|
||||
const int blocksize = GET_PARAM(0);
|
||||
FillRandom();
|
||||
tran_low_t *coeff = GetCoeff();
|
||||
|
||||
vpx_usec_timer_start(&timer);
|
||||
for (int i = 0; i < kCountSpeedTestBlock; ++i) {
|
||||
GET_PARAM(1)(coeff, blocksize);
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer));
|
||||
printf("blocksize: %4d time: %4d us\n", blocksize, elapsed_time);
|
||||
}
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
class SatdHighbdTest : public SatdTest {
|
||||
protected:
|
||||
virtual void FillRandom() {
|
||||
for (int i = 0; i < satd_size_; ++i) {
|
||||
src_[i] = rnd_.Rand20Signed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(SatdHighbdTest, MinValue) {
|
||||
const int kMin = -524280;
|
||||
const int expected = -kMin * satd_size_;
|
||||
FillConstant(kMin);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdHighbdTest, MaxValue) {
|
||||
const int kMax = 524280;
|
||||
const int expected = kMax * satd_size_;
|
||||
FillConstant(kMax);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdHighbdTest, Random) {
|
||||
int expected;
|
||||
switch (satd_size_) {
|
||||
case 16: expected = 5249712; break;
|
||||
case 64: expected = 18362120; break;
|
||||
case 256: expected = 66100520; break;
|
||||
case 1024: expected = 266094734; break;
|
||||
default:
|
||||
FAIL() << "Invalid satd size (" << satd_size_
|
||||
<< ") valid: 16/64/256/1024";
|
||||
}
|
||||
FillRandom();
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(SatdHighbdTest, DISABLED_Speed) {
|
||||
const int kCountSpeedTestBlock = 20000;
|
||||
vpx_usec_timer timer;
|
||||
const int blocksize = GET_PARAM(0);
|
||||
FillRandom();
|
||||
tran_low_t *coeff = GetCoeff();
|
||||
|
||||
vpx_usec_timer_start(&timer);
|
||||
for (int i = 0; i < kCountSpeedTestBlock; ++i) {
|
||||
GET_PARAM(1)(coeff, blocksize);
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer));
|
||||
printf("blocksize: %4d time: %4d us\n", blocksize, elapsed_time);
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
TEST_P(BlockErrorTestFP, MinValue) {
|
||||
const int64_t kMin = -32640;
|
||||
const int64_t expected = kMin * kMin * txfm_size_;
|
||||
FillConstant(kMin, 0);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(BlockErrorTestFP, MaxValue) {
|
||||
const int64_t kMax = 32640;
|
||||
const int64_t expected = kMax * kMax * txfm_size_;
|
||||
FillConstant(kMax, 0);
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(BlockErrorTestFP, Random) {
|
||||
int64_t expected;
|
||||
switch (txfm_size_) {
|
||||
case 16: expected = 2051681432; break;
|
||||
case 64: expected = 11075114379; break;
|
||||
case 256: expected = 44386271116; break;
|
||||
case 1024: expected = 184774996089; break;
|
||||
default:
|
||||
FAIL() << "Invalid satd size (" << txfm_size_
|
||||
<< ") valid: 16/64/256/1024";
|
||||
}
|
||||
FillRandom();
|
||||
Check(expected);
|
||||
}
|
||||
|
||||
TEST_P(BlockErrorTestFP, DISABLED_Speed) {
|
||||
const int kCountSpeedTestBlock = 20000;
|
||||
vpx_usec_timer timer;
|
||||
const int blocksize = GET_PARAM(0);
|
||||
FillRandom();
|
||||
tran_low_t *coeff = GetCoeff();
|
||||
tran_low_t *dqcoeff = GetDQCoeff();
|
||||
|
||||
vpx_usec_timer_start(&timer);
|
||||
for (int i = 0; i < kCountSpeedTestBlock; ++i) {
|
||||
GET_PARAM(1)(coeff, dqcoeff, blocksize);
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
const int elapsed_time = static_cast<int>(vpx_usec_timer_elapsed(&timer));
|
||||
printf("blocksize: %4d time: %4d us\n", blocksize, elapsed_time);
|
||||
}
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, AverageTest,
|
||||
::testing::Values(make_tuple(16, 16, 1, 8, &vpx_avg_8x8_c),
|
||||
make_tuple(16, 16, 1, 4, &vpx_avg_4x4_c)));
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, AverageTestHBD,
|
||||
::testing::Values(make_tuple(16, 16, 1, 8, &vpx_highbd_avg_8x8_c),
|
||||
make_tuple(16, 16, 1, 4, &vpx_highbd_avg_4x4_c)));
|
||||
|
||||
#if HAVE_SSE2
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, AverageTestHBD,
|
||||
::testing::Values(make_tuple(16, 16, 1, 8, &vpx_highbd_avg_8x8_sse2),
|
||||
make_tuple(16, 16, 1, 4, &vpx_highbd_avg_4x4_sse2)));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(C, SatdHighbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_c),
|
||||
make_tuple(64, &vpx_satd_c),
|
||||
make_tuple(256, &vpx_satd_c),
|
||||
make_tuple(1024, &vpx_satd_c)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(C, SatdLowbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_c),
|
||||
make_tuple(64, &vpx_satd_c),
|
||||
make_tuple(256, &vpx_satd_c),
|
||||
make_tuple(1024, &vpx_satd_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, BlockErrorTestFP,
|
||||
::testing::Values(make_tuple(16, &vp9_block_error_fp_c),
|
||||
make_tuple(64, &vp9_block_error_fp_c),
|
||||
make_tuple(256, &vp9_block_error_fp_c),
|
||||
make_tuple(1024, &vp9_block_error_fp_c)));
|
||||
|
||||
#if HAVE_SSE2
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, AverageTest,
|
||||
::testing::Values(make_tuple(16, 16, 0, 8, &vpx_avg_8x8_sse2),
|
||||
make_tuple(16, 16, 5, 8, &vpx_avg_8x8_sse2),
|
||||
make_tuple(32, 32, 15, 8, &vpx_avg_8x8_sse2),
|
||||
make_tuple(16, 16, 0, 4, &vpx_avg_4x4_sse2),
|
||||
make_tuple(16, 16, 5, 4, &vpx_avg_4x4_sse2),
|
||||
make_tuple(32, 32, 15, 4, &vpx_avg_4x4_sse2)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, IntProRowTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_row_sse2, &vpx_int_pro_row_c),
|
||||
make_tuple(32, &vpx_int_pro_row_sse2, &vpx_int_pro_row_c),
|
||||
make_tuple(64, &vpx_int_pro_row_sse2,
|
||||
&vpx_int_pro_row_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, IntProColTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_col_sse2, &vpx_int_pro_col_c),
|
||||
make_tuple(32, &vpx_int_pro_col_sse2, &vpx_int_pro_col_c),
|
||||
make_tuple(64, &vpx_int_pro_col_sse2,
|
||||
&vpx_int_pro_col_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(SSE2, SatdLowbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_sse2),
|
||||
make_tuple(64, &vpx_satd_sse2),
|
||||
make_tuple(256, &vpx_satd_sse2),
|
||||
make_tuple(1024, &vpx_satd_sse2)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, BlockErrorTestFP,
|
||||
::testing::Values(make_tuple(16, &vp9_block_error_fp_sse2),
|
||||
make_tuple(64, &vp9_block_error_fp_sse2),
|
||||
make_tuple(256, &vp9_block_error_fp_sse2),
|
||||
make_tuple(1024, &vp9_block_error_fp_sse2)));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
#if HAVE_AVX2
|
||||
INSTANTIATE_TEST_CASE_P(AVX2, SatdLowbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_avx2),
|
||||
make_tuple(64, &vpx_satd_avx2),
|
||||
make_tuple(256, &vpx_satd_avx2),
|
||||
make_tuple(1024, &vpx_satd_avx2)));
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AVX2, SatdHighbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_highbd_satd_avx2),
|
||||
make_tuple(64, &vpx_highbd_satd_avx2),
|
||||
make_tuple(256, &vpx_highbd_satd_avx2),
|
||||
make_tuple(1024, &vpx_highbd_satd_avx2)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AVX2, BlockErrorTestFP,
|
||||
::testing::Values(make_tuple(16, &vp9_block_error_fp_avx2),
|
||||
make_tuple(64, &vp9_block_error_fp_avx2),
|
||||
make_tuple(256, &vp9_block_error_fp_avx2),
|
||||
make_tuple(1024, &vp9_block_error_fp_avx2)));
|
||||
#endif
|
||||
|
||||
#if HAVE_NEON
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, AverageTest,
|
||||
::testing::Values(make_tuple(16, 16, 0, 8, &vpx_avg_8x8_neon),
|
||||
make_tuple(16, 16, 5, 8, &vpx_avg_8x8_neon),
|
||||
make_tuple(32, 32, 15, 8, &vpx_avg_8x8_neon),
|
||||
make_tuple(16, 16, 0, 4, &vpx_avg_4x4_neon),
|
||||
make_tuple(16, 16, 5, 4, &vpx_avg_4x4_neon),
|
||||
make_tuple(32, 32, 15, 4, &vpx_avg_4x4_neon)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, IntProRowTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_row_neon, &vpx_int_pro_row_c),
|
||||
make_tuple(32, &vpx_int_pro_row_neon, &vpx_int_pro_row_c),
|
||||
make_tuple(64, &vpx_int_pro_row_neon,
|
||||
&vpx_int_pro_row_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, IntProColTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_col_neon, &vpx_int_pro_col_c),
|
||||
make_tuple(32, &vpx_int_pro_col_neon, &vpx_int_pro_col_c),
|
||||
make_tuple(64, &vpx_int_pro_col_neon,
|
||||
&vpx_int_pro_col_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(NEON, SatdLowbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_neon),
|
||||
make_tuple(64, &vpx_satd_neon),
|
||||
make_tuple(256, &vpx_satd_neon),
|
||||
make_tuple(1024, &vpx_satd_neon)));
|
||||
|
||||
// TODO(jianj): Remove the highbitdepth flag once the SIMD functions are
|
||||
// in place.
|
||||
#if !CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, BlockErrorTestFP,
|
||||
::testing::Values(make_tuple(16, &vp9_block_error_fp_neon),
|
||||
make_tuple(64, &vp9_block_error_fp_neon),
|
||||
make_tuple(256, &vp9_block_error_fp_neon),
|
||||
make_tuple(1024, &vp9_block_error_fp_neon)));
|
||||
#endif // !CONFIG_VP9_HIGHBITDEPTH
|
||||
#endif // HAVE_NEON
|
||||
|
||||
#if HAVE_MSA
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, AverageTest,
|
||||
::testing::Values(make_tuple(16, 16, 0, 8, &vpx_avg_8x8_msa),
|
||||
make_tuple(16, 16, 5, 8, &vpx_avg_8x8_msa),
|
||||
make_tuple(32, 32, 15, 8, &vpx_avg_8x8_msa),
|
||||
make_tuple(16, 16, 0, 4, &vpx_avg_4x4_msa),
|
||||
make_tuple(16, 16, 5, 4, &vpx_avg_4x4_msa),
|
||||
make_tuple(32, 32, 15, 4, &vpx_avg_4x4_msa)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, IntProRowTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_row_msa, &vpx_int_pro_row_c),
|
||||
make_tuple(32, &vpx_int_pro_row_msa, &vpx_int_pro_row_c),
|
||||
make_tuple(64, &vpx_int_pro_row_msa,
|
||||
&vpx_int_pro_row_c)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, IntProColTest,
|
||||
::testing::Values(make_tuple(16, &vpx_int_pro_col_msa, &vpx_int_pro_col_c),
|
||||
make_tuple(32, &vpx_int_pro_col_msa, &vpx_int_pro_col_c),
|
||||
make_tuple(64, &vpx_int_pro_col_msa,
|
||||
&vpx_int_pro_col_c)));
|
||||
|
||||
// TODO(jingning): Remove the highbitdepth flag once the SIMD functions are
|
||||
// in place.
|
||||
#if !CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(MSA, SatdLowbdTest,
|
||||
::testing::Values(make_tuple(16, &vpx_satd_msa),
|
||||
make_tuple(64, &vpx_satd_msa),
|
||||
make_tuple(256, &vpx_satd_msa),
|
||||
make_tuple(1024, &vpx_satd_msa)));
|
||||
#endif // !CONFIG_VP9_HIGHBITDEPTH
|
||||
#endif // HAVE_MSA
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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 <algorithm>
|
||||
|
||||
#include "test/bench.h"
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
|
||||
void AbstractBench::RunNTimes(int n) {
|
||||
for (int r = 0; r < VPX_BENCH_ROBUST_ITER; r++) {
|
||||
vpx_usec_timer timer;
|
||||
vpx_usec_timer_start(&timer);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
Run();
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
times_[r] = static_cast<int>(vpx_usec_timer_elapsed(&timer));
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractBench::PrintMedian(const char *title) {
|
||||
std::sort(times_, times_ + VPX_BENCH_ROBUST_ITER);
|
||||
const int med = times_[VPX_BENCH_ROBUST_ITER >> 1];
|
||||
int sad = 0;
|
||||
for (int t = 0; t < VPX_BENCH_ROBUST_ITER; t++) {
|
||||
sad += abs(times_[t] - med);
|
||||
}
|
||||
printf("[%10s] %s %.1f ms ( ±%.1f ms )\n", "BENCH ", title, med / 1000.0,
|
||||
sad / (VPX_BENCH_ROBUST_ITER * 1000.0));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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 VPX_TEST_BENCH_H_
|
||||
#define VPX_TEST_BENCH_H_
|
||||
|
||||
// Number of iterations used to compute median run time.
|
||||
#define VPX_BENCH_ROBUST_ITER 15
|
||||
|
||||
class AbstractBench {
|
||||
public:
|
||||
void RunNTimes(int n);
|
||||
void PrintMedian(const char *title);
|
||||
|
||||
protected:
|
||||
// Implement this method and put the code to benchmark in it.
|
||||
virtual void Run() = 0;
|
||||
|
||||
private:
|
||||
int times_[VPX_BENCH_ROBUST_ITER];
|
||||
};
|
||||
|
||||
#endif // VPX_TEST_BENCH_H_
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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 <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#if CONFIG_VP9_ENCODER
|
||||
#include "./vp9_rtcd.h"
|
||||
#endif
|
||||
|
||||
#include "test/acm_random.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
#include "vp9/encoder/vp9_blockiness.h"
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
|
||||
namespace {
|
||||
class BlockinessTestBase : public ::testing::Test {
|
||||
public:
|
||||
BlockinessTestBase(int width, int height) : width_(width), height_(height) {}
|
||||
|
||||
static void SetUpTestCase() {
|
||||
source_data_ = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
reference_data_ = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
vpx_free(source_data_);
|
||||
source_data_ = NULL;
|
||||
vpx_free(reference_data_);
|
||||
reference_data_ = NULL;
|
||||
}
|
||||
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
// Handle frames up to 640x480
|
||||
static const int kDataAlignment = 16;
|
||||
static const int kDataBufferSize = 640 * 480;
|
||||
|
||||
virtual void SetUp() {
|
||||
source_stride_ = (width_ + 31) & ~31;
|
||||
reference_stride_ = width_ * 2;
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
}
|
||||
|
||||
void FillConstant(uint8_t *data, int stride, uint8_t fill_constant, int width,
|
||||
int height) {
|
||||
for (int h = 0; h < height; ++h) {
|
||||
for (int w = 0; w < width; ++w) {
|
||||
data[h * stride + w] = fill_constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillConstant(uint8_t *data, int stride, uint8_t fill_constant) {
|
||||
FillConstant(data, stride, fill_constant, width_, height_);
|
||||
}
|
||||
|
||||
void FillRandom(uint8_t *data, int stride, int width, int height) {
|
||||
for (int h = 0; h < height; ++h) {
|
||||
for (int w = 0; w < width; ++w) {
|
||||
data[h * stride + w] = rnd_.Rand8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillRandom(uint8_t *data, int stride) {
|
||||
FillRandom(data, stride, width_, height_);
|
||||
}
|
||||
|
||||
void FillRandomBlocky(uint8_t *data, int stride) {
|
||||
for (int h = 0; h < height_; h += 4) {
|
||||
for (int w = 0; w < width_; w += 4) {
|
||||
FillRandom(data + h * stride + w, stride, 4, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillCheckerboard(uint8_t *data, int stride) {
|
||||
for (int h = 0; h < height_; h += 4) {
|
||||
for (int w = 0; w < width_; w += 4) {
|
||||
if (((h / 4) ^ (w / 4)) & 1) {
|
||||
FillConstant(data + h * stride + w, stride, 255, 4, 4);
|
||||
} else {
|
||||
FillConstant(data + h * stride + w, stride, 0, 4, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Blur(uint8_t *data, int stride, int taps) {
|
||||
int sum = 0;
|
||||
int half_taps = taps / 2;
|
||||
for (int h = 0; h < height_; ++h) {
|
||||
for (int w = 0; w < taps; ++w) {
|
||||
sum += data[w + h * stride];
|
||||
}
|
||||
for (int w = taps; w < width_; ++w) {
|
||||
sum += data[w + h * stride] - data[w - taps + h * stride];
|
||||
data[w - half_taps + h * stride] = (sum + half_taps) / taps;
|
||||
}
|
||||
}
|
||||
for (int w = 0; w < width_; ++w) {
|
||||
for (int h = 0; h < taps; ++h) {
|
||||
sum += data[h + w * stride];
|
||||
}
|
||||
for (int h = taps; h < height_; ++h) {
|
||||
sum += data[w + h * stride] - data[(h - taps) * stride + w];
|
||||
data[(h - half_taps) * stride + w] = (sum + half_taps) / taps;
|
||||
}
|
||||
}
|
||||
}
|
||||
int width_, height_;
|
||||
static uint8_t *source_data_;
|
||||
int source_stride_;
|
||||
static uint8_t *reference_data_;
|
||||
int reference_stride_;
|
||||
|
||||
ACMRandom rnd_;
|
||||
};
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
typedef std::tuple<int, int> BlockinessParam;
|
||||
class BlockinessVP9Test
|
||||
: public BlockinessTestBase,
|
||||
public ::testing::WithParamInterface<BlockinessParam> {
|
||||
public:
|
||||
BlockinessVP9Test() : BlockinessTestBase(GET_PARAM(0), GET_PARAM(1)) {}
|
||||
|
||||
protected:
|
||||
double GetBlockiness() const {
|
||||
return vp9_get_blockiness(source_data_, source_stride_, reference_data_,
|
||||
reference_stride_, width_, height_);
|
||||
}
|
||||
};
|
||||
#endif // CONFIG_VP9_ENCODER
|
||||
|
||||
uint8_t *BlockinessTestBase::source_data_ = NULL;
|
||||
uint8_t *BlockinessTestBase::reference_data_ = NULL;
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
TEST_P(BlockinessVP9Test, SourceBlockierThanReference) {
|
||||
// Source is blockier than reference.
|
||||
FillRandomBlocky(source_data_, source_stride_);
|
||||
FillConstant(reference_data_, reference_stride_, 128);
|
||||
const double super_blocky = GetBlockiness();
|
||||
|
||||
EXPECT_DOUBLE_EQ(0.0, super_blocky)
|
||||
<< "Blocky source should produce 0 blockiness.";
|
||||
}
|
||||
|
||||
TEST_P(BlockinessVP9Test, ReferenceBlockierThanSource) {
|
||||
// Source is blockier than reference.
|
||||
FillConstant(source_data_, source_stride_, 128);
|
||||
FillRandomBlocky(reference_data_, reference_stride_);
|
||||
const double super_blocky = GetBlockiness();
|
||||
|
||||
EXPECT_GT(super_blocky, 0.0)
|
||||
<< "Blocky reference should score high for blockiness.";
|
||||
}
|
||||
|
||||
TEST_P(BlockinessVP9Test, BlurringDecreasesBlockiness) {
|
||||
// Source is blockier than reference.
|
||||
FillConstant(source_data_, source_stride_, 128);
|
||||
FillRandomBlocky(reference_data_, reference_stride_);
|
||||
const double super_blocky = GetBlockiness();
|
||||
|
||||
Blur(reference_data_, reference_stride_, 4);
|
||||
const double less_blocky = GetBlockiness();
|
||||
|
||||
EXPECT_GT(super_blocky, less_blocky)
|
||||
<< "A straight blur should decrease blockiness.";
|
||||
}
|
||||
|
||||
TEST_P(BlockinessVP9Test, WorstCaseBlockiness) {
|
||||
// Source is blockier than reference.
|
||||
FillConstant(source_data_, source_stride_, 128);
|
||||
FillCheckerboard(reference_data_, reference_stride_);
|
||||
|
||||
const double super_blocky = GetBlockiness();
|
||||
|
||||
Blur(reference_data_, reference_stride_, 4);
|
||||
const double less_blocky = GetBlockiness();
|
||||
|
||||
EXPECT_GT(super_blocky, less_blocky)
|
||||
<< "A straight blur should decrease blockiness.";
|
||||
}
|
||||
#endif // CONFIG_VP9_ENCODER
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// C functions
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
const BlockinessParam c_vp9_tests[] = { make_tuple(320, 240),
|
||||
make_tuple(318, 242),
|
||||
make_tuple(318, 238) };
|
||||
INSTANTIATE_TEST_CASE_P(C, BlockinessVP9Test, ::testing::ValuesIn(c_vp9_tests));
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 <climits>
|
||||
#include <vector>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class BordersTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
|
||||
protected:
|
||||
BordersTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
virtual ~BordersTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, 1);
|
||||
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
|
||||
encoder->Control(VP8E_SET_ARNR_MAXFRAMES, 7);
|
||||
encoder->Control(VP8E_SET_ARNR_STRENGTH, 5);
|
||||
encoder->Control(VP8E_SET_ARNR_TYPE, 3);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(BordersTest, TestEncodeHighBitrate) {
|
||||
// Validate that this non multiple of 64 wide clip encodes and decodes
|
||||
// without a mismatch when passing in a very low max q. This pushes
|
||||
// the encoder to producing lots of big partitions which will likely
|
||||
// extend into the border and test the border condition.
|
||||
cfg_.g_lag_in_frames = 25;
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 2000;
|
||||
cfg_.rc_max_quantizer = 10;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
|
||||
40);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
TEST_P(BordersTest, TestLowBitrate) {
|
||||
// Validate that this clip encodes and decodes without a mismatch
|
||||
// when passing in a very high min q. This pushes the encoder to producing
|
||||
// lots of small partitions which might will test the other condition.
|
||||
|
||||
cfg_.g_lag_in_frames = 25;
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 200;
|
||||
cfg_.rc_min_quantizer = 40;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
|
||||
40);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(BordersTest,
|
||||
::testing::Values(::libvpx_test::kTwoPassGood));
|
||||
} // namespace
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 VPX_TEST_BUFFER_H_
|
||||
#define VPX_TEST_BUFFER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "test/acm_random.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
|
||||
template <typename T>
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer(int width, int height, int top_padding, int left_padding,
|
||||
int right_padding, int bottom_padding)
|
||||
: width_(width), height_(height), top_padding_(top_padding),
|
||||
left_padding_(left_padding), right_padding_(right_padding),
|
||||
bottom_padding_(bottom_padding), alignment_(0), padding_value_(0),
|
||||
stride_(0), raw_size_(0), num_elements_(0), raw_buffer_(NULL) {}
|
||||
|
||||
Buffer(int width, int height, int top_padding, int left_padding,
|
||||
int right_padding, int bottom_padding, unsigned int alignment)
|
||||
: width_(width), height_(height), top_padding_(top_padding),
|
||||
left_padding_(left_padding), right_padding_(right_padding),
|
||||
bottom_padding_(bottom_padding), alignment_(alignment),
|
||||
padding_value_(0), stride_(0), raw_size_(0), num_elements_(0),
|
||||
raw_buffer_(NULL) {}
|
||||
|
||||
Buffer(int width, int height, int padding)
|
||||
: width_(width), height_(height), top_padding_(padding),
|
||||
left_padding_(padding), right_padding_(padding),
|
||||
bottom_padding_(padding), alignment_(0), padding_value_(0), stride_(0),
|
||||
raw_size_(0), num_elements_(0), raw_buffer_(NULL) {}
|
||||
|
||||
Buffer(int width, int height, int padding, unsigned int alignment)
|
||||
: width_(width), height_(height), top_padding_(padding),
|
||||
left_padding_(padding), right_padding_(padding),
|
||||
bottom_padding_(padding), alignment_(alignment), padding_value_(0),
|
||||
stride_(0), raw_size_(0), num_elements_(0), raw_buffer_(NULL) {}
|
||||
|
||||
~Buffer() {
|
||||
if (alignment_) {
|
||||
vpx_free(raw_buffer_);
|
||||
} else {
|
||||
delete[] raw_buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
T *TopLeftPixel() const;
|
||||
|
||||
int stride() const { return stride_; }
|
||||
|
||||
// Set the buffer (excluding padding) to 'value'.
|
||||
void Set(const T value);
|
||||
|
||||
// Set the buffer (excluding padding) to the output of ACMRandom function
|
||||
// 'rand_func'.
|
||||
void Set(ACMRandom *rand_class, T (ACMRandom::*rand_func)());
|
||||
|
||||
// Set the buffer (excluding padding) to the output of ACMRandom function
|
||||
// 'RandRange' with range 'low' to 'high' which typically must be within
|
||||
// testing::internal::Random::kMaxRange (1u << 31). However, because we want
|
||||
// to allow negative low (and high) values, it is restricted to INT32_MAX
|
||||
// here.
|
||||
void Set(ACMRandom *rand_class, const T low, const T high);
|
||||
|
||||
// Copy the contents of Buffer 'a' (excluding padding).
|
||||
void CopyFrom(const Buffer<T> &a);
|
||||
|
||||
void DumpBuffer() const;
|
||||
|
||||
// Highlight the differences between two buffers if they are the same size.
|
||||
void PrintDifference(const Buffer<T> &a) const;
|
||||
|
||||
bool HasPadding() const;
|
||||
|
||||
// Sets all the values in the buffer to 'padding_value'.
|
||||
void SetPadding(const T padding_value);
|
||||
|
||||
// Checks if all the values (excluding padding) are equal to 'value' if the
|
||||
// Buffers are the same size.
|
||||
bool CheckValues(const T value) const;
|
||||
|
||||
// Check that padding matches the expected value or there is no padding.
|
||||
bool CheckPadding() const;
|
||||
|
||||
// Compare the non-padding portion of two buffers if they are the same size.
|
||||
bool CheckValues(const Buffer<T> &a) const;
|
||||
|
||||
bool Init() {
|
||||
if (raw_buffer_ != NULL) return false;
|
||||
EXPECT_GT(width_, 0);
|
||||
EXPECT_GT(height_, 0);
|
||||
EXPECT_GE(top_padding_, 0);
|
||||
EXPECT_GE(left_padding_, 0);
|
||||
EXPECT_GE(right_padding_, 0);
|
||||
EXPECT_GE(bottom_padding_, 0);
|
||||
stride_ = left_padding_ + width_ + right_padding_;
|
||||
num_elements_ = stride_ * (top_padding_ + height_ + bottom_padding_);
|
||||
raw_size_ = num_elements_ * sizeof(T);
|
||||
if (alignment_) {
|
||||
EXPECT_GE(alignment_, sizeof(T));
|
||||
// Ensure alignment of the first value will be preserved.
|
||||
EXPECT_EQ((left_padding_ * sizeof(T)) % alignment_, 0u);
|
||||
// Ensure alignment of the subsequent rows will be preserved when there is
|
||||
// a stride.
|
||||
if (stride_ != width_) {
|
||||
EXPECT_EQ((stride_ * sizeof(T)) % alignment_, 0u);
|
||||
}
|
||||
raw_buffer_ = reinterpret_cast<T *>(vpx_memalign(alignment_, raw_size_));
|
||||
} else {
|
||||
raw_buffer_ = new (std::nothrow) T[num_elements_];
|
||||
}
|
||||
EXPECT_TRUE(raw_buffer_ != NULL);
|
||||
SetPadding(std::numeric_limits<T>::max());
|
||||
return !::testing::Test::HasFailure();
|
||||
}
|
||||
|
||||
private:
|
||||
bool BufferSizesMatch(const Buffer<T> &a) const;
|
||||
|
||||
const int width_;
|
||||
const int height_;
|
||||
const int top_padding_;
|
||||
const int left_padding_;
|
||||
const int right_padding_;
|
||||
const int bottom_padding_;
|
||||
const unsigned int alignment_;
|
||||
T padding_value_;
|
||||
int stride_;
|
||||
int raw_size_;
|
||||
int num_elements_;
|
||||
T *raw_buffer_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T *Buffer<T>::TopLeftPixel() const {
|
||||
if (!raw_buffer_) return NULL;
|
||||
return raw_buffer_ + (top_padding_ * stride_) + left_padding_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::Set(const T value) {
|
||||
if (!raw_buffer_) return;
|
||||
T *src = TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
src[width] = value;
|
||||
}
|
||||
src += stride_;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::Set(ACMRandom *rand_class, T (ACMRandom::*rand_func)()) {
|
||||
if (!raw_buffer_) return;
|
||||
T *src = TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
src[width] = (*rand_class.*rand_func)();
|
||||
}
|
||||
src += stride_;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::Set(ACMRandom *rand_class, const T low, const T high) {
|
||||
if (!raw_buffer_) return;
|
||||
|
||||
EXPECT_LE(low, high);
|
||||
EXPECT_LE(static_cast<int64_t>(high) - low,
|
||||
std::numeric_limits<int32_t>::max());
|
||||
|
||||
T *src = TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
// 'low' will be promoted to unsigned given the return type of RandRange.
|
||||
// Store the value as an int to avoid unsigned overflow warnings when
|
||||
// 'low' is negative.
|
||||
const int32_t value =
|
||||
static_cast<int32_t>((*rand_class).RandRange(high - low));
|
||||
src[width] = static_cast<T>(value + low);
|
||||
}
|
||||
src += stride_;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::CopyFrom(const Buffer<T> &a) {
|
||||
if (!raw_buffer_) return;
|
||||
if (!BufferSizesMatch(a)) return;
|
||||
|
||||
T *a_src = a.TopLeftPixel();
|
||||
T *b_src = this->TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
b_src[width] = a_src[width];
|
||||
}
|
||||
a_src += a.stride();
|
||||
b_src += this->stride();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::DumpBuffer() const {
|
||||
if (!raw_buffer_) return;
|
||||
for (int height = 0; height < height_ + top_padding_ + bottom_padding_;
|
||||
++height) {
|
||||
for (int width = 0; width < stride_; ++width) {
|
||||
printf("%4d", raw_buffer_[height + width * stride_]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Buffer<T>::HasPadding() const {
|
||||
if (!raw_buffer_) return false;
|
||||
return top_padding_ || left_padding_ || right_padding_ || bottom_padding_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::PrintDifference(const Buffer<T> &a) const {
|
||||
if (!raw_buffer_) return;
|
||||
if (!BufferSizesMatch(a)) return;
|
||||
|
||||
T *a_src = a.TopLeftPixel();
|
||||
T *b_src = TopLeftPixel();
|
||||
|
||||
printf("This buffer:\n");
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
if (a_src[width] != b_src[width]) {
|
||||
printf("*%3d", b_src[width]);
|
||||
} else {
|
||||
printf("%4d", b_src[width]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
a_src += a.stride();
|
||||
b_src += this->stride();
|
||||
}
|
||||
|
||||
a_src = a.TopLeftPixel();
|
||||
b_src = TopLeftPixel();
|
||||
|
||||
printf("Reference buffer:\n");
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
if (a_src[width] != b_src[width]) {
|
||||
printf("*%3d", a_src[width]);
|
||||
} else {
|
||||
printf("%4d", a_src[width]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
a_src += a.stride();
|
||||
b_src += this->stride();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Buffer<T>::SetPadding(const T padding_value) {
|
||||
if (!raw_buffer_) return;
|
||||
padding_value_ = padding_value;
|
||||
|
||||
T *src = raw_buffer_;
|
||||
for (int i = 0; i < num_elements_; ++i) {
|
||||
src[i] = padding_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Buffer<T>::CheckValues(const T value) const {
|
||||
if (!raw_buffer_) return false;
|
||||
T *src = TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
if (value != src[width]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
src += stride_;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Buffer<T>::CheckPadding() const {
|
||||
if (!raw_buffer_) return false;
|
||||
if (!HasPadding()) return true;
|
||||
|
||||
// Top padding.
|
||||
T const *top = raw_buffer_;
|
||||
for (int i = 0; i < stride_ * top_padding_; ++i) {
|
||||
if (padding_value_ != top[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Left padding.
|
||||
T const *left = TopLeftPixel() - left_padding_;
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < left_padding_; ++width) {
|
||||
if (padding_value_ != left[width]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
left += stride_;
|
||||
}
|
||||
|
||||
// Right padding.
|
||||
T const *right = TopLeftPixel() + width_;
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < right_padding_; ++width) {
|
||||
if (padding_value_ != right[width]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
right += stride_;
|
||||
}
|
||||
|
||||
// Bottom padding
|
||||
T const *bottom = raw_buffer_ + (top_padding_ + height_) * stride_;
|
||||
for (int i = 0; i < stride_ * bottom_padding_; ++i) {
|
||||
if (padding_value_ != bottom[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Buffer<T>::CheckValues(const Buffer<T> &a) const {
|
||||
if (!raw_buffer_) return false;
|
||||
if (!BufferSizesMatch(a)) return false;
|
||||
|
||||
T *a_src = a.TopLeftPixel();
|
||||
T *b_src = this->TopLeftPixel();
|
||||
for (int height = 0; height < height_; ++height) {
|
||||
for (int width = 0; width < width_; ++width) {
|
||||
if (a_src[width] != b_src[width]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
a_src += a.stride();
|
||||
b_src += this->stride();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Buffer<T>::BufferSizesMatch(const Buffer<T> &a) const {
|
||||
if (!raw_buffer_) return false;
|
||||
if (a.width_ != this->width_ || a.height_ != this->height_) {
|
||||
printf(
|
||||
"Reference buffer of size %dx%d does not match this buffer which is "
|
||||
"size %dx%d\n",
|
||||
a.width_, a.height_, this->width_, this->height_);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace libvpx_test
|
||||
#endif // VPX_TEST_BUFFER_H_
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/md5_helper.h"
|
||||
#include "test/util.h"
|
||||
#if CONFIG_WEBM_IO
|
||||
#include "test/webm_video_source.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#if CONFIG_WEBM_IO
|
||||
|
||||
const int kLegacyByteAlignment = 0;
|
||||
const int kLegacyYPlaneByteAlignment = 32;
|
||||
const int kNumPlanesToCheck = 3;
|
||||
const char kVP9TestFile[] = "vp90-2-02-size-lf-1920x1080.webm";
|
||||
const char kVP9Md5File[] = "vp90-2-02-size-lf-1920x1080.webm.md5";
|
||||
|
||||
struct ByteAlignmentTestParam {
|
||||
int byte_alignment;
|
||||
vpx_codec_err_t expected_value;
|
||||
bool decode_remaining;
|
||||
};
|
||||
|
||||
const ByteAlignmentTestParam kBaTestParams[] = {
|
||||
{ kLegacyByteAlignment, VPX_CODEC_OK, true },
|
||||
{ 32, VPX_CODEC_OK, true },
|
||||
{ 64, VPX_CODEC_OK, true },
|
||||
{ 128, VPX_CODEC_OK, true },
|
||||
{ 256, VPX_CODEC_OK, true },
|
||||
{ 512, VPX_CODEC_OK, true },
|
||||
{ 1024, VPX_CODEC_OK, true },
|
||||
{ 1, VPX_CODEC_INVALID_PARAM, false },
|
||||
{ -2, VPX_CODEC_INVALID_PARAM, false },
|
||||
{ 4, VPX_CODEC_INVALID_PARAM, false },
|
||||
{ 16, VPX_CODEC_INVALID_PARAM, false },
|
||||
{ 255, VPX_CODEC_INVALID_PARAM, false },
|
||||
{ 2048, VPX_CODEC_INVALID_PARAM, false },
|
||||
};
|
||||
|
||||
// Class for testing byte alignment of reference buffers.
|
||||
class ByteAlignmentTest
|
||||
: public ::testing::TestWithParam<ByteAlignmentTestParam> {
|
||||
protected:
|
||||
ByteAlignmentTest() : video_(NULL), decoder_(NULL), md5_file_(NULL) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
video_ = new libvpx_test::WebMVideoSource(kVP9TestFile);
|
||||
ASSERT_TRUE(video_ != NULL);
|
||||
video_->Init();
|
||||
video_->Begin();
|
||||
|
||||
const vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
|
||||
decoder_ = new libvpx_test::VP9Decoder(cfg, 0);
|
||||
ASSERT_TRUE(decoder_ != NULL);
|
||||
|
||||
OpenMd5File(kVP9Md5File);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (md5_file_ != NULL) fclose(md5_file_);
|
||||
|
||||
delete decoder_;
|
||||
delete video_;
|
||||
}
|
||||
|
||||
void SetByteAlignment(int byte_alignment, vpx_codec_err_t expected_value) {
|
||||
decoder_->Control(VP9_SET_BYTE_ALIGNMENT, byte_alignment, expected_value);
|
||||
}
|
||||
|
||||
vpx_codec_err_t DecodeOneFrame(int byte_alignment_to_check) {
|
||||
const vpx_codec_err_t res =
|
||||
decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
|
||||
CheckDecodedFrames(byte_alignment_to_check);
|
||||
if (res == VPX_CODEC_OK) video_->Next();
|
||||
return res;
|
||||
}
|
||||
|
||||
vpx_codec_err_t DecodeRemainingFrames(int byte_alignment_to_check) {
|
||||
for (; video_->cxdata() != NULL; video_->Next()) {
|
||||
const vpx_codec_err_t res =
|
||||
decoder_->DecodeFrame(video_->cxdata(), video_->frame_size());
|
||||
if (res != VPX_CODEC_OK) return res;
|
||||
CheckDecodedFrames(byte_alignment_to_check);
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
// Check if |data| is aligned to |byte_alignment_to_check|.
|
||||
// |byte_alignment_to_check| must be a power of 2.
|
||||
void CheckByteAlignment(const uint8_t *data, int byte_alignment_to_check) {
|
||||
ASSERT_EQ(0u, reinterpret_cast<size_t>(data) % byte_alignment_to_check);
|
||||
}
|
||||
|
||||
// Iterate through the planes of the decoded frames and check for
|
||||
// alignment based off |byte_alignment_to_check|.
|
||||
void CheckDecodedFrames(int byte_alignment_to_check) {
|
||||
libvpx_test::DxDataIterator dec_iter = decoder_->GetDxData();
|
||||
const vpx_image_t *img;
|
||||
|
||||
// Get decompressed data
|
||||
while ((img = dec_iter.Next()) != NULL) {
|
||||
if (byte_alignment_to_check == kLegacyByteAlignment) {
|
||||
CheckByteAlignment(img->planes[0], kLegacyYPlaneByteAlignment);
|
||||
} else {
|
||||
for (int i = 0; i < kNumPlanesToCheck; ++i) {
|
||||
CheckByteAlignment(img->planes[i], byte_alignment_to_check);
|
||||
}
|
||||
}
|
||||
CheckMd5(*img);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(fgalligan): Move the MD5 testing code into another class.
|
||||
void OpenMd5File(const std::string &md5_file_name_) {
|
||||
md5_file_ = libvpx_test::OpenTestDataFile(md5_file_name_);
|
||||
ASSERT_TRUE(md5_file_ != NULL)
|
||||
<< "MD5 file open failed. Filename: " << md5_file_name_;
|
||||
}
|
||||
|
||||
void CheckMd5(const vpx_image_t &img) {
|
||||
ASSERT_TRUE(md5_file_ != NULL);
|
||||
char expected_md5[33];
|
||||
char junk[128];
|
||||
|
||||
// Read correct md5 checksums.
|
||||
const int res = fscanf(md5_file_, "%s %s", expected_md5, junk);
|
||||
ASSERT_NE(EOF, res) << "Read md5 data failed";
|
||||
expected_md5[32] = '\0';
|
||||
|
||||
::libvpx_test::MD5 md5_res;
|
||||
md5_res.Add(&img);
|
||||
const char *const actual_md5 = md5_res.Get();
|
||||
|
||||
// Check md5 match.
|
||||
ASSERT_STREQ(expected_md5, actual_md5) << "MD5 checksums don't match";
|
||||
}
|
||||
|
||||
libvpx_test::WebMVideoSource *video_;
|
||||
libvpx_test::VP9Decoder *decoder_;
|
||||
FILE *md5_file_;
|
||||
};
|
||||
|
||||
TEST_F(ByteAlignmentTest, SwitchByteAlignment) {
|
||||
const int num_elements = 14;
|
||||
const int byte_alignments[] = { 0, 32, 64, 128, 256, 512, 1024,
|
||||
0, 1024, 32, 512, 64, 256, 128 };
|
||||
|
||||
for (int i = 0; i < num_elements; ++i) {
|
||||
SetByteAlignment(byte_alignments[i], VPX_CODEC_OK);
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeOneFrame(byte_alignments[i]));
|
||||
}
|
||||
SetByteAlignment(byte_alignments[0], VPX_CODEC_OK);
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames(byte_alignments[0]));
|
||||
}
|
||||
|
||||
TEST_P(ByteAlignmentTest, TestAlignment) {
|
||||
const ByteAlignmentTestParam t = GetParam();
|
||||
SetByteAlignment(t.byte_alignment, t.expected_value);
|
||||
if (t.decode_remaining) {
|
||||
ASSERT_EQ(VPX_CODEC_OK, DecodeRemainingFrames(t.byte_alignment));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Alignments, ByteAlignmentTest,
|
||||
::testing::ValuesIn(kBaTestParams));
|
||||
|
||||
#endif // CONFIG_WEBM_IO
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef VPX_TEST_CLEAR_SYSTEM_STATE_H_
|
||||
#define VPX_TEST_CLEAR_SYSTEM_STATE_H_
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "vpx_ports/system_state.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
|
||||
// Reset system to a known state. This function should be used for all non-API
|
||||
// test cases.
|
||||
inline void ClearSystemState() { vpx_clear_system_state(); }
|
||||
|
||||
} // namespace libvpx_test
|
||||
#endif // VPX_TEST_CLEAR_SYSTEM_STATE_H_
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef VPX_TEST_CODEC_FACTORY_H_
|
||||
#define VPX_TEST_CODEC_FACTORY_H_
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#if CONFIG_VP8_ENCODER || CONFIG_VP9_ENCODER
|
||||
#include "vpx/vp8cx.h"
|
||||
#endif
|
||||
#if CONFIG_VP8_DECODER || CONFIG_VP9_DECODER
|
||||
#include "vpx/vp8dx.h"
|
||||
#endif
|
||||
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
namespace libvpx_test {
|
||||
|
||||
const int kCodecFactoryParam = 0;
|
||||
|
||||
class CodecFactory {
|
||||
public:
|
||||
CodecFactory() {}
|
||||
|
||||
virtual ~CodecFactory() {}
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg) const = 0;
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg,
|
||||
const vpx_codec_flags_t flags) const = 0;
|
||||
|
||||
virtual Encoder *CreateEncoder(vpx_codec_enc_cfg_t cfg,
|
||||
unsigned long deadline,
|
||||
const unsigned long init_flags,
|
||||
TwopassStatsStore *stats) const = 0;
|
||||
|
||||
virtual vpx_codec_err_t DefaultEncoderConfig(vpx_codec_enc_cfg_t *cfg,
|
||||
int usage) const = 0;
|
||||
};
|
||||
|
||||
/* Provide CodecTestWith<n>Params classes for a variable number of parameters
|
||||
* to avoid having to include a pointer to the CodecFactory in every test
|
||||
* definition.
|
||||
*/
|
||||
template <class T1>
|
||||
class CodecTestWithParam
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<const libvpx_test::CodecFactory *, T1> > {};
|
||||
|
||||
template <class T1, class T2>
|
||||
class CodecTestWith2Params
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<const libvpx_test::CodecFactory *, T1, T2> > {};
|
||||
|
||||
template <class T1, class T2, class T3>
|
||||
class CodecTestWith3Params
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<const libvpx_test::CodecFactory *, T1, T2, T3> > {};
|
||||
|
||||
template <class T1, class T2, class T3, class T4>
|
||||
class CodecTestWith4Params
|
||||
: public ::testing::TestWithParam<
|
||||
std::tuple<const libvpx_test::CodecFactory *, T1, T2, T3, T4> > {};
|
||||
|
||||
/*
|
||||
* VP8 Codec Definitions
|
||||
*/
|
||||
#if CONFIG_VP8
|
||||
class VP8Decoder : public Decoder {
|
||||
public:
|
||||
explicit VP8Decoder(vpx_codec_dec_cfg_t cfg) : Decoder(cfg) {}
|
||||
|
||||
VP8Decoder(vpx_codec_dec_cfg_t cfg, const vpx_codec_flags_t flag)
|
||||
: Decoder(cfg, flag) {}
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t *CodecInterface() const {
|
||||
#if CONFIG_VP8_DECODER
|
||||
return &vpx_codec_vp8_dx_algo;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class VP8Encoder : public Encoder {
|
||||
public:
|
||||
VP8Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline,
|
||||
const unsigned long init_flags, TwopassStatsStore *stats)
|
||||
: Encoder(cfg, deadline, init_flags, stats) {}
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t *CodecInterface() const {
|
||||
#if CONFIG_VP8_ENCODER
|
||||
return &vpx_codec_vp8_cx_algo;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class VP8CodecFactory : public CodecFactory {
|
||||
public:
|
||||
VP8CodecFactory() : CodecFactory() {}
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg) const {
|
||||
return CreateDecoder(cfg, 0);
|
||||
}
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg,
|
||||
const vpx_codec_flags_t flags) const {
|
||||
#if CONFIG_VP8_DECODER
|
||||
return new VP8Decoder(cfg, flags);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)flags;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Encoder *CreateEncoder(vpx_codec_enc_cfg_t cfg,
|
||||
unsigned long deadline,
|
||||
const unsigned long init_flags,
|
||||
TwopassStatsStore *stats) const {
|
||||
#if CONFIG_VP8_ENCODER
|
||||
return new VP8Encoder(cfg, deadline, init_flags, stats);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)deadline;
|
||||
(void)init_flags;
|
||||
(void)stats;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual vpx_codec_err_t DefaultEncoderConfig(vpx_codec_enc_cfg_t *cfg,
|
||||
int usage) const {
|
||||
#if CONFIG_VP8_ENCODER
|
||||
return vpx_codec_enc_config_default(&vpx_codec_vp8_cx_algo, cfg, usage);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)usage;
|
||||
return VPX_CODEC_INCAPABLE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
const libvpx_test::VP8CodecFactory kVP8;
|
||||
|
||||
#define VP8_INSTANTIATE_TEST_CASE(test, ...) \
|
||||
INSTANTIATE_TEST_CASE_P( \
|
||||
VP8, test, \
|
||||
::testing::Combine( \
|
||||
::testing::Values(static_cast<const libvpx_test::CodecFactory *>( \
|
||||
&libvpx_test::kVP8)), \
|
||||
__VA_ARGS__))
|
||||
#else
|
||||
#define VP8_INSTANTIATE_TEST_CASE(test, ...)
|
||||
#endif // CONFIG_VP8
|
||||
|
||||
/*
|
||||
* VP9 Codec Definitions
|
||||
*/
|
||||
#if CONFIG_VP9
|
||||
class VP9Decoder : public Decoder {
|
||||
public:
|
||||
explicit VP9Decoder(vpx_codec_dec_cfg_t cfg) : Decoder(cfg) {}
|
||||
|
||||
VP9Decoder(vpx_codec_dec_cfg_t cfg, const vpx_codec_flags_t flag)
|
||||
: Decoder(cfg, flag) {}
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t *CodecInterface() const {
|
||||
#if CONFIG_VP9_DECODER
|
||||
return &vpx_codec_vp9_dx_algo;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class VP9Encoder : public Encoder {
|
||||
public:
|
||||
VP9Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline,
|
||||
const unsigned long init_flags, TwopassStatsStore *stats)
|
||||
: Encoder(cfg, deadline, init_flags, stats) {}
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t *CodecInterface() const {
|
||||
#if CONFIG_VP9_ENCODER
|
||||
return &vpx_codec_vp9_cx_algo;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class VP9CodecFactory : public CodecFactory {
|
||||
public:
|
||||
VP9CodecFactory() : CodecFactory() {}
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg) const {
|
||||
return CreateDecoder(cfg, 0);
|
||||
}
|
||||
|
||||
virtual Decoder *CreateDecoder(vpx_codec_dec_cfg_t cfg,
|
||||
const vpx_codec_flags_t flags) const {
|
||||
#if CONFIG_VP9_DECODER
|
||||
return new VP9Decoder(cfg, flags);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)flags;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Encoder *CreateEncoder(vpx_codec_enc_cfg_t cfg,
|
||||
unsigned long deadline,
|
||||
const unsigned long init_flags,
|
||||
TwopassStatsStore *stats) const {
|
||||
#if CONFIG_VP9_ENCODER
|
||||
return new VP9Encoder(cfg, deadline, init_flags, stats);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)deadline;
|
||||
(void)init_flags;
|
||||
(void)stats;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual vpx_codec_err_t DefaultEncoderConfig(vpx_codec_enc_cfg_t *cfg,
|
||||
int usage) const {
|
||||
#if CONFIG_VP9_ENCODER
|
||||
return vpx_codec_enc_config_default(&vpx_codec_vp9_cx_algo, cfg, usage);
|
||||
#else
|
||||
(void)cfg;
|
||||
(void)usage;
|
||||
return VPX_CODEC_INCAPABLE;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
const libvpx_test::VP9CodecFactory kVP9;
|
||||
|
||||
#define VP9_INSTANTIATE_TEST_CASE(test, ...) \
|
||||
INSTANTIATE_TEST_CASE_P( \
|
||||
VP9, test, \
|
||||
::testing::Combine( \
|
||||
::testing::Values(static_cast<const libvpx_test::CodecFactory *>( \
|
||||
&libvpx_test::kVP9)), \
|
||||
__VA_ARGS__))
|
||||
#else
|
||||
#define VP9_INSTANTIATE_TEST_CASE(test, ...)
|
||||
#endif // CONFIG_VP9
|
||||
|
||||
} // namespace libvpx_test
|
||||
#endif // VPX_TEST_CODEC_FACTORY_H_
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
|
||||
#include "test/acm_random.h"
|
||||
#include "test/buffer.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using ::libvpx_test::ACMRandom;
|
||||
using ::libvpx_test::Buffer;
|
||||
|
||||
typedef void (*AvgPredFunc)(uint8_t *a, const uint8_t *b, int w, int h,
|
||||
const uint8_t *c, int c_stride);
|
||||
|
||||
uint8_t avg_with_rounding(uint8_t a, uint8_t b) { return (a + b + 1) >> 1; }
|
||||
|
||||
void reference_pred(const Buffer<uint8_t> &pred, const Buffer<uint8_t> &ref,
|
||||
int width, int height, Buffer<uint8_t> *avg) {
|
||||
ASSERT_TRUE(avg->TopLeftPixel() != NULL);
|
||||
ASSERT_TRUE(pred.TopLeftPixel() != NULL);
|
||||
ASSERT_TRUE(ref.TopLeftPixel() != NULL);
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
avg->TopLeftPixel()[y * avg->stride() + x] =
|
||||
avg_with_rounding(pred.TopLeftPixel()[y * pred.stride() + x],
|
||||
ref.TopLeftPixel()[y * ref.stride() + x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AvgPredTest : public ::testing::TestWithParam<AvgPredFunc> {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
avg_pred_func_ = GetParam();
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
}
|
||||
|
||||
protected:
|
||||
AvgPredFunc avg_pred_func_;
|
||||
ACMRandom rnd_;
|
||||
};
|
||||
|
||||
TEST_P(AvgPredTest, SizeCombinations) {
|
||||
// This is called as part of the sub pixel variance. As such it must be one of
|
||||
// the variance block sizes.
|
||||
|
||||
for (int width_pow = 2; width_pow <= 6; ++width_pow) {
|
||||
for (int height_pow = width_pow - 1; height_pow <= width_pow + 1;
|
||||
++height_pow) {
|
||||
// Don't test 4x2 or 64x128
|
||||
if (height_pow == 1 || height_pow == 7) continue;
|
||||
|
||||
// The sse2 special-cases when ref width == stride, so make sure to test
|
||||
// it.
|
||||
for (int ref_padding = 0; ref_padding < 2; ref_padding++) {
|
||||
const int width = 1 << width_pow;
|
||||
const int height = 1 << height_pow;
|
||||
// Only the reference buffer may have a stride not equal to width.
|
||||
Buffer<uint8_t> ref =
|
||||
Buffer<uint8_t>(width, height, ref_padding ? 8 : 0);
|
||||
ASSERT_TRUE(ref.Init());
|
||||
Buffer<uint8_t> pred = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(pred.Init());
|
||||
Buffer<uint8_t> avg_ref = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(avg_ref.Init());
|
||||
Buffer<uint8_t> avg_chk = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(avg_chk.Init());
|
||||
|
||||
ref.Set(&rnd_, &ACMRandom::Rand8);
|
||||
pred.Set(&rnd_, &ACMRandom::Rand8);
|
||||
|
||||
reference_pred(pred, ref, width, height, &avg_ref);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
avg_pred_func_(avg_chk.TopLeftPixel(), pred.TopLeftPixel(), width,
|
||||
height, ref.TopLeftPixel(), ref.stride()));
|
||||
|
||||
EXPECT_TRUE(avg_chk.CheckValues(avg_ref));
|
||||
if (HasFailure()) {
|
||||
printf("Width: %d Height: %d\n", width, height);
|
||||
avg_chk.PrintDifference(avg_ref);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AvgPredTest, CompareReferenceRandom) {
|
||||
const int width = 64;
|
||||
const int height = 32;
|
||||
Buffer<uint8_t> ref = Buffer<uint8_t>(width, height, 8);
|
||||
ASSERT_TRUE(ref.Init());
|
||||
Buffer<uint8_t> pred = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(pred.Init());
|
||||
Buffer<uint8_t> avg_ref = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(avg_ref.Init());
|
||||
Buffer<uint8_t> avg_chk = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(avg_chk.Init());
|
||||
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
ref.Set(&rnd_, &ACMRandom::Rand8);
|
||||
pred.Set(&rnd_, &ACMRandom::Rand8);
|
||||
|
||||
reference_pred(pred, ref, width, height, &avg_ref);
|
||||
ASM_REGISTER_STATE_CHECK(avg_pred_func_(avg_chk.TopLeftPixel(),
|
||||
pred.TopLeftPixel(), width, height,
|
||||
ref.TopLeftPixel(), ref.stride()));
|
||||
EXPECT_TRUE(avg_chk.CheckValues(avg_ref));
|
||||
if (HasFailure()) {
|
||||
printf("Width: %d Height: %d\n", width, height);
|
||||
avg_chk.PrintDifference(avg_ref);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(AvgPredTest, DISABLED_Speed) {
|
||||
for (int width_pow = 2; width_pow <= 6; ++width_pow) {
|
||||
for (int height_pow = width_pow - 1; height_pow <= width_pow + 1;
|
||||
++height_pow) {
|
||||
// Don't test 4x2 or 64x128
|
||||
if (height_pow == 1 || height_pow == 7) continue;
|
||||
|
||||
for (int ref_padding = 0; ref_padding < 2; ref_padding++) {
|
||||
const int width = 1 << width_pow;
|
||||
const int height = 1 << height_pow;
|
||||
Buffer<uint8_t> ref =
|
||||
Buffer<uint8_t>(width, height, ref_padding ? 8 : 0);
|
||||
ASSERT_TRUE(ref.Init());
|
||||
Buffer<uint8_t> pred = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(pred.Init());
|
||||
Buffer<uint8_t> avg = Buffer<uint8_t>(width, height, 0, 16);
|
||||
ASSERT_TRUE(avg.Init());
|
||||
|
||||
ref.Set(&rnd_, &ACMRandom::Rand8);
|
||||
pred.Set(&rnd_, &ACMRandom::Rand8);
|
||||
|
||||
vpx_usec_timer timer;
|
||||
vpx_usec_timer_start(&timer);
|
||||
for (int i = 0; i < 10000000 / (width * height); ++i) {
|
||||
avg_pred_func_(avg.TopLeftPixel(), pred.TopLeftPixel(), width, height,
|
||||
ref.TopLeftPixel(), ref.stride());
|
||||
}
|
||||
vpx_usec_timer_mark(&timer);
|
||||
|
||||
const int elapsed_time =
|
||||
static_cast<int>(vpx_usec_timer_elapsed(&timer));
|
||||
printf("Average Test (ref_padding: %d) %dx%d time: %5d us\n",
|
||||
ref_padding, width, height, elapsed_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(C, AvgPredTest,
|
||||
::testing::Values(&vpx_comp_avg_pred_c));
|
||||
|
||||
#if HAVE_SSE2
|
||||
INSTANTIATE_TEST_CASE_P(SSE2, AvgPredTest,
|
||||
::testing::Values(&vpx_comp_avg_pred_sse2));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
#if HAVE_NEON
|
||||
INSTANTIATE_TEST_CASE_P(NEON, AvgPredTest,
|
||||
::testing::Values(&vpx_comp_avg_pred_neon));
|
||||
#endif // HAVE_NEON
|
||||
|
||||
#if HAVE_VSX
|
||||
INSTANTIATE_TEST_CASE_P(VSX, AvgPredTest,
|
||||
::testing::Values(&vpx_comp_avg_pred_vsx));
|
||||
#endif // HAVE_VSX
|
||||
} // namespace
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/util.h"
|
||||
#include "test/video_source.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class ConfigTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
|
||||
protected:
|
||||
ConfigTest()
|
||||
: EncoderTest(GET_PARAM(0)), frame_count_in_(0), frame_count_out_(0),
|
||||
frame_count_max_(0) {}
|
||||
virtual ~ConfigTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(GET_PARAM(1));
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) {
|
||||
frame_count_in_ = 0;
|
||||
frame_count_out_ = 0;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(libvpx_test::VideoSource * /*video*/) {
|
||||
++frame_count_in_;
|
||||
abort_ |= (frame_count_in_ >= frame_count_max_);
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t * /*pkt*/) {
|
||||
++frame_count_out_;
|
||||
}
|
||||
|
||||
unsigned int frame_count_in_;
|
||||
unsigned int frame_count_out_;
|
||||
unsigned int frame_count_max_;
|
||||
};
|
||||
|
||||
TEST_P(ConfigTest, LagIsDisabled) {
|
||||
frame_count_max_ = 2;
|
||||
cfg_.g_lag_in_frames = 15;
|
||||
|
||||
libvpx_test::DummyVideoSource video;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
|
||||
EXPECT_EQ(frame_count_in_, frame_count_out_);
|
||||
}
|
||||
|
||||
VP8_INSTANTIATE_TEST_CASE(ConfigTest, ONE_PASS_TEST_MODES);
|
||||
} // namespace
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#if CONFIG_VP9_ENCODER
|
||||
#include "./vp9_rtcd.h"
|
||||
#endif
|
||||
|
||||
#include "test/acm_random.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vpx_dsp/ssim.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
|
||||
extern "C" double vpx_get_ssim_metrics(uint8_t *img1, int img1_pitch,
|
||||
uint8_t *img2, int img2_pitch, int width,
|
||||
int height, Ssimv *sv2, Metrics *m,
|
||||
int do_inconsistency);
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
|
||||
namespace {
|
||||
class ConsistencyTestBase : public ::testing::Test {
|
||||
public:
|
||||
ConsistencyTestBase(int width, int height) : width_(width), height_(height) {}
|
||||
|
||||
static void SetUpTestCase() {
|
||||
source_data_[0] = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
reference_data_[0] = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
source_data_[1] = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
reference_data_[1] = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(kDataAlignment, kDataBufferSize));
|
||||
ssim_array_ = new Ssimv[kDataBufferSize / 16];
|
||||
}
|
||||
|
||||
static void ClearSsim() { memset(ssim_array_, 0, kDataBufferSize / 16); }
|
||||
static void TearDownTestCase() {
|
||||
vpx_free(source_data_[0]);
|
||||
source_data_[0] = NULL;
|
||||
vpx_free(reference_data_[0]);
|
||||
reference_data_[0] = NULL;
|
||||
vpx_free(source_data_[1]);
|
||||
source_data_[1] = NULL;
|
||||
vpx_free(reference_data_[1]);
|
||||
reference_data_[1] = NULL;
|
||||
|
||||
delete[] ssim_array_;
|
||||
}
|
||||
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
// Handle frames up to 640x480
|
||||
static const int kDataAlignment = 16;
|
||||
static const int kDataBufferSize = 640 * 480;
|
||||
|
||||
virtual void SetUp() {
|
||||
source_stride_ = (width_ + 31) & ~31;
|
||||
reference_stride_ = width_ * 2;
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
}
|
||||
|
||||
void FillRandom(uint8_t *data, int stride, int width, int height) {
|
||||
for (int h = 0; h < height; ++h) {
|
||||
for (int w = 0; w < width; ++w) {
|
||||
data[h * stride + w] = rnd_.Rand8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillRandom(uint8_t *data, int stride) {
|
||||
FillRandom(data, stride, width_, height_);
|
||||
}
|
||||
|
||||
void Copy(uint8_t *reference, uint8_t *source) {
|
||||
memcpy(reference, source, kDataBufferSize);
|
||||
}
|
||||
|
||||
void Blur(uint8_t *data, int stride, int taps) {
|
||||
int sum = 0;
|
||||
int half_taps = taps / 2;
|
||||
for (int h = 0; h < height_; ++h) {
|
||||
for (int w = 0; w < taps; ++w) {
|
||||
sum += data[w + h * stride];
|
||||
}
|
||||
for (int w = taps; w < width_; ++w) {
|
||||
sum += data[w + h * stride] - data[w - taps + h * stride];
|
||||
data[w - half_taps + h * stride] = (sum + half_taps) / taps;
|
||||
}
|
||||
}
|
||||
for (int w = 0; w < width_; ++w) {
|
||||
for (int h = 0; h < taps; ++h) {
|
||||
sum += data[h + w * stride];
|
||||
}
|
||||
for (int h = taps; h < height_; ++h) {
|
||||
sum += data[w + h * stride] - data[(h - taps) * stride + w];
|
||||
data[(h - half_taps) * stride + w] = (sum + half_taps) / taps;
|
||||
}
|
||||
}
|
||||
}
|
||||
int width_, height_;
|
||||
static uint8_t *source_data_[2];
|
||||
int source_stride_;
|
||||
static uint8_t *reference_data_[2];
|
||||
int reference_stride_;
|
||||
static Ssimv *ssim_array_;
|
||||
Metrics metrics_;
|
||||
|
||||
ACMRandom rnd_;
|
||||
};
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
typedef std::tuple<int, int> ConsistencyParam;
|
||||
class ConsistencyVP9Test
|
||||
: public ConsistencyTestBase,
|
||||
public ::testing::WithParamInterface<ConsistencyParam> {
|
||||
public:
|
||||
ConsistencyVP9Test() : ConsistencyTestBase(GET_PARAM(0), GET_PARAM(1)) {}
|
||||
|
||||
protected:
|
||||
double CheckConsistency(int frame) {
|
||||
EXPECT_LT(frame, 2) << "Frame to check has to be less than 2.";
|
||||
return vpx_get_ssim_metrics(source_data_[frame], source_stride_,
|
||||
reference_data_[frame], reference_stride_,
|
||||
width_, height_, ssim_array_, &metrics_, 1);
|
||||
}
|
||||
};
|
||||
#endif // CONFIG_VP9_ENCODER
|
||||
|
||||
uint8_t *ConsistencyTestBase::source_data_[2] = { NULL, NULL };
|
||||
uint8_t *ConsistencyTestBase::reference_data_[2] = { NULL, NULL };
|
||||
Ssimv *ConsistencyTestBase::ssim_array_ = NULL;
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
TEST_P(ConsistencyVP9Test, ConsistencyIsZero) {
|
||||
FillRandom(source_data_[0], source_stride_);
|
||||
Copy(source_data_[1], source_data_[0]);
|
||||
Copy(reference_data_[0], source_data_[0]);
|
||||
Blur(reference_data_[0], reference_stride_, 3);
|
||||
Copy(reference_data_[1], source_data_[0]);
|
||||
Blur(reference_data_[1], reference_stride_, 3);
|
||||
|
||||
double inconsistency = CheckConsistency(1);
|
||||
inconsistency = CheckConsistency(0);
|
||||
EXPECT_EQ(inconsistency, 0.0)
|
||||
<< "Should have 0 inconsistency if they are exactly the same.";
|
||||
|
||||
// If sources are not consistent reference frames inconsistency should
|
||||
// be less than if the source is consistent.
|
||||
FillRandom(source_data_[0], source_stride_);
|
||||
FillRandom(source_data_[1], source_stride_);
|
||||
FillRandom(reference_data_[0], reference_stride_);
|
||||
FillRandom(reference_data_[1], reference_stride_);
|
||||
CheckConsistency(0);
|
||||
inconsistency = CheckConsistency(1);
|
||||
|
||||
Copy(source_data_[1], source_data_[0]);
|
||||
CheckConsistency(0);
|
||||
double inconsistency2 = CheckConsistency(1);
|
||||
EXPECT_LT(inconsistency, inconsistency2)
|
||||
<< "Should have less inconsistency if source itself is inconsistent.";
|
||||
|
||||
// Less of a blur should be less inconsistent than more blur coming off a
|
||||
// a frame with no blur.
|
||||
ClearSsim();
|
||||
FillRandom(source_data_[0], source_stride_);
|
||||
Copy(source_data_[1], source_data_[0]);
|
||||
Copy(reference_data_[0], source_data_[0]);
|
||||
Copy(reference_data_[1], source_data_[0]);
|
||||
Blur(reference_data_[1], reference_stride_, 4);
|
||||
CheckConsistency(0);
|
||||
inconsistency = CheckConsistency(1);
|
||||
ClearSsim();
|
||||
Copy(reference_data_[1], source_data_[0]);
|
||||
Blur(reference_data_[1], reference_stride_, 8);
|
||||
CheckConsistency(0);
|
||||
inconsistency2 = CheckConsistency(1);
|
||||
|
||||
EXPECT_LT(inconsistency, inconsistency2)
|
||||
<< "Stronger Blur should produce more inconsistency.";
|
||||
}
|
||||
#endif // CONFIG_VP9_ENCODER
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// C functions
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
const ConsistencyParam c_vp9_tests[] = { make_tuple(320, 240),
|
||||
make_tuple(318, 242),
|
||||
make_tuple(318, 238) };
|
||||
INSTANTIATE_TEST_CASE_P(C, ConsistencyVP9Test,
|
||||
::testing::ValuesIn(c_vp9_tests));
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
#include "test/y4m_video_source.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxPSNR = 100;
|
||||
|
||||
class CpuSpeedTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWith2Params<libvpx_test::TestMode, int> {
|
||||
protected:
|
||||
CpuSpeedTest()
|
||||
: EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)),
|
||||
set_cpu_used_(GET_PARAM(2)), min_psnr_(kMaxPSNR),
|
||||
tune_content_(VP9E_CONTENT_DEFAULT) {}
|
||||
virtual ~CpuSpeedTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(encoding_mode_);
|
||||
if (encoding_mode_ != ::libvpx_test::kRealTime) {
|
||||
cfg_.g_lag_in_frames = 25;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
} else {
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) { min_psnr_ = kMaxPSNR; }
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_);
|
||||
encoder->Control(VP9E_SET_TUNE_CONTENT, tune_content_);
|
||||
if (encoding_mode_ != ::libvpx_test::kRealTime) {
|
||||
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 1);
|
||||
encoder->Control(VP8E_SET_ARNR_MAXFRAMES, 7);
|
||||
encoder->Control(VP8E_SET_ARNR_STRENGTH, 5);
|
||||
encoder->Control(VP8E_SET_ARNR_TYPE, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
if (pkt->data.psnr.psnr[0] < min_psnr_) min_psnr_ = pkt->data.psnr.psnr[0];
|
||||
}
|
||||
|
||||
::libvpx_test::TestMode encoding_mode_;
|
||||
int set_cpu_used_;
|
||||
double min_psnr_;
|
||||
int tune_content_;
|
||||
};
|
||||
|
||||
TEST_P(CpuSpeedTest, TestQ0) {
|
||||
// Validate that this non multiple of 64 wide clip encodes and decodes
|
||||
// without a mismatch when passing in a very low max q. This pushes
|
||||
// the encoder to producing lots of big partitions which will likely
|
||||
// extend into the border and test the border condition.
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 400;
|
||||
cfg_.rc_max_quantizer = 0;
|
||||
cfg_.rc_min_quantizer = 0;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
|
||||
20);
|
||||
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
EXPECT_GE(min_psnr_, kMaxPSNR);
|
||||
}
|
||||
|
||||
TEST_P(CpuSpeedTest, TestScreencastQ0) {
|
||||
::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 25);
|
||||
cfg_.g_timebase = video.timebase();
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 400;
|
||||
cfg_.rc_max_quantizer = 0;
|
||||
cfg_.rc_min_quantizer = 0;
|
||||
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
EXPECT_GE(min_psnr_, kMaxPSNR);
|
||||
}
|
||||
|
||||
TEST_P(CpuSpeedTest, TestTuneScreen) {
|
||||
::libvpx_test::Y4mVideoSource video("screendata.y4m", 0, 25);
|
||||
cfg_.g_timebase = video.timebase();
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 2000;
|
||||
cfg_.rc_max_quantizer = 63;
|
||||
cfg_.rc_min_quantizer = 0;
|
||||
tune_content_ = VP9E_CONTENT_SCREEN;
|
||||
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
TEST_P(CpuSpeedTest, TestEncodeHighBitrate) {
|
||||
// Validate that this non multiple of 64 wide clip encodes and decodes
|
||||
// without a mismatch when passing in a very low max q. This pushes
|
||||
// the encoder to producing lots of big partitions which will likely
|
||||
// extend into the border and test the border condition.
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 12000;
|
||||
cfg_.rc_max_quantizer = 10;
|
||||
cfg_.rc_min_quantizer = 0;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
|
||||
20);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
TEST_P(CpuSpeedTest, TestLowBitrate) {
|
||||
// Validate that this clip encodes and decodes without a mismatch
|
||||
// when passing in a very high min q. This pushes the encoder to producing
|
||||
// lots of small partitions which might will test the other condition.
|
||||
cfg_.rc_2pass_vbr_minsection_pct = 5;
|
||||
cfg_.rc_2pass_vbr_maxsection_pct = 2000;
|
||||
cfg_.rc_target_bitrate = 200;
|
||||
cfg_.rc_min_quantizer = 40;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
|
||||
20);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(CpuSpeedTest,
|
||||
::testing::Values(::libvpx_test::kTwoPassGood,
|
||||
::libvpx_test::kOnePassGood,
|
||||
::libvpx_test::kRealTime),
|
||||
::testing::Range(0, 10));
|
||||
} // namespace
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 <cmath>
|
||||
#include <map>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// CQ level range: [kCQLevelMin, kCQLevelMax).
|
||||
const int kCQLevelMin = 4;
|
||||
const int kCQLevelMax = 63;
|
||||
const int kCQLevelStep = 8;
|
||||
const unsigned int kCQTargetBitrate = 2000;
|
||||
|
||||
class CQTest : public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<int> {
|
||||
public:
|
||||
// maps the cqlevel to the bitrate produced.
|
||||
typedef std::map<int, uint32_t> BitrateMap;
|
||||
|
||||
static void SetUpTestCase() { bitrates_.clear(); }
|
||||
|
||||
static void TearDownTestCase() {
|
||||
ASSERT_TRUE(!HasFailure())
|
||||
<< "skipping bitrate validation due to earlier failure.";
|
||||
uint32_t prev_actual_bitrate = kCQTargetBitrate;
|
||||
for (BitrateMap::const_iterator iter = bitrates_.begin();
|
||||
iter != bitrates_.end(); ++iter) {
|
||||
const uint32_t cq_actual_bitrate = iter->second;
|
||||
EXPECT_LE(cq_actual_bitrate, prev_actual_bitrate)
|
||||
<< "cq_level: " << iter->first
|
||||
<< ", bitrate should decrease with increase in CQ level.";
|
||||
prev_actual_bitrate = cq_actual_bitrate;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
CQTest() : EncoderTest(GET_PARAM(0)), cq_level_(GET_PARAM(1)) {
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
}
|
||||
|
||||
virtual ~CQTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(libvpx_test::kTwoPassGood);
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) {
|
||||
file_size_ = 0;
|
||||
psnr_ = 0.0;
|
||||
n_frames_ = 0;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
|
||||
libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
if (cfg_.rc_end_usage == VPX_CQ) {
|
||||
encoder->Control(VP8E_SET_CQ_LEVEL, cq_level_);
|
||||
}
|
||||
encoder->Control(VP8E_SET_CPUUSED, 3);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
psnr_ += pow(10.0, pkt->data.psnr.psnr[0] / 10.0);
|
||||
n_frames_++;
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
file_size_ += pkt->data.frame.sz;
|
||||
}
|
||||
|
||||
double GetLinearPSNROverBitrate() const {
|
||||
double avg_psnr = log10(psnr_ / n_frames_) * 10.0;
|
||||
return pow(10.0, avg_psnr / 10.0) / file_size_;
|
||||
}
|
||||
|
||||
int cq_level() const { return cq_level_; }
|
||||
size_t file_size() const { return file_size_; }
|
||||
int n_frames() const { return n_frames_; }
|
||||
|
||||
static BitrateMap bitrates_;
|
||||
|
||||
private:
|
||||
int cq_level_;
|
||||
size_t file_size_;
|
||||
double psnr_;
|
||||
int n_frames_;
|
||||
};
|
||||
|
||||
CQTest::BitrateMap CQTest::bitrates_;
|
||||
|
||||
TEST_P(CQTest, LinearPSNRIsHigherForCQLevel) {
|
||||
const vpx_rational timebase = { 33333333, 1000000000 };
|
||||
cfg_.g_timebase = timebase;
|
||||
cfg_.rc_target_bitrate = kCQTargetBitrate;
|
||||
cfg_.g_lag_in_frames = 25;
|
||||
|
||||
cfg_.rc_end_usage = VPX_CQ;
|
||||
libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
timebase.den, timebase.num, 0, 30);
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
const double cq_psnr_lin = GetLinearPSNROverBitrate();
|
||||
const unsigned int cq_actual_bitrate =
|
||||
static_cast<unsigned int>(file_size()) * 8 * 30 / (n_frames() * 1000);
|
||||
EXPECT_LE(cq_actual_bitrate, kCQTargetBitrate);
|
||||
bitrates_[cq_level()] = cq_actual_bitrate;
|
||||
|
||||
// try targeting the approximate same bitrate with VBR mode
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
cfg_.rc_target_bitrate = cq_actual_bitrate;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
const double vbr_psnr_lin = GetLinearPSNROverBitrate();
|
||||
EXPECT_GE(cq_psnr_lin, vbr_psnr_lin);
|
||||
}
|
||||
|
||||
VP8_INSTANTIATE_TEST_CASE(CQTest, ::testing::Range(kCQLevelMin, kCQLevelMax,
|
||||
kCQLevelStep));
|
||||
} // namespace
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
##
|
||||
## Copyright (c) 2016 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 the libvpx cx_set_ref example. To add new tests to this
|
||||
## file, do the following:
|
||||
## 1. Write a shell function (this is your test).
|
||||
## 2. Add the function to cx_set_ref_tests (on a new line).
|
||||
##
|
||||
. $(dirname $0)/tools_common.sh
|
||||
|
||||
# Environment check: $YUV_RAW_INPUT is required.
|
||||
cx_set_ref_verify_environment() {
|
||||
if [ ! -e "${YUV_RAW_INPUT}" ]; then
|
||||
echo "Libvpx test data must exist in LIBVPX_TEST_DATA_PATH."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs cx_set_ref and updates the reference frame before encoding frame 90.
|
||||
# $1 is the codec name.
|
||||
vpx_set_ref() {
|
||||
local codec="$1"
|
||||
local encoder="${LIBVPX_BIN_PATH}/${codec}cx_set_ref${VPX_TEST_EXE_SUFFIX}"
|
||||
local output_file="${VPX_TEST_OUTPUT_DIR}/${codec}cx_set_ref_${codec}.ivf"
|
||||
local ref_frame_num=90
|
||||
|
||||
if [ ! -x "${encoder}" ]; then
|
||||
elog "${encoder} does not exist or is not executable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "${VPX_TEST_PREFIX}" "${encoder}" "${YUV_RAW_INPUT_WIDTH}" \
|
||||
"${YUV_RAW_INPUT_HEIGHT}" "${YUV_RAW_INPUT}" "${output_file}" \
|
||||
"${ref_frame_num}" ${devnull}
|
||||
|
||||
[ -e "${output_file}" ] || return 1
|
||||
}
|
||||
|
||||
cx_set_ref_vp8() {
|
||||
if [ "$(vp8_encode_available)" = "yes" ]; then
|
||||
vpx_set_ref vp8 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cx_set_ref_vp9() {
|
||||
if [ "$(vp9_encode_available)" = "yes" ]; then
|
||||
vpx_set_ref vp9 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cx_set_ref_tests="cx_set_ref_vp8 cx_set_ref_vp9"
|
||||
|
||||
run_tests cx_set_ref_verify_environment "${cx_set_ref_tests}"
|
||||
@@ -0,0 +1,868 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vp9_rtcd.h"
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "test/acm_random.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vp9/common/vp9_entropy.h"
|
||||
#include "vp9/common/vp9_scan.h"
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_ports/mem.h"
|
||||
#include "vpx_ports/msvc.h" // for round()
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
|
||||
namespace {
|
||||
|
||||
const int kNumCoeffs = 256;
|
||||
const double C1 = 0.995184726672197;
|
||||
const double C2 = 0.98078528040323;
|
||||
const double C3 = 0.956940335732209;
|
||||
const double C4 = 0.923879532511287;
|
||||
const double C5 = 0.881921264348355;
|
||||
const double C6 = 0.831469612302545;
|
||||
const double C7 = 0.773010453362737;
|
||||
const double C8 = 0.707106781186548;
|
||||
const double C9 = 0.634393284163646;
|
||||
const double C10 = 0.555570233019602;
|
||||
const double C11 = 0.471396736825998;
|
||||
const double C12 = 0.38268343236509;
|
||||
const double C13 = 0.290284677254462;
|
||||
const double C14 = 0.195090322016128;
|
||||
const double C15 = 0.098017140329561;
|
||||
|
||||
void butterfly_16x16_dct_1d(double input[16], double output[16]) {
|
||||
double step[16];
|
||||
double intermediate[16];
|
||||
double temp1, temp2;
|
||||
|
||||
// step 1
|
||||
step[0] = input[0] + input[15];
|
||||
step[1] = input[1] + input[14];
|
||||
step[2] = input[2] + input[13];
|
||||
step[3] = input[3] + input[12];
|
||||
step[4] = input[4] + input[11];
|
||||
step[5] = input[5] + input[10];
|
||||
step[6] = input[6] + input[9];
|
||||
step[7] = input[7] + input[8];
|
||||
step[8] = input[7] - input[8];
|
||||
step[9] = input[6] - input[9];
|
||||
step[10] = input[5] - input[10];
|
||||
step[11] = input[4] - input[11];
|
||||
step[12] = input[3] - input[12];
|
||||
step[13] = input[2] - input[13];
|
||||
step[14] = input[1] - input[14];
|
||||
step[15] = input[0] - input[15];
|
||||
|
||||
// step 2
|
||||
output[0] = step[0] + step[7];
|
||||
output[1] = step[1] + step[6];
|
||||
output[2] = step[2] + step[5];
|
||||
output[3] = step[3] + step[4];
|
||||
output[4] = step[3] - step[4];
|
||||
output[5] = step[2] - step[5];
|
||||
output[6] = step[1] - step[6];
|
||||
output[7] = step[0] - step[7];
|
||||
|
||||
temp1 = step[8] * C7;
|
||||
temp2 = step[15] * C9;
|
||||
output[8] = temp1 + temp2;
|
||||
|
||||
temp1 = step[9] * C11;
|
||||
temp2 = step[14] * C5;
|
||||
output[9] = temp1 - temp2;
|
||||
|
||||
temp1 = step[10] * C3;
|
||||
temp2 = step[13] * C13;
|
||||
output[10] = temp1 + temp2;
|
||||
|
||||
temp1 = step[11] * C15;
|
||||
temp2 = step[12] * C1;
|
||||
output[11] = temp1 - temp2;
|
||||
|
||||
temp1 = step[11] * C1;
|
||||
temp2 = step[12] * C15;
|
||||
output[12] = temp2 + temp1;
|
||||
|
||||
temp1 = step[10] * C13;
|
||||
temp2 = step[13] * C3;
|
||||
output[13] = temp2 - temp1;
|
||||
|
||||
temp1 = step[9] * C5;
|
||||
temp2 = step[14] * C11;
|
||||
output[14] = temp2 + temp1;
|
||||
|
||||
temp1 = step[8] * C9;
|
||||
temp2 = step[15] * C7;
|
||||
output[15] = temp2 - temp1;
|
||||
|
||||
// step 3
|
||||
step[0] = output[0] + output[3];
|
||||
step[1] = output[1] + output[2];
|
||||
step[2] = output[1] - output[2];
|
||||
step[3] = output[0] - output[3];
|
||||
|
||||
temp1 = output[4] * C14;
|
||||
temp2 = output[7] * C2;
|
||||
step[4] = temp1 + temp2;
|
||||
|
||||
temp1 = output[5] * C10;
|
||||
temp2 = output[6] * C6;
|
||||
step[5] = temp1 + temp2;
|
||||
|
||||
temp1 = output[5] * C6;
|
||||
temp2 = output[6] * C10;
|
||||
step[6] = temp2 - temp1;
|
||||
|
||||
temp1 = output[4] * C2;
|
||||
temp2 = output[7] * C14;
|
||||
step[7] = temp2 - temp1;
|
||||
|
||||
step[8] = output[8] + output[11];
|
||||
step[9] = output[9] + output[10];
|
||||
step[10] = output[9] - output[10];
|
||||
step[11] = output[8] - output[11];
|
||||
|
||||
step[12] = output[12] + output[15];
|
||||
step[13] = output[13] + output[14];
|
||||
step[14] = output[13] - output[14];
|
||||
step[15] = output[12] - output[15];
|
||||
|
||||
// step 4
|
||||
output[0] = (step[0] + step[1]);
|
||||
output[8] = (step[0] - step[1]);
|
||||
|
||||
temp1 = step[2] * C12;
|
||||
temp2 = step[3] * C4;
|
||||
temp1 = temp1 + temp2;
|
||||
output[4] = 2 * (temp1 * C8);
|
||||
|
||||
temp1 = step[2] * C4;
|
||||
temp2 = step[3] * C12;
|
||||
temp1 = temp2 - temp1;
|
||||
output[12] = 2 * (temp1 * C8);
|
||||
|
||||
output[2] = 2 * ((step[4] + step[5]) * C8);
|
||||
output[14] = 2 * ((step[7] - step[6]) * C8);
|
||||
|
||||
temp1 = step[4] - step[5];
|
||||
temp2 = step[6] + step[7];
|
||||
output[6] = (temp1 + temp2);
|
||||
output[10] = (temp1 - temp2);
|
||||
|
||||
intermediate[8] = step[8] + step[14];
|
||||
intermediate[9] = step[9] + step[15];
|
||||
|
||||
temp1 = intermediate[8] * C12;
|
||||
temp2 = intermediate[9] * C4;
|
||||
temp1 = temp1 - temp2;
|
||||
output[3] = 2 * (temp1 * C8);
|
||||
|
||||
temp1 = intermediate[8] * C4;
|
||||
temp2 = intermediate[9] * C12;
|
||||
temp1 = temp2 + temp1;
|
||||
output[13] = 2 * (temp1 * C8);
|
||||
|
||||
output[9] = 2 * ((step[10] + step[11]) * C8);
|
||||
|
||||
intermediate[11] = step[10] - step[11];
|
||||
intermediate[12] = step[12] + step[13];
|
||||
intermediate[13] = step[12] - step[13];
|
||||
intermediate[14] = step[8] - step[14];
|
||||
intermediate[15] = step[9] - step[15];
|
||||
|
||||
output[15] = (intermediate[11] + intermediate[12]);
|
||||
output[1] = -(intermediate[11] - intermediate[12]);
|
||||
|
||||
output[7] = 2 * (intermediate[13] * C8);
|
||||
|
||||
temp1 = intermediate[14] * C12;
|
||||
temp2 = intermediate[15] * C4;
|
||||
temp1 = temp1 - temp2;
|
||||
output[11] = -2 * (temp1 * C8);
|
||||
|
||||
temp1 = intermediate[14] * C4;
|
||||
temp2 = intermediate[15] * C12;
|
||||
temp1 = temp2 + temp1;
|
||||
output[5] = 2 * (temp1 * C8);
|
||||
}
|
||||
|
||||
void reference_16x16_dct_2d(int16_t input[256], double output[256]) {
|
||||
// First transform columns
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
double temp_in[16], temp_out[16];
|
||||
for (int j = 0; j < 16; ++j) temp_in[j] = input[j * 16 + i];
|
||||
butterfly_16x16_dct_1d(temp_in, temp_out);
|
||||
for (int j = 0; j < 16; ++j) output[j * 16 + i] = temp_out[j];
|
||||
}
|
||||
// Then transform rows
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
double temp_in[16], temp_out[16];
|
||||
for (int j = 0; j < 16; ++j) temp_in[j] = output[j + i * 16];
|
||||
butterfly_16x16_dct_1d(temp_in, temp_out);
|
||||
// Scale by some magic number
|
||||
for (int j = 0; j < 16; ++j) output[j + i * 16] = temp_out[j] / 2;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*FdctFunc)(const int16_t *in, tran_low_t *out, int stride);
|
||||
typedef void (*IdctFunc)(const tran_low_t *in, uint8_t *out, int stride);
|
||||
typedef void (*FhtFunc)(const int16_t *in, tran_low_t *out, int stride,
|
||||
int tx_type);
|
||||
typedef void (*IhtFunc)(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int tx_type);
|
||||
|
||||
typedef std::tuple<FdctFunc, IdctFunc, int, vpx_bit_depth_t> Dct16x16Param;
|
||||
typedef std::tuple<FhtFunc, IhtFunc, int, vpx_bit_depth_t> Ht16x16Param;
|
||||
typedef std::tuple<IdctFunc, IdctFunc, int, vpx_bit_depth_t> Idct16x16Param;
|
||||
|
||||
void fdct16x16_ref(const int16_t *in, tran_low_t *out, int stride,
|
||||
int /*tx_type*/) {
|
||||
vpx_fdct16x16_c(in, out, stride);
|
||||
}
|
||||
|
||||
void idct16x16_ref(const tran_low_t *in, uint8_t *dest, int stride,
|
||||
int /*tx_type*/) {
|
||||
vpx_idct16x16_256_add_c(in, dest, stride);
|
||||
}
|
||||
|
||||
void fht16x16_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
|
||||
vp9_fht16x16_c(in, out, stride, tx_type);
|
||||
}
|
||||
|
||||
void iht16x16_ref(const tran_low_t *in, uint8_t *dest, int stride,
|
||||
int tx_type) {
|
||||
vp9_iht16x16_256_add_c(in, dest, stride, tx_type);
|
||||
}
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
void idct16x16_10(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_256_add_c(in, CAST_TO_SHORTPTR(out), stride, 10);
|
||||
}
|
||||
|
||||
void idct16x16_12(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_256_add_c(in, CAST_TO_SHORTPTR(out), stride, 12);
|
||||
}
|
||||
|
||||
void idct16x16_10_ref(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int /*tx_type*/) {
|
||||
idct16x16_10(in, out, stride);
|
||||
}
|
||||
|
||||
void idct16x16_12_ref(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int /*tx_type*/) {
|
||||
idct16x16_12(in, out, stride);
|
||||
}
|
||||
|
||||
void iht16x16_10(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
|
||||
vp9_highbd_iht16x16_256_add_c(in, CAST_TO_SHORTPTR(out), stride, tx_type, 10);
|
||||
}
|
||||
|
||||
void iht16x16_12(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
|
||||
vp9_highbd_iht16x16_256_add_c(in, CAST_TO_SHORTPTR(out), stride, tx_type, 12);
|
||||
}
|
||||
|
||||
#if HAVE_SSE2
|
||||
void idct16x16_10_add_10_c(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_10_add_c(in, CAST_TO_SHORTPTR(out), stride, 10);
|
||||
}
|
||||
|
||||
void idct16x16_10_add_12_c(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_10_add_c(in, CAST_TO_SHORTPTR(out), stride, 12);
|
||||
}
|
||||
|
||||
void idct16x16_256_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_256_add_sse2(in, CAST_TO_SHORTPTR(out), stride, 10);
|
||||
}
|
||||
|
||||
void idct16x16_256_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_256_add_sse2(in, CAST_TO_SHORTPTR(out), stride, 12);
|
||||
}
|
||||
|
||||
void idct16x16_10_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_10_add_sse2(in, CAST_TO_SHORTPTR(out), stride, 10);
|
||||
}
|
||||
|
||||
void idct16x16_10_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct16x16_10_add_sse2(in, CAST_TO_SHORTPTR(out), stride, 12);
|
||||
}
|
||||
#endif // HAVE_SSE2
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
class Trans16x16TestBase {
|
||||
public:
|
||||
virtual ~Trans16x16TestBase() {}
|
||||
|
||||
protected:
|
||||
virtual void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) = 0;
|
||||
|
||||
virtual void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) = 0;
|
||||
|
||||
void RunAccuracyCheck() {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
uint32_t max_error = 0;
|
||||
int64_t total_error = 0;
|
||||
const int count_test_block = 10000;
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
DECLARE_ALIGNED(16, int16_t, test_input_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, test_temp_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, src[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, src16[kNumCoeffs]);
|
||||
#endif
|
||||
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
src[j] = rnd.Rand8();
|
||||
dst[j] = rnd.Rand8();
|
||||
test_input_block[j] = src[j] - dst[j];
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
src16[j] = rnd.Rand16() & mask_;
|
||||
dst16[j] = rnd.Rand16() & mask_;
|
||||
test_input_block[j] = src16[j] - dst16[j];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
RunFwdTxfm(test_input_block, test_temp_block, pitch_));
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(test_temp_block, dst, pitch_));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
RunInvTxfm(test_temp_block, CAST_TO_BYTEPTR(dst16), pitch_));
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
const int32_t diff =
|
||||
bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
|
||||
#else
|
||||
const int32_t diff = dst[j] - src[j];
|
||||
#endif
|
||||
const uint32_t error = diff * diff;
|
||||
if (max_error < error) max_error = error;
|
||||
total_error += error;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_GE(1u << 2 * (bit_depth_ - 8), max_error)
|
||||
<< "Error: 16x16 FHT/IHT has an individual round trip error > 1";
|
||||
|
||||
EXPECT_GE(count_test_block << 2 * (bit_depth_ - 8), total_error)
|
||||
<< "Error: 16x16 FHT/IHT has average round trip error > 1 per block";
|
||||
}
|
||||
|
||||
void RunCoeffCheck() {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
DECLARE_ALIGNED(16, int16_t, input_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_block[kNumCoeffs]);
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
input_block[j] = (rnd.Rand16() & mask_) - (rnd.Rand16() & mask_);
|
||||
}
|
||||
|
||||
fwd_txfm_ref(input_block, output_ref_block, pitch_, tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(RunFwdTxfm(input_block, output_block, pitch_));
|
||||
|
||||
// The minimum quant value is 4.
|
||||
for (int j = 0; j < kNumCoeffs; ++j)
|
||||
EXPECT_EQ(output_block[j], output_ref_block[j]);
|
||||
}
|
||||
}
|
||||
|
||||
void RunMemCheck() {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
DECLARE_ALIGNED(16, int16_t, input_extreme_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_block[kNumCoeffs]);
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
input_extreme_block[j] = rnd.Rand8() % 2 ? mask_ : -mask_;
|
||||
}
|
||||
if (i == 0) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = mask_;
|
||||
} else if (i == 1) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = -mask_;
|
||||
}
|
||||
|
||||
fwd_txfm_ref(input_extreme_block, output_ref_block, pitch_, tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
RunFwdTxfm(input_extreme_block, output_block, pitch_));
|
||||
|
||||
// The minimum quant value is 4.
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
EXPECT_EQ(output_block[j], output_ref_block[j]);
|
||||
EXPECT_GE(4 * DCT_MAX_VALUE << (bit_depth_ - 8), abs(output_block[j]))
|
||||
<< "Error: 16x16 FDCT has coefficient larger than 4*DCT_MAX_VALUE";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunQuantCheck(int dc_thred, int ac_thred) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 100000;
|
||||
DECLARE_ALIGNED(16, int16_t, input_extreme_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kNumCoeffs]);
|
||||
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, ref[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, ref16[kNumCoeffs]);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
input_extreme_block[j] = rnd.Rand8() % 2 ? mask_ : -mask_;
|
||||
}
|
||||
if (i == 0) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = mask_;
|
||||
}
|
||||
if (i == 1) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = -mask_;
|
||||
}
|
||||
|
||||
fwd_txfm_ref(input_extreme_block, output_ref_block, pitch_, tx_type_);
|
||||
|
||||
// clear reconstructed pixel buffers
|
||||
memset(dst, 0, kNumCoeffs * sizeof(uint8_t));
|
||||
memset(ref, 0, kNumCoeffs * sizeof(uint8_t));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
memset(dst16, 0, kNumCoeffs * sizeof(uint16_t));
|
||||
memset(ref16, 0, kNumCoeffs * sizeof(uint16_t));
|
||||
#endif
|
||||
|
||||
// quantization with maximum allowed step sizes
|
||||
output_ref_block[0] = (output_ref_block[0] / dc_thred) * dc_thred;
|
||||
for (int j = 1; j < kNumCoeffs; ++j) {
|
||||
output_ref_block[j] = (output_ref_block[j] / ac_thred) * ac_thred;
|
||||
}
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
inv_txfm_ref(output_ref_block, ref, pitch_, tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(output_ref_block, dst, pitch_));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
inv_txfm_ref(output_ref_block, CAST_TO_BYTEPTR(ref16), pitch_,
|
||||
tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
RunInvTxfm(output_ref_block, CAST_TO_BYTEPTR(dst16), pitch_));
|
||||
#endif
|
||||
}
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) EXPECT_EQ(ref[j], dst[j]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) EXPECT_EQ(ref16[j], dst16[j]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunInvAccuracyCheck() {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
DECLARE_ALIGNED(16, int16_t, in[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, src[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, src16[kNumCoeffs]);
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
double out_r[kNumCoeffs];
|
||||
|
||||
// Initialize a test block with input range [-255, 255].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
src[j] = rnd.Rand8();
|
||||
dst[j] = rnd.Rand8();
|
||||
in[j] = src[j] - dst[j];
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
src16[j] = rnd.Rand16() & mask_;
|
||||
dst16[j] = rnd.Rand16() & mask_;
|
||||
in[j] = src16[j] - dst16[j];
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
}
|
||||
}
|
||||
|
||||
reference_16x16_dct_2d(in, out_r);
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
coeff[j] = static_cast<tran_low_t>(round(out_r[j]));
|
||||
}
|
||||
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, 16));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, CAST_TO_BYTEPTR(dst16), 16));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
}
|
||||
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
const uint32_t diff =
|
||||
bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
|
||||
#else
|
||||
const uint32_t diff = dst[j] - src[j];
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
const uint32_t error = diff * diff;
|
||||
EXPECT_GE(1u, error)
|
||||
<< "Error: 16x16 IDCT has error " << error << " at index " << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompareInvReference(IdctFunc ref_txfm, int thresh) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 10000;
|
||||
const int eob = 10;
|
||||
const int16_t *scan = vp9_default_scan_orders[TX_16X16].scan;
|
||||
DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, ref[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, ref16[kNumCoeffs]);
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (j < eob) {
|
||||
// Random values less than the threshold, either positive or negative
|
||||
coeff[scan[j]] = rnd(thresh) * (1 - 2 * (i % 2));
|
||||
} else {
|
||||
coeff[scan[j]] = 0;
|
||||
}
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
dst[j] = 0;
|
||||
ref[j] = 0;
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
dst16[j] = 0;
|
||||
ref16[j] = 0;
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
}
|
||||
}
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
ref_txfm(coeff, ref, pitch_);
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, pitch_));
|
||||
} else {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
ref_txfm(coeff, CAST_TO_BYTEPTR(ref16), pitch_);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
RunInvTxfm(coeff, CAST_TO_BYTEPTR(dst16), pitch_));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
}
|
||||
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
const uint32_t diff =
|
||||
bit_depth_ == VPX_BITS_8 ? dst[j] - ref[j] : dst16[j] - ref16[j];
|
||||
#else
|
||||
const uint32_t diff = dst[j] - ref[j];
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
const uint32_t error = diff * diff;
|
||||
EXPECT_EQ(0u, error) << "Error: 16x16 IDCT Comparison has error "
|
||||
<< error << " at index " << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pitch_;
|
||||
int tx_type_;
|
||||
vpx_bit_depth_t bit_depth_;
|
||||
int mask_;
|
||||
FhtFunc fwd_txfm_ref;
|
||||
IhtFunc inv_txfm_ref;
|
||||
};
|
||||
|
||||
class Trans16x16DCT : public Trans16x16TestBase,
|
||||
public ::testing::TestWithParam<Dct16x16Param> {
|
||||
public:
|
||||
virtual ~Trans16x16DCT() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
fwd_txfm_ = GET_PARAM(0);
|
||||
inv_txfm_ = GET_PARAM(1);
|
||||
tx_type_ = GET_PARAM(2);
|
||||
bit_depth_ = GET_PARAM(3);
|
||||
pitch_ = 16;
|
||||
fwd_txfm_ref = fdct16x16_ref;
|
||||
inv_txfm_ref = idct16x16_ref;
|
||||
mask_ = (1 << bit_depth_) - 1;
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
switch (bit_depth_) {
|
||||
case VPX_BITS_10: inv_txfm_ref = idct16x16_10_ref; break;
|
||||
case VPX_BITS_12: inv_txfm_ref = idct16x16_12_ref; break;
|
||||
default: inv_txfm_ref = idct16x16_ref; break;
|
||||
}
|
||||
#else
|
||||
inv_txfm_ref = idct16x16_ref;
|
||||
#endif
|
||||
}
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
|
||||
fwd_txfm_(in, out, stride);
|
||||
}
|
||||
void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
|
||||
inv_txfm_(out, dst, stride);
|
||||
}
|
||||
|
||||
FdctFunc fwd_txfm_;
|
||||
IdctFunc inv_txfm_;
|
||||
};
|
||||
|
||||
TEST_P(Trans16x16DCT, AccuracyCheck) { RunAccuracyCheck(); }
|
||||
|
||||
TEST_P(Trans16x16DCT, CoeffCheck) { RunCoeffCheck(); }
|
||||
|
||||
TEST_P(Trans16x16DCT, MemCheck) { RunMemCheck(); }
|
||||
|
||||
TEST_P(Trans16x16DCT, QuantCheck) {
|
||||
// Use maximally allowed quantization step sizes for DC and AC
|
||||
// coefficients respectively.
|
||||
RunQuantCheck(1336, 1828);
|
||||
}
|
||||
|
||||
TEST_P(Trans16x16DCT, InvAccuracyCheck) { RunInvAccuracyCheck(); }
|
||||
|
||||
class Trans16x16HT : public Trans16x16TestBase,
|
||||
public ::testing::TestWithParam<Ht16x16Param> {
|
||||
public:
|
||||
virtual ~Trans16x16HT() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
fwd_txfm_ = GET_PARAM(0);
|
||||
inv_txfm_ = GET_PARAM(1);
|
||||
tx_type_ = GET_PARAM(2);
|
||||
bit_depth_ = GET_PARAM(3);
|
||||
pitch_ = 16;
|
||||
fwd_txfm_ref = fht16x16_ref;
|
||||
inv_txfm_ref = iht16x16_ref;
|
||||
mask_ = (1 << bit_depth_) - 1;
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
switch (bit_depth_) {
|
||||
case VPX_BITS_10: inv_txfm_ref = iht16x16_10; break;
|
||||
case VPX_BITS_12: inv_txfm_ref = iht16x16_12; break;
|
||||
default: inv_txfm_ref = iht16x16_ref; break;
|
||||
}
|
||||
#else
|
||||
inv_txfm_ref = iht16x16_ref;
|
||||
#endif
|
||||
}
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
|
||||
fwd_txfm_(in, out, stride, tx_type_);
|
||||
}
|
||||
void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
|
||||
inv_txfm_(out, dst, stride, tx_type_);
|
||||
}
|
||||
|
||||
FhtFunc fwd_txfm_;
|
||||
IhtFunc inv_txfm_;
|
||||
};
|
||||
|
||||
TEST_P(Trans16x16HT, AccuracyCheck) { RunAccuracyCheck(); }
|
||||
|
||||
TEST_P(Trans16x16HT, CoeffCheck) { RunCoeffCheck(); }
|
||||
|
||||
TEST_P(Trans16x16HT, MemCheck) { RunMemCheck(); }
|
||||
|
||||
TEST_P(Trans16x16HT, QuantCheck) {
|
||||
// The encoder skips any non-DC intra prediction modes,
|
||||
// when the quantization step size goes beyond 988.
|
||||
RunQuantCheck(429, 729);
|
||||
}
|
||||
|
||||
class InvTrans16x16DCT : public Trans16x16TestBase,
|
||||
public ::testing::TestWithParam<Idct16x16Param> {
|
||||
public:
|
||||
virtual ~InvTrans16x16DCT() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
ref_txfm_ = GET_PARAM(0);
|
||||
inv_txfm_ = GET_PARAM(1);
|
||||
thresh_ = GET_PARAM(2);
|
||||
bit_depth_ = GET_PARAM(3);
|
||||
pitch_ = 16;
|
||||
mask_ = (1 << bit_depth_) - 1;
|
||||
}
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
void RunFwdTxfm(int16_t * /*in*/, tran_low_t * /*out*/, int /*stride*/) {}
|
||||
void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
|
||||
inv_txfm_(out, dst, stride);
|
||||
}
|
||||
|
||||
IdctFunc ref_txfm_;
|
||||
IdctFunc inv_txfm_;
|
||||
int thresh_;
|
||||
};
|
||||
|
||||
TEST_P(InvTrans16x16DCT, CompareReference) {
|
||||
CompareInvReference(ref_txfm_, thresh_);
|
||||
}
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, Trans16x16DCT,
|
||||
::testing::Values(
|
||||
make_tuple(&vpx_highbd_fdct16x16_c, &idct16x16_10, 0, VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct16x16_c, &idct16x16_12, 0, VPX_BITS_12),
|
||||
make_tuple(&vpx_fdct16x16_c, &vpx_idct16x16_256_add_c, 0, VPX_BITS_8)));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(C, Trans16x16DCT,
|
||||
::testing::Values(make_tuple(&vpx_fdct16x16_c,
|
||||
&vpx_idct16x16_256_add_c,
|
||||
0, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, Trans16x16HT,
|
||||
::testing::Values(
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_10, 0, VPX_BITS_10),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_10, 1, VPX_BITS_10),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_10, 2, VPX_BITS_10),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_10, 3, VPX_BITS_10),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_12, 0, VPX_BITS_12),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_12, 1, VPX_BITS_12),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_12, 2, VPX_BITS_12),
|
||||
make_tuple(&vp9_highbd_fht16x16_c, &iht16x16_12, 3, VPX_BITS_12),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 0, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 1, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 2, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 3, VPX_BITS_8)));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, Trans16x16HT,
|
||||
::testing::Values(
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 0, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 1, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 2, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_c, &vp9_iht16x16_256_add_c, 3, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_NEON && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, Trans16x16DCT,
|
||||
::testing::Values(make_tuple(&vpx_fdct16x16_neon,
|
||||
&vpx_idct16x16_256_add_neon, 0, VPX_BITS_8)));
|
||||
#endif // HAVE_NEON && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans16x16DCT,
|
||||
::testing::Values(make_tuple(&vpx_fdct16x16_sse2,
|
||||
&vpx_idct16x16_256_add_sse2, 0, VPX_BITS_8)));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans16x16HT,
|
||||
::testing::Values(make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_sse2,
|
||||
0, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_sse2,
|
||||
1, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_sse2,
|
||||
2, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_sse2,
|
||||
3, VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans16x16DCT,
|
||||
::testing::Values(
|
||||
make_tuple(&vpx_highbd_fdct16x16_sse2, &idct16x16_10, 0, VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct16x16_c, &idct16x16_256_add_10_sse2, 0,
|
||||
VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct16x16_sse2, &idct16x16_12, 0, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct16x16_c, &idct16x16_256_add_12_sse2, 0,
|
||||
VPX_BITS_12),
|
||||
make_tuple(&vpx_fdct16x16_sse2, &vpx_idct16x16_256_add_c, 0,
|
||||
VPX_BITS_8)));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans16x16HT,
|
||||
::testing::Values(
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_c, 0, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_c, 1, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_c, 2, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_sse2, &vp9_iht16x16_256_add_c, 3,
|
||||
VPX_BITS_8)));
|
||||
// Optimizations take effect at a threshold of 3155, so we use a value close to
|
||||
// that to test both branches.
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, InvTrans16x16DCT,
|
||||
::testing::Values(make_tuple(&idct16x16_10_add_10_c,
|
||||
&idct16x16_10_add_10_sse2, 3167, VPX_BITS_10),
|
||||
make_tuple(&idct16x16_10, &idct16x16_256_add_10_sse2,
|
||||
3167, VPX_BITS_10),
|
||||
make_tuple(&idct16x16_10_add_12_c,
|
||||
&idct16x16_10_add_12_sse2, 3167, VPX_BITS_12),
|
||||
make_tuple(&idct16x16_12, &idct16x16_256_add_12_sse2,
|
||||
3167, VPX_BITS_12)));
|
||||
#endif // HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(MSA, Trans16x16DCT,
|
||||
::testing::Values(make_tuple(&vpx_fdct16x16_msa,
|
||||
&vpx_idct16x16_256_add_msa,
|
||||
0, VPX_BITS_8)));
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, Trans16x16HT,
|
||||
::testing::Values(
|
||||
make_tuple(&vp9_fht16x16_msa, &vp9_iht16x16_256_add_msa, 0, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_msa, &vp9_iht16x16_256_add_msa, 1, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_msa, &vp9_iht16x16_256_add_msa, 2, VPX_BITS_8),
|
||||
make_tuple(&vp9_fht16x16_msa, &vp9_iht16x16_256_add_msa, 3,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(VSX, Trans16x16DCT,
|
||||
::testing::Values(make_tuple(&vpx_fdct16x16_c,
|
||||
&vpx_idct16x16_256_add_vsx,
|
||||
0, VPX_BITS_8)));
|
||||
#endif // HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
} // namespace
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vp9_rtcd.h"
|
||||
#include "./vpx_config.h"
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "test/acm_random.h"
|
||||
#include "test/bench.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vp9/common/vp9_entropy.h"
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_ports/mem.h"
|
||||
#include "vpx_ports/msvc.h" // for round()
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
|
||||
namespace {
|
||||
|
||||
const int kNumCoeffs = 1024;
|
||||
const double kPi = 3.141592653589793238462643383279502884;
|
||||
void reference_32x32_dct_1d(const double in[32], double out[32]) {
|
||||
const double kInvSqrt2 = 0.707106781186547524400844362104;
|
||||
for (int k = 0; k < 32; k++) {
|
||||
out[k] = 0.0;
|
||||
for (int n = 0; n < 32; n++) {
|
||||
out[k] += in[n] * cos(kPi * (2 * n + 1) * k / 64.0);
|
||||
}
|
||||
if (k == 0) out[k] = out[k] * kInvSqrt2;
|
||||
}
|
||||
}
|
||||
|
||||
void reference_32x32_dct_2d(const int16_t input[kNumCoeffs],
|
||||
double output[kNumCoeffs]) {
|
||||
// First transform columns
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
double temp_in[32], temp_out[32];
|
||||
for (int j = 0; j < 32; ++j) temp_in[j] = input[j * 32 + i];
|
||||
reference_32x32_dct_1d(temp_in, temp_out);
|
||||
for (int j = 0; j < 32; ++j) output[j * 32 + i] = temp_out[j];
|
||||
}
|
||||
// Then transform rows
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
double temp_in[32], temp_out[32];
|
||||
for (int j = 0; j < 32; ++j) temp_in[j] = output[j + i * 32];
|
||||
reference_32x32_dct_1d(temp_in, temp_out);
|
||||
// Scale by some magic number
|
||||
for (int j = 0; j < 32; ++j) output[j + i * 32] = temp_out[j] / 4;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*FwdTxfmFunc)(const int16_t *in, tran_low_t *out, int stride);
|
||||
typedef void (*InvTxfmFunc)(const tran_low_t *in, uint8_t *out, int stride);
|
||||
|
||||
typedef std::tuple<FwdTxfmFunc, InvTxfmFunc, int, vpx_bit_depth_t>
|
||||
Trans32x32Param;
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
void idct32x32_10(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct32x32_1024_add_c(in, CAST_TO_SHORTPTR(out), stride, 10);
|
||||
}
|
||||
|
||||
void idct32x32_12(const tran_low_t *in, uint8_t *out, int stride) {
|
||||
vpx_highbd_idct32x32_1024_add_c(in, CAST_TO_SHORTPTR(out), stride, 12);
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
class Trans32x32Test : public AbstractBench,
|
||||
public ::testing::TestWithParam<Trans32x32Param> {
|
||||
public:
|
||||
virtual ~Trans32x32Test() {}
|
||||
virtual void SetUp() {
|
||||
fwd_txfm_ = GET_PARAM(0);
|
||||
inv_txfm_ = GET_PARAM(1);
|
||||
version_ = GET_PARAM(2); // 0: high precision forward transform
|
||||
// 1: low precision version for rd loop
|
||||
bit_depth_ = GET_PARAM(3);
|
||||
mask_ = (1 << bit_depth_) - 1;
|
||||
}
|
||||
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
int version_;
|
||||
vpx_bit_depth_t bit_depth_;
|
||||
int mask_;
|
||||
FwdTxfmFunc fwd_txfm_;
|
||||
InvTxfmFunc inv_txfm_;
|
||||
|
||||
int16_t *bench_in_;
|
||||
tran_low_t *bench_out_;
|
||||
virtual void Run();
|
||||
};
|
||||
|
||||
void Trans32x32Test::Run() { fwd_txfm_(bench_in_, bench_out_, 32); }
|
||||
|
||||
TEST_P(Trans32x32Test, AccuracyCheck) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
uint32_t max_error = 0;
|
||||
int64_t total_error = 0;
|
||||
const int count_test_block = 10000;
|
||||
DECLARE_ALIGNED(16, int16_t, test_input_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, test_temp_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, src[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, src16[kNumCoeffs]);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
src[j] = rnd.Rand8();
|
||||
dst[j] = rnd.Rand8();
|
||||
test_input_block[j] = src[j] - dst[j];
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
src16[j] = rnd.Rand16() & mask_;
|
||||
dst16[j] = rnd.Rand16() & mask_;
|
||||
test_input_block[j] = src16[j] - dst16[j];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(fwd_txfm_(test_input_block, test_temp_block, 32));
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
ASM_REGISTER_STATE_CHECK(inv_txfm_(test_temp_block, dst, 32));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
inv_txfm_(test_temp_block, CAST_TO_BYTEPTR(dst16), 32));
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
const int32_t diff =
|
||||
bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
|
||||
#else
|
||||
const int32_t diff = dst[j] - src[j];
|
||||
#endif
|
||||
const uint32_t error = diff * diff;
|
||||
if (max_error < error) max_error = error;
|
||||
total_error += error;
|
||||
}
|
||||
}
|
||||
|
||||
if (version_ == 1) {
|
||||
max_error /= 2;
|
||||
total_error /= 45;
|
||||
}
|
||||
|
||||
EXPECT_GE(1u << 2 * (bit_depth_ - 8), max_error)
|
||||
<< "Error: 32x32 FDCT/IDCT has an individual round-trip error > 1";
|
||||
|
||||
EXPECT_GE(count_test_block << 2 * (bit_depth_ - 8), total_error)
|
||||
<< "Error: 32x32 FDCT/IDCT has average round-trip error > 1 per block";
|
||||
}
|
||||
|
||||
TEST_P(Trans32x32Test, CoeffCheck) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
|
||||
DECLARE_ALIGNED(16, int16_t, input_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_block[kNumCoeffs]);
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
input_block[j] = (rnd.Rand16() & mask_) - (rnd.Rand16() & mask_);
|
||||
}
|
||||
|
||||
const int stride = 32;
|
||||
vpx_fdct32x32_c(input_block, output_ref_block, stride);
|
||||
ASM_REGISTER_STATE_CHECK(fwd_txfm_(input_block, output_block, stride));
|
||||
|
||||
if (version_ == 0) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j)
|
||||
EXPECT_EQ(output_block[j], output_ref_block[j])
|
||||
<< "Error: 32x32 FDCT versions have mismatched coefficients";
|
||||
} else {
|
||||
for (int j = 0; j < kNumCoeffs; ++j)
|
||||
EXPECT_GE(6, abs(output_block[j] - output_ref_block[j]))
|
||||
<< "Error: 32x32 FDCT rd has mismatched coefficients";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Trans32x32Test, MemCheck) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 2000;
|
||||
|
||||
DECLARE_ALIGNED(16, int16_t, input_extreme_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_block[kNumCoeffs]);
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-mask_, mask_].
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
input_extreme_block[j] = rnd.Rand8() & 1 ? mask_ : -mask_;
|
||||
}
|
||||
if (i == 0) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = mask_;
|
||||
} else if (i == 1) {
|
||||
for (int j = 0; j < kNumCoeffs; ++j) input_extreme_block[j] = -mask_;
|
||||
}
|
||||
|
||||
const int stride = 32;
|
||||
vpx_fdct32x32_c(input_extreme_block, output_ref_block, stride);
|
||||
ASM_REGISTER_STATE_CHECK(
|
||||
fwd_txfm_(input_extreme_block, output_block, stride));
|
||||
|
||||
// The minimum quant value is 4.
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (version_ == 0) {
|
||||
EXPECT_EQ(output_block[j], output_ref_block[j])
|
||||
<< "Error: 32x32 FDCT versions have mismatched coefficients";
|
||||
} else {
|
||||
EXPECT_GE(6, abs(output_block[j] - output_ref_block[j]))
|
||||
<< "Error: 32x32 FDCT rd has mismatched coefficients";
|
||||
}
|
||||
EXPECT_GE(4 * DCT_MAX_VALUE << (bit_depth_ - 8), abs(output_ref_block[j]))
|
||||
<< "Error: 32x32 FDCT C has coefficient larger than 4*DCT_MAX_VALUE";
|
||||
EXPECT_GE(4 * DCT_MAX_VALUE << (bit_depth_ - 8), abs(output_block[j]))
|
||||
<< "Error: 32x32 FDCT has coefficient larger than "
|
||||
<< "4*DCT_MAX_VALUE";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Trans32x32Test, DISABLED_Speed) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
|
||||
DECLARE_ALIGNED(16, int16_t, input_extreme_block[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, output_block[kNumCoeffs]);
|
||||
|
||||
bench_in_ = input_extreme_block;
|
||||
bench_out_ = output_block;
|
||||
|
||||
RunNTimes(INT16_MAX);
|
||||
PrintMedian("32x32");
|
||||
}
|
||||
|
||||
TEST_P(Trans32x32Test, InverseAccuracy) {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
DECLARE_ALIGNED(16, int16_t, in[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint8_t, src[kNumCoeffs]);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
|
||||
DECLARE_ALIGNED(16, uint16_t, src16[kNumCoeffs]);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
double out_r[kNumCoeffs];
|
||||
|
||||
// Initialize a test block with input range [-255, 255]
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
src[j] = rnd.Rand8();
|
||||
dst[j] = rnd.Rand8();
|
||||
in[j] = src[j] - dst[j];
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
src16[j] = rnd.Rand16() & mask_;
|
||||
dst16[j] = rnd.Rand16() & mask_;
|
||||
in[j] = src16[j] - dst16[j];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
reference_32x32_dct_2d(in, out_r);
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
coeff[j] = static_cast<tran_low_t>(round(out_r[j]));
|
||||
}
|
||||
if (bit_depth_ == VPX_BITS_8) {
|
||||
ASM_REGISTER_STATE_CHECK(inv_txfm_(coeff, dst, 32));
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
} else {
|
||||
ASM_REGISTER_STATE_CHECK(inv_txfm_(coeff, CAST_TO_BYTEPTR(dst16), 32));
|
||||
#endif
|
||||
}
|
||||
for (int j = 0; j < kNumCoeffs; ++j) {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
const int diff =
|
||||
bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
|
||||
#else
|
||||
const int diff = dst[j] - src[j];
|
||||
#endif
|
||||
const int error = diff * diff;
|
||||
EXPECT_GE(1, error) << "Error: 32x32 IDCT has error " << error
|
||||
<< " at index " << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, Trans32x32Test,
|
||||
::testing::Values(
|
||||
make_tuple(&vpx_highbd_fdct32x32_c, &idct32x32_10, 0, VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct32x32_rd_c, &idct32x32_10, 1, VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct32x32_c, &idct32x32_12, 0, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct32x32_rd_c, &idct32x32_12, 1, VPX_BITS_12),
|
||||
make_tuple(&vpx_fdct32x32_c, &vpx_idct32x32_1024_add_c, 0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_c, &vpx_idct32x32_1024_add_c, 1,
|
||||
VPX_BITS_8)));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_c, &vpx_idct32x32_1024_add_c, 0,
|
||||
VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_c, &vpx_idct32x32_1024_add_c,
|
||||
1, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_NEON && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_neon,
|
||||
&vpx_idct32x32_1024_add_neon, 0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_neon,
|
||||
&vpx_idct32x32_1024_add_neon, 1, VPX_BITS_8)));
|
||||
#endif // HAVE_NEON && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_sse2,
|
||||
&vpx_idct32x32_1024_add_sse2, 0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_sse2,
|
||||
&vpx_idct32x32_1024_add_sse2, 1, VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, Trans32x32Test,
|
||||
::testing::Values(
|
||||
make_tuple(&vpx_highbd_fdct32x32_sse2, &idct32x32_10, 0, VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct32x32_rd_sse2, &idct32x32_10, 1,
|
||||
VPX_BITS_10),
|
||||
make_tuple(&vpx_highbd_fdct32x32_sse2, &idct32x32_12, 0, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct32x32_rd_sse2, &idct32x32_12, 1,
|
||||
VPX_BITS_12),
|
||||
make_tuple(&vpx_fdct32x32_sse2, &vpx_idct32x32_1024_add_c, 0,
|
||||
VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_sse2, &vpx_idct32x32_1024_add_c, 1,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_AVX2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AVX2, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_avx2,
|
||||
&vpx_idct32x32_1024_add_sse2, 0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_avx2,
|
||||
&vpx_idct32x32_1024_add_sse2, 1, VPX_BITS_8)));
|
||||
#endif // HAVE_AVX2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_msa,
|
||||
&vpx_idct32x32_1024_add_msa, 0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_msa,
|
||||
&vpx_idct32x32_1024_add_msa, 1, VPX_BITS_8)));
|
||||
#endif // HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
VSX, Trans32x32Test,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_c, &vpx_idct32x32_1024_add_vsx,
|
||||
0, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct32x32_rd_vsx,
|
||||
&vpx_idct32x32_1024_add_vsx, 1, VPX_BITS_8)));
|
||||
#endif // HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
|
||||
} // namespace
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "test/acm_random.h"
|
||||
#include "test/buffer.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_dsp/vpx_dsp_common.h"
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
using libvpx_test::Buffer;
|
||||
using std::make_tuple;
|
||||
using std::tuple;
|
||||
|
||||
namespace {
|
||||
typedef void (*PartialFdctFunc)(const int16_t *in, tran_low_t *out, int stride);
|
||||
|
||||
typedef tuple<PartialFdctFunc, int /* size */, vpx_bit_depth_t>
|
||||
PartialFdctParam;
|
||||
|
||||
tran_low_t partial_fdct_ref(const Buffer<int16_t> &in, int size) {
|
||||
int64_t sum = 0;
|
||||
if (in.TopLeftPixel() != NULL) {
|
||||
for (int y = 0; y < size; ++y) {
|
||||
for (int x = 0; x < size; ++x) {
|
||||
sum += in.TopLeftPixel()[y * in.stride() + x];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case 4: sum *= 2; break;
|
||||
case 8: /*sum = sum;*/ break;
|
||||
case 16: sum >>= 1; break;
|
||||
case 32: sum >>= 3; break;
|
||||
}
|
||||
|
||||
return static_cast<tran_low_t>(sum);
|
||||
}
|
||||
|
||||
class PartialFdctTest : public ::testing::TestWithParam<PartialFdctParam> {
|
||||
public:
|
||||
PartialFdctTest() {
|
||||
fwd_txfm_ = GET_PARAM(0);
|
||||
size_ = GET_PARAM(1);
|
||||
bit_depth_ = GET_PARAM(2);
|
||||
}
|
||||
|
||||
virtual void TearDown() { libvpx_test::ClearSystemState(); }
|
||||
|
||||
protected:
|
||||
void RunTest() {
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int16_t maxvalue =
|
||||
clip_pixel_highbd(std::numeric_limits<int16_t>::max(), bit_depth_);
|
||||
const int16_t minvalue = -maxvalue;
|
||||
Buffer<int16_t> input_block =
|
||||
Buffer<int16_t>(size_, size_, 8, size_ == 4 ? 0 : 16);
|
||||
ASSERT_TRUE(input_block.Init());
|
||||
Buffer<tran_low_t> output_block = Buffer<tran_low_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(output_block.Init());
|
||||
|
||||
if (output_block.TopLeftPixel() != NULL) {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
if (i == 0) {
|
||||
input_block.Set(maxvalue);
|
||||
} else if (i == 1) {
|
||||
input_block.Set(minvalue);
|
||||
} else {
|
||||
input_block.Set(&rnd, minvalue, maxvalue);
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(fwd_txfm_(input_block.TopLeftPixel(),
|
||||
output_block.TopLeftPixel(),
|
||||
input_block.stride()));
|
||||
|
||||
EXPECT_EQ(partial_fdct_ref(input_block, size_),
|
||||
output_block.TopLeftPixel()[0]);
|
||||
}
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
PartialFdctFunc fwd_txfm_;
|
||||
vpx_bit_depth_t bit_depth_;
|
||||
int size_;
|
||||
};
|
||||
|
||||
TEST_P(PartialFdctTest, PartialFdctTest) { RunTest(); }
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_highbd_fdct32x32_1_c, 32, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct32x32_1_c, 32, VPX_BITS_10),
|
||||
make_tuple(&vpx_fdct32x32_1_c, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_highbd_fdct16x16_1_c, 16, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct16x16_1_c, 16, VPX_BITS_10),
|
||||
make_tuple(&vpx_fdct16x16_1_c, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_highbd_fdct8x8_1_c, 8, VPX_BITS_12),
|
||||
make_tuple(&vpx_highbd_fdct8x8_1_c, 8, VPX_BITS_10),
|
||||
make_tuple(&vpx_fdct8x8_1_c, 8, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct4x4_1_c, 4, VPX_BITS_8)));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_1_c, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct16x16_1_c, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct8x8_1_c, 8, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct4x4_1_c, 4, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_SSE2
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_1_sse2, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct16x16_1_sse2, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct8x8_1_sse2, 8, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct4x4_1_sse2, 4, VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
#if HAVE_NEON
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_1_neon, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct16x16_1_neon, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct8x8_1_neon, 8, VPX_BITS_12),
|
||||
make_tuple(&vpx_fdct8x8_1_neon, 8, VPX_BITS_10),
|
||||
make_tuple(&vpx_fdct8x8_1_neon, 8, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct4x4_1_neon, 4, VPX_BITS_8)));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_1_neon, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct16x16_1_neon, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct8x8_1_neon, 8, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct4x4_1_neon, 4, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
#endif // HAVE_NEON
|
||||
|
||||
#if HAVE_MSA
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(MSA, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct8x8_1_msa, 8,
|
||||
VPX_BITS_8)));
|
||||
#else // !CONFIG_VP9_HIGHBITDEPTH
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
MSA, PartialFdctTest,
|
||||
::testing::Values(make_tuple(&vpx_fdct32x32_1_msa, 32, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct16x16_1_msa, 16, VPX_BITS_8),
|
||||
make_tuple(&vpx_fdct8x8_1_msa, 8, VPX_BITS_8)));
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
#endif // HAVE_MSA
|
||||
} // namespace
|
||||
@@ -0,0 +1,756 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vp9_rtcd.h"
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "test/acm_random.h"
|
||||
#include "test/buffer.h"
|
||||
#include "test/clear_system_state.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/util.h"
|
||||
#include "vp9/common/vp9_entropy.h"
|
||||
#include "vpx/vpx_codec.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_ports/mem.h"
|
||||
|
||||
using libvpx_test::ACMRandom;
|
||||
using libvpx_test::Buffer;
|
||||
using std::make_tuple;
|
||||
using std::tuple;
|
||||
|
||||
namespace {
|
||||
typedef void (*FdctFunc)(const int16_t *in, tran_low_t *out, int stride);
|
||||
typedef void (*IdctFunc)(const tran_low_t *in, uint8_t *out, int stride);
|
||||
typedef void (*FhtFunc)(const int16_t *in, tran_low_t *out, int stride,
|
||||
int tx_type);
|
||||
typedef void (*FhtFuncRef)(const Buffer<int16_t> &in, Buffer<tran_low_t> *out,
|
||||
int size, int tx_type);
|
||||
typedef void (*IhtFunc)(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int tx_type);
|
||||
typedef void (*IhtWithBdFunc)(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int tx_type, int bd);
|
||||
|
||||
template <FdctFunc fn>
|
||||
void fdct_wrapper(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
|
||||
(void)tx_type;
|
||||
fn(in, out, stride);
|
||||
}
|
||||
|
||||
template <IdctFunc fn>
|
||||
void idct_wrapper(const tran_low_t *in, uint8_t *out, int stride, int tx_type,
|
||||
int bd) {
|
||||
(void)tx_type;
|
||||
(void)bd;
|
||||
fn(in, out, stride);
|
||||
}
|
||||
|
||||
template <IhtFunc fn>
|
||||
void iht_wrapper(const tran_low_t *in, uint8_t *out, int stride, int tx_type,
|
||||
int bd) {
|
||||
(void)bd;
|
||||
fn(in, out, stride, tx_type);
|
||||
}
|
||||
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
typedef void (*HighbdIdctFunc)(const tran_low_t *in, uint16_t *out, int stride,
|
||||
int bd);
|
||||
|
||||
typedef void (*HighbdIhtFunc)(const tran_low_t *in, uint16_t *out, int stride,
|
||||
int tx_type, int bd);
|
||||
|
||||
template <HighbdIdctFunc fn>
|
||||
void highbd_idct_wrapper(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int tx_type, int bd) {
|
||||
(void)tx_type;
|
||||
fn(in, CAST_TO_SHORTPTR(out), stride, bd);
|
||||
}
|
||||
|
||||
template <HighbdIhtFunc fn>
|
||||
void highbd_iht_wrapper(const tran_low_t *in, uint8_t *out, int stride,
|
||||
int tx_type, int bd) {
|
||||
fn(in, CAST_TO_SHORTPTR(out), stride, tx_type, bd);
|
||||
}
|
||||
#endif // CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
struct FuncInfo {
|
||||
FhtFunc ft_func;
|
||||
IhtWithBdFunc it_func;
|
||||
int size;
|
||||
int pixel_size;
|
||||
};
|
||||
|
||||
/* forward transform, inverse transform, size, transform type, bit depth */
|
||||
typedef tuple<int, const FuncInfo *, int, vpx_bit_depth_t> DctParam;
|
||||
|
||||
void fdct_ref(const Buffer<int16_t> &in, Buffer<tran_low_t> *out, int size,
|
||||
int /*tx_type*/) {
|
||||
const int16_t *i = in.TopLeftPixel();
|
||||
const int i_stride = in.stride();
|
||||
tran_low_t *o = out->TopLeftPixel();
|
||||
if (size == 4) {
|
||||
vpx_fdct4x4_c(i, o, i_stride);
|
||||
} else if (size == 8) {
|
||||
vpx_fdct8x8_c(i, o, i_stride);
|
||||
} else if (size == 16) {
|
||||
vpx_fdct16x16_c(i, o, i_stride);
|
||||
} else if (size == 32) {
|
||||
vpx_fdct32x32_c(i, o, i_stride);
|
||||
}
|
||||
}
|
||||
|
||||
void fht_ref(const Buffer<int16_t> &in, Buffer<tran_low_t> *out, int size,
|
||||
int tx_type) {
|
||||
const int16_t *i = in.TopLeftPixel();
|
||||
const int i_stride = in.stride();
|
||||
tran_low_t *o = out->TopLeftPixel();
|
||||
if (size == 4) {
|
||||
vp9_fht4x4_c(i, o, i_stride, tx_type);
|
||||
} else if (size == 8) {
|
||||
vp9_fht8x8_c(i, o, i_stride, tx_type);
|
||||
} else if (size == 16) {
|
||||
vp9_fht16x16_c(i, o, i_stride, tx_type);
|
||||
}
|
||||
}
|
||||
|
||||
void fwht_ref(const Buffer<int16_t> &in, Buffer<tran_low_t> *out, int size,
|
||||
int /*tx_type*/) {
|
||||
ASSERT_EQ(size, 4);
|
||||
vp9_fwht4x4_c(in.TopLeftPixel(), out->TopLeftPixel(), in.stride());
|
||||
}
|
||||
|
||||
class TransTestBase : public ::testing::TestWithParam<DctParam> {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
rnd_.Reset(ACMRandom::DeterministicSeed());
|
||||
const int idx = GET_PARAM(0);
|
||||
const FuncInfo *func_info = &(GET_PARAM(1)[idx]);
|
||||
tx_type_ = GET_PARAM(2);
|
||||
bit_depth_ = GET_PARAM(3);
|
||||
fwd_txfm_ = func_info->ft_func;
|
||||
inv_txfm_ = func_info->it_func;
|
||||
size_ = func_info->size;
|
||||
pixel_size_ = func_info->pixel_size;
|
||||
max_pixel_value_ = (1 << bit_depth_) - 1;
|
||||
|
||||
// Randomize stride_ to a value less than or equal to 1024
|
||||
stride_ = rnd_(1024) + 1;
|
||||
if (stride_ < size_) {
|
||||
stride_ = size_;
|
||||
}
|
||||
// Align stride_ to 16 if it's bigger than 16.
|
||||
if (stride_ > 16) {
|
||||
stride_ &= ~15;
|
||||
}
|
||||
|
||||
block_size_ = size_ * stride_;
|
||||
|
||||
src_ = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(16, pixel_size_ * block_size_));
|
||||
ASSERT_TRUE(src_ != NULL);
|
||||
dst_ = reinterpret_cast<uint8_t *>(
|
||||
vpx_memalign(16, pixel_size_ * block_size_));
|
||||
ASSERT_TRUE(dst_ != NULL);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
vpx_free(src_);
|
||||
src_ = NULL;
|
||||
vpx_free(dst_);
|
||||
dst_ = NULL;
|
||||
libvpx_test::ClearSystemState();
|
||||
}
|
||||
|
||||
void InitMem() {
|
||||
if (pixel_size_ == 1 && bit_depth_ > VPX_BITS_8) return;
|
||||
if (pixel_size_ == 1) {
|
||||
for (int j = 0; j < block_size_; ++j) {
|
||||
src_[j] = rnd_.Rand16() & max_pixel_value_;
|
||||
}
|
||||
for (int j = 0; j < block_size_; ++j) {
|
||||
dst_[j] = rnd_.Rand16() & max_pixel_value_;
|
||||
}
|
||||
} else {
|
||||
ASSERT_EQ(pixel_size_, 2);
|
||||
uint16_t *const src = reinterpret_cast<uint16_t *>(src_);
|
||||
uint16_t *const dst = reinterpret_cast<uint16_t *>(dst_);
|
||||
for (int j = 0; j < block_size_; ++j) {
|
||||
src[j] = rnd_.Rand16() & max_pixel_value_;
|
||||
}
|
||||
for (int j = 0; j < block_size_; ++j) {
|
||||
dst[j] = rnd_.Rand16() & max_pixel_value_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunFwdTxfm(const Buffer<int16_t> &in, Buffer<tran_low_t> *out) {
|
||||
fwd_txfm_(in.TopLeftPixel(), out->TopLeftPixel(), in.stride(), tx_type_);
|
||||
}
|
||||
|
||||
void RunInvTxfm(const Buffer<tran_low_t> &in, uint8_t *out) {
|
||||
inv_txfm_(in.TopLeftPixel(), out, stride_, tx_type_, bit_depth_);
|
||||
}
|
||||
|
||||
protected:
|
||||
void RunAccuracyCheck(int limit) {
|
||||
if (pixel_size_ == 1 && bit_depth_ > VPX_BITS_8) return;
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
Buffer<int16_t> test_input_block =
|
||||
Buffer<int16_t>(size_, size_, 8, size_ == 4 ? 0 : 16);
|
||||
ASSERT_TRUE(test_input_block.Init());
|
||||
ASSERT_TRUE(test_input_block.TopLeftPixel() != NULL);
|
||||
Buffer<tran_low_t> test_temp_block =
|
||||
Buffer<tran_low_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(test_temp_block.Init());
|
||||
uint32_t max_error = 0;
|
||||
int64_t total_error = 0;
|
||||
const int count_test_block = 10000;
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
InitMem();
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
if (pixel_size_ == 1) {
|
||||
test_input_block.TopLeftPixel()[h * test_input_block.stride() + w] =
|
||||
src_[h * stride_ + w] - dst_[h * stride_ + w];
|
||||
} else {
|
||||
ASSERT_EQ(pixel_size_, 2);
|
||||
const uint16_t *const src = reinterpret_cast<uint16_t *>(src_);
|
||||
const uint16_t *const dst = reinterpret_cast<uint16_t *>(dst_);
|
||||
test_input_block.TopLeftPixel()[h * test_input_block.stride() + w] =
|
||||
src[h * stride_ + w] - dst[h * stride_ + w];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(RunFwdTxfm(test_input_block, &test_temp_block));
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(test_temp_block, dst_));
|
||||
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
int diff;
|
||||
if (pixel_size_ == 1) {
|
||||
diff = dst_[h * stride_ + w] - src_[h * stride_ + w];
|
||||
} else {
|
||||
ASSERT_EQ(pixel_size_, 2);
|
||||
const uint16_t *const src = reinterpret_cast<uint16_t *>(src_);
|
||||
const uint16_t *const dst = reinterpret_cast<uint16_t *>(dst_);
|
||||
diff = dst[h * stride_ + w] - src[h * stride_ + w];
|
||||
}
|
||||
const uint32_t error = diff * diff;
|
||||
if (max_error < error) max_error = error;
|
||||
total_error += error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_GE(static_cast<uint32_t>(limit), max_error)
|
||||
<< "Error: " << size_ << "x" << size_
|
||||
<< " transform/inverse transform has an individual round trip error > "
|
||||
<< limit;
|
||||
|
||||
EXPECT_GE(count_test_block * limit, total_error)
|
||||
<< "Error: " << size_ << "x" << size_
|
||||
<< " transform/inverse transform has average round trip error > "
|
||||
<< limit << " per block";
|
||||
}
|
||||
|
||||
void RunCoeffCheck() {
|
||||
if (pixel_size_ == 1 && bit_depth_ > VPX_BITS_8) return;
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 5000;
|
||||
Buffer<int16_t> input_block =
|
||||
Buffer<int16_t>(size_, size_, 8, size_ == 4 ? 0 : 16);
|
||||
ASSERT_TRUE(input_block.Init());
|
||||
Buffer<tran_low_t> output_ref_block = Buffer<tran_low_t>(size_, size_, 0);
|
||||
ASSERT_TRUE(output_ref_block.Init());
|
||||
Buffer<tran_low_t> output_block = Buffer<tran_low_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(output_block.Init());
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with input range [-max_pixel_value_,
|
||||
// max_pixel_value_].
|
||||
input_block.Set(&rnd, -max_pixel_value_, max_pixel_value_);
|
||||
|
||||
fwd_txfm_ref(input_block, &output_ref_block, size_, tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(RunFwdTxfm(input_block, &output_block));
|
||||
|
||||
// The minimum quant value is 4.
|
||||
EXPECT_TRUE(output_block.CheckValues(output_ref_block));
|
||||
if (::testing::Test::HasFailure()) {
|
||||
printf("Size: %d Transform type: %d\n", size_, tx_type_);
|
||||
output_block.PrintDifference(output_ref_block);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunMemCheck() {
|
||||
if (pixel_size_ == 1 && bit_depth_ > VPX_BITS_8) return;
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 5000;
|
||||
Buffer<int16_t> input_extreme_block =
|
||||
Buffer<int16_t>(size_, size_, 8, size_ == 4 ? 0 : 16);
|
||||
ASSERT_TRUE(input_extreme_block.Init());
|
||||
Buffer<tran_low_t> output_ref_block = Buffer<tran_low_t>(size_, size_, 0);
|
||||
ASSERT_TRUE(output_ref_block.Init());
|
||||
Buffer<tran_low_t> output_block = Buffer<tran_low_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(output_block.Init());
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
// Initialize a test block with -max_pixel_value_ or max_pixel_value_.
|
||||
if (i == 0) {
|
||||
input_extreme_block.Set(max_pixel_value_);
|
||||
} else if (i == 1) {
|
||||
input_extreme_block.Set(-max_pixel_value_);
|
||||
} else {
|
||||
ASSERT_TRUE(input_extreme_block.TopLeftPixel() != NULL);
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
input_extreme_block
|
||||
.TopLeftPixel()[h * input_extreme_block.stride() + w] =
|
||||
rnd.Rand8() % 2 ? max_pixel_value_ : -max_pixel_value_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwd_txfm_ref(input_extreme_block, &output_ref_block, size_, tx_type_);
|
||||
ASM_REGISTER_STATE_CHECK(RunFwdTxfm(input_extreme_block, &output_block));
|
||||
|
||||
// The minimum quant value is 4.
|
||||
EXPECT_TRUE(output_block.CheckValues(output_ref_block));
|
||||
ASSERT_TRUE(output_block.TopLeftPixel() != NULL);
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
EXPECT_GE(
|
||||
4 * DCT_MAX_VALUE << (bit_depth_ - 8),
|
||||
abs(output_block.TopLeftPixel()[h * output_block.stride() + w]))
|
||||
<< "Error: " << size_ << "x" << size_
|
||||
<< " transform has coefficient larger than 4*DCT_MAX_VALUE"
|
||||
<< " at " << w << "," << h;
|
||||
if (::testing::Test::HasFailure()) {
|
||||
printf("Size: %d Transform type: %d\n", size_, tx_type_);
|
||||
output_block.DumpBuffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunInvAccuracyCheck(int limit) {
|
||||
if (pixel_size_ == 1 && bit_depth_ > VPX_BITS_8) return;
|
||||
ACMRandom rnd(ACMRandom::DeterministicSeed());
|
||||
const int count_test_block = 1000;
|
||||
Buffer<int16_t> in = Buffer<int16_t>(size_, size_, 4);
|
||||
ASSERT_TRUE(in.Init());
|
||||
Buffer<tran_low_t> coeff = Buffer<tran_low_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(coeff.Init());
|
||||
Buffer<uint8_t> dst = Buffer<uint8_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(dst.Init());
|
||||
Buffer<uint8_t> src = Buffer<uint8_t>(size_, size_, 0);
|
||||
ASSERT_TRUE(src.Init());
|
||||
Buffer<uint16_t> dst16 = Buffer<uint16_t>(size_, size_, 0, 16);
|
||||
ASSERT_TRUE(dst16.Init());
|
||||
Buffer<uint16_t> src16 = Buffer<uint16_t>(size_, size_, 0);
|
||||
ASSERT_TRUE(src16.Init());
|
||||
|
||||
for (int i = 0; i < count_test_block; ++i) {
|
||||
InitMem();
|
||||
ASSERT_TRUE(in.TopLeftPixel() != NULL);
|
||||
// Initialize a test block with input range [-max_pixel_value_,
|
||||
// max_pixel_value_].
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
if (pixel_size_ == 1) {
|
||||
in.TopLeftPixel()[h * in.stride() + w] =
|
||||
src_[h * stride_ + w] - dst_[h * stride_ + w];
|
||||
} else {
|
||||
ASSERT_EQ(pixel_size_, 2);
|
||||
const uint16_t *const src = reinterpret_cast<uint16_t *>(src_);
|
||||
const uint16_t *const dst = reinterpret_cast<uint16_t *>(dst_);
|
||||
in.TopLeftPixel()[h * in.stride() + w] =
|
||||
src[h * stride_ + w] - dst[h * stride_ + w];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fwd_txfm_ref(in, &coeff, size_, tx_type_);
|
||||
|
||||
ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst_));
|
||||
|
||||
for (int h = 0; h < size_; ++h) {
|
||||
for (int w = 0; w < size_; ++w) {
|
||||
int diff;
|
||||
if (pixel_size_ == 1) {
|
||||
diff = dst_[h * stride_ + w] - src_[h * stride_ + w];
|
||||
} else {
|
||||
ASSERT_EQ(pixel_size_, 2);
|
||||
const uint16_t *const src = reinterpret_cast<uint16_t *>(src_);
|
||||
const uint16_t *const dst = reinterpret_cast<uint16_t *>(dst_);
|
||||
diff = dst[h * stride_ + w] - src[h * stride_ + w];
|
||||
}
|
||||
const uint32_t error = diff * diff;
|
||||
EXPECT_GE(static_cast<uint32_t>(limit), error)
|
||||
<< "Error: " << size_ << "x" << size_
|
||||
<< " inverse transform has error " << error << " at " << w << ","
|
||||
<< h;
|
||||
if (::testing::Test::HasFailure()) {
|
||||
printf("Size: %d Transform type: %d\n", size_, tx_type_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FhtFunc fwd_txfm_;
|
||||
FhtFuncRef fwd_txfm_ref;
|
||||
IhtWithBdFunc inv_txfm_;
|
||||
ACMRandom rnd_;
|
||||
uint8_t *src_;
|
||||
uint8_t *dst_;
|
||||
vpx_bit_depth_t bit_depth_;
|
||||
int tx_type_;
|
||||
int max_pixel_value_;
|
||||
int size_;
|
||||
int stride_;
|
||||
int pixel_size_;
|
||||
int block_size_;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class TransDCT : public TransTestBase {
|
||||
public:
|
||||
TransDCT() { fwd_txfm_ref = fdct_ref; }
|
||||
};
|
||||
|
||||
TEST_P(TransDCT, AccuracyCheck) {
|
||||
int t = 1;
|
||||
if (size_ == 16 && bit_depth_ > 10 && pixel_size_ == 2) {
|
||||
t = 2;
|
||||
} else if (size_ == 32 && bit_depth_ > 10 && pixel_size_ == 2) {
|
||||
t = 7;
|
||||
}
|
||||
RunAccuracyCheck(t);
|
||||
}
|
||||
|
||||
TEST_P(TransDCT, CoeffCheck) { RunCoeffCheck(); }
|
||||
|
||||
TEST_P(TransDCT, MemCheck) { RunMemCheck(); }
|
||||
|
||||
TEST_P(TransDCT, InvAccuracyCheck) { RunInvAccuracyCheck(1); }
|
||||
|
||||
static const FuncInfo dct_c_func_info[] = {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
{ &fdct_wrapper<vpx_highbd_fdct4x4_c>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct4x4_16_add_c>, 4, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct8x8_c>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct8x8_64_add_c>, 8, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct16x16_c>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct16x16_256_add_c>, 16, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct32x32_c>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct32x32_1024_add_c>, 32, 2 },
|
||||
#endif
|
||||
{ &fdct_wrapper<vpx_fdct4x4_c>, &idct_wrapper<vpx_idct4x4_16_add_c>, 4, 1 },
|
||||
{ &fdct_wrapper<vpx_fdct8x8_c>, &idct_wrapper<vpx_idct8x8_64_add_c>, 8, 1 },
|
||||
{ &fdct_wrapper<vpx_fdct16x16_c>, &idct_wrapper<vpx_idct16x16_256_add_c>, 16,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct32x32_c>, &idct_wrapper<vpx_idct32x32_1024_add_c>, 32,
|
||||
1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, TransDCT,
|
||||
::testing::Combine(
|
||||
::testing::Range(0, static_cast<int>(sizeof(dct_c_func_info) /
|
||||
sizeof(dct_c_func_info[0]))),
|
||||
::testing::Values(dct_c_func_info), ::testing::Values(0),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10, VPX_BITS_12)));
|
||||
|
||||
#if !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_SSE2
|
||||
static const FuncInfo dct_sse2_func_info[] = {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
{ &fdct_wrapper<vpx_highbd_fdct4x4_sse2>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct4x4_16_add_sse2>, 4, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct8x8_sse2>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct8x8_64_add_sse2>, 8, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct16x16_sse2>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct16x16_256_add_sse2>, 16, 2 },
|
||||
{ &fdct_wrapper<vpx_highbd_fdct32x32_sse2>,
|
||||
&highbd_idct_wrapper<vpx_highbd_idct32x32_1024_add_sse2>, 32, 2 },
|
||||
#endif
|
||||
{ &fdct_wrapper<vpx_fdct4x4_sse2>, &idct_wrapper<vpx_idct4x4_16_add_sse2>, 4,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct8x8_sse2>, &idct_wrapper<vpx_idct8x8_64_add_sse2>, 8,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct16x16_sse2>,
|
||||
&idct_wrapper<vpx_idct16x16_256_add_sse2>, 16, 1 },
|
||||
{ &fdct_wrapper<vpx_fdct32x32_sse2>,
|
||||
&idct_wrapper<vpx_idct32x32_1024_add_sse2>, 32, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE2, TransDCT,
|
||||
::testing::Combine(
|
||||
::testing::Range(0, static_cast<int>(sizeof(dct_sse2_func_info) /
|
||||
sizeof(dct_sse2_func_info[0]))),
|
||||
::testing::Values(dct_sse2_func_info), ::testing::Values(0),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10, VPX_BITS_12)));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
#if HAVE_SSSE3 && !CONFIG_VP9_HIGHBITDEPTH && VPX_ARCH_X86_64
|
||||
// vpx_fdct8x8_ssse3 is only available in 64 bit builds.
|
||||
static const FuncInfo dct_ssse3_func_info = {
|
||||
&fdct_wrapper<vpx_fdct8x8_ssse3>, &idct_wrapper<vpx_idct8x8_64_add_sse2>, 8, 1
|
||||
};
|
||||
|
||||
// TODO(johannkoenig): high bit depth fdct8x8.
|
||||
INSTANTIATE_TEST_CASE_P(SSSE3, TransDCT,
|
||||
::testing::Values(make_tuple(0, &dct_ssse3_func_info, 0,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_SSSE3 && !CONFIG_VP9_HIGHBITDEPTH && VPX_ARCH_X86_64
|
||||
|
||||
#if HAVE_AVX2 && !CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo dct_avx2_func_info = {
|
||||
&fdct_wrapper<vpx_fdct32x32_avx2>, &idct_wrapper<vpx_idct32x32_1024_add_sse2>,
|
||||
32, 1
|
||||
};
|
||||
|
||||
// TODO(johannkoenig): high bit depth fdct32x32.
|
||||
INSTANTIATE_TEST_CASE_P(AVX2, TransDCT,
|
||||
::testing::Values(make_tuple(0, &dct_avx2_func_info, 0,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_AVX2 && !CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_NEON
|
||||
static const FuncInfo dct_neon_func_info[4] = {
|
||||
{ &fdct_wrapper<vpx_fdct4x4_neon>, &idct_wrapper<vpx_idct4x4_16_add_neon>, 4,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct8x8_neon>, &idct_wrapper<vpx_idct8x8_64_add_neon>, 8,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct16x16_neon>,
|
||||
&idct_wrapper<vpx_idct16x16_256_add_neon>, 16, 1 },
|
||||
{ &fdct_wrapper<vpx_fdct32x32_neon>,
|
||||
&idct_wrapper<vpx_idct32x32_1024_add_neon>, 32, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, TransDCT,
|
||||
::testing::Combine(::testing::Range(0, 4),
|
||||
::testing::Values(dct_neon_func_info),
|
||||
::testing::Values(0), ::testing::Values(VPX_BITS_8)));
|
||||
#endif // HAVE_NEON
|
||||
|
||||
#if HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo dct_msa_func_info[4] = {
|
||||
{ &fdct_wrapper<vpx_fdct4x4_msa>, &idct_wrapper<vpx_idct4x4_16_add_msa>, 4,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct8x8_msa>, &idct_wrapper<vpx_idct8x8_64_add_msa>, 8,
|
||||
1 },
|
||||
{ &fdct_wrapper<vpx_fdct16x16_msa>, &idct_wrapper<vpx_idct16x16_256_add_msa>,
|
||||
16, 1 },
|
||||
{ &fdct_wrapper<vpx_fdct32x32_msa>, &idct_wrapper<vpx_idct32x32_1024_add_msa>,
|
||||
32, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MSA, TransDCT,
|
||||
::testing::Combine(::testing::Range(0, 4),
|
||||
::testing::Values(dct_msa_func_info),
|
||||
::testing::Values(0),
|
||||
::testing::Values(VPX_BITS_8)));
|
||||
#endif // HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo dct_vsx_func_info = {
|
||||
&fdct_wrapper<vpx_fdct4x4_c>, &idct_wrapper<vpx_idct4x4_16_add_vsx>, 4, 1
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VSX, TransDCT,
|
||||
::testing::Values(make_tuple(0, &dct_vsx_func_info, 0,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_VSX && !CONFIG_VP9_HIGHBITDEPTH &&
|
||||
|
||||
#endif // !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class TransHT : public TransTestBase {
|
||||
public:
|
||||
TransHT() { fwd_txfm_ref = fht_ref; }
|
||||
};
|
||||
|
||||
TEST_P(TransHT, AccuracyCheck) {
|
||||
RunAccuracyCheck(size_ == 16 && bit_depth_ > 10 && pixel_size_ == 2 ? 2 : 1);
|
||||
}
|
||||
|
||||
TEST_P(TransHT, CoeffCheck) { RunCoeffCheck(); }
|
||||
|
||||
TEST_P(TransHT, MemCheck) { RunMemCheck(); }
|
||||
|
||||
TEST_P(TransHT, InvAccuracyCheck) { RunInvAccuracyCheck(1); }
|
||||
|
||||
static const FuncInfo ht_c_func_info[] = {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
{ &vp9_highbd_fht4x4_c, &highbd_iht_wrapper<vp9_highbd_iht4x4_16_add_c>, 4,
|
||||
2 },
|
||||
{ &vp9_highbd_fht8x8_c, &highbd_iht_wrapper<vp9_highbd_iht8x8_64_add_c>, 8,
|
||||
2 },
|
||||
{ &vp9_highbd_fht16x16_c, &highbd_iht_wrapper<vp9_highbd_iht16x16_256_add_c>,
|
||||
16, 2 },
|
||||
#endif
|
||||
{ &vp9_fht4x4_c, &iht_wrapper<vp9_iht4x4_16_add_c>, 4, 1 },
|
||||
{ &vp9_fht8x8_c, &iht_wrapper<vp9_iht8x8_64_add_c>, 8, 1 },
|
||||
{ &vp9_fht16x16_c, &iht_wrapper<vp9_iht16x16_256_add_c>, 16, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, TransHT,
|
||||
::testing::Combine(
|
||||
::testing::Range(0, static_cast<int>(sizeof(ht_c_func_info) /
|
||||
sizeof(ht_c_func_info[0]))),
|
||||
::testing::Values(ht_c_func_info), ::testing::Range(0, 4),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10, VPX_BITS_12)));
|
||||
|
||||
#if !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_NEON
|
||||
|
||||
static const FuncInfo ht_neon_func_info[] = {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
{ &vp9_highbd_fht4x4_c, &highbd_iht_wrapper<vp9_highbd_iht4x4_16_add_neon>, 4,
|
||||
2 },
|
||||
{ &vp9_highbd_fht8x8_c, &highbd_iht_wrapper<vp9_highbd_iht8x8_64_add_neon>, 8,
|
||||
2 },
|
||||
{ &vp9_highbd_fht16x16_c,
|
||||
&highbd_iht_wrapper<vp9_highbd_iht16x16_256_add_neon>, 16, 2 },
|
||||
#endif
|
||||
{ &vp9_fht4x4_c, &iht_wrapper<vp9_iht4x4_16_add_neon>, 4, 1 },
|
||||
{ &vp9_fht8x8_c, &iht_wrapper<vp9_iht8x8_64_add_neon>, 8, 1 },
|
||||
{ &vp9_fht16x16_c, &iht_wrapper<vp9_iht16x16_256_add_neon>, 16, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
NEON, TransHT,
|
||||
::testing::Combine(
|
||||
::testing::Range(0, static_cast<int>(sizeof(ht_neon_func_info) /
|
||||
sizeof(ht_neon_func_info[0]))),
|
||||
::testing::Values(ht_neon_func_info), ::testing::Range(0, 4),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10, VPX_BITS_12)));
|
||||
#endif // HAVE_NEON
|
||||
|
||||
#if HAVE_SSE2
|
||||
|
||||
static const FuncInfo ht_sse2_func_info[3] = {
|
||||
{ &vp9_fht4x4_sse2, &iht_wrapper<vp9_iht4x4_16_add_sse2>, 4, 1 },
|
||||
{ &vp9_fht8x8_sse2, &iht_wrapper<vp9_iht8x8_64_add_sse2>, 8, 1 },
|
||||
{ &vp9_fht16x16_sse2, &iht_wrapper<vp9_iht16x16_256_add_sse2>, 16, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(SSE2, TransHT,
|
||||
::testing::Combine(::testing::Range(0, 3),
|
||||
::testing::Values(ht_sse2_func_info),
|
||||
::testing::Range(0, 4),
|
||||
::testing::Values(VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2
|
||||
|
||||
#if HAVE_SSE4_1 && CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo ht_sse4_1_func_info[3] = {
|
||||
{ &vp9_highbd_fht4x4_c, &highbd_iht_wrapper<vp9_highbd_iht4x4_16_add_sse4_1>,
|
||||
4, 2 },
|
||||
{ vp9_highbd_fht8x8_c, &highbd_iht_wrapper<vp9_highbd_iht8x8_64_add_sse4_1>,
|
||||
8, 2 },
|
||||
{ &vp9_highbd_fht16x16_c,
|
||||
&highbd_iht_wrapper<vp9_highbd_iht16x16_256_add_sse4_1>, 16, 2 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SSE4_1, TransHT,
|
||||
::testing::Combine(::testing::Range(0, 3),
|
||||
::testing::Values(ht_sse4_1_func_info),
|
||||
::testing::Range(0, 4),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10,
|
||||
VPX_BITS_12)));
|
||||
#endif // HAVE_SSE4_1 && CONFIG_VP9_HIGHBITDEPTH
|
||||
|
||||
#if HAVE_VSX && !CONFIG_EMULATE_HARDWARE && !CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo ht_vsx_func_info[3] = {
|
||||
{ &vp9_fht4x4_c, &iht_wrapper<vp9_iht4x4_16_add_vsx>, 4, 1 },
|
||||
{ &vp9_fht8x8_c, &iht_wrapper<vp9_iht8x8_64_add_vsx>, 8, 1 },
|
||||
{ &vp9_fht16x16_c, &iht_wrapper<vp9_iht16x16_256_add_vsx>, 16, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VSX, TransHT,
|
||||
::testing::Combine(::testing::Range(0, 3),
|
||||
::testing::Values(ht_vsx_func_info),
|
||||
::testing::Range(0, 4),
|
||||
::testing::Values(VPX_BITS_8)));
|
||||
#endif // HAVE_VSX
|
||||
#endif // !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class TransWHT : public TransTestBase {
|
||||
public:
|
||||
TransWHT() { fwd_txfm_ref = fwht_ref; }
|
||||
};
|
||||
|
||||
TEST_P(TransWHT, AccuracyCheck) { RunAccuracyCheck(0); }
|
||||
|
||||
TEST_P(TransWHT, CoeffCheck) { RunCoeffCheck(); }
|
||||
|
||||
TEST_P(TransWHT, MemCheck) { RunMemCheck(); }
|
||||
|
||||
TEST_P(TransWHT, InvAccuracyCheck) { RunInvAccuracyCheck(0); }
|
||||
|
||||
static const FuncInfo wht_c_func_info[] = {
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
{ &fdct_wrapper<vp9_highbd_fwht4x4_c>,
|
||||
&highbd_idct_wrapper<vpx_highbd_iwht4x4_16_add_c>, 4, 2 },
|
||||
#endif
|
||||
{ &fdct_wrapper<vp9_fwht4x4_c>, &idct_wrapper<vpx_iwht4x4_16_add_c>, 4, 1 }
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
C, TransWHT,
|
||||
::testing::Combine(
|
||||
::testing::Range(0, static_cast<int>(sizeof(wht_c_func_info) /
|
||||
sizeof(wht_c_func_info[0]))),
|
||||
::testing::Values(wht_c_func_info), ::testing::Values(0),
|
||||
::testing::Values(VPX_BITS_8, VPX_BITS_10, VPX_BITS_12)));
|
||||
|
||||
#if HAVE_SSE2 && !CONFIG_EMULATE_HARDWARE
|
||||
static const FuncInfo wht_sse2_func_info = {
|
||||
&fdct_wrapper<vp9_fwht4x4_sse2>, &idct_wrapper<vpx_iwht4x4_16_add_sse2>, 4, 1
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(SSE2, TransWHT,
|
||||
::testing::Values(make_tuple(0, &wht_sse2_func_info, 0,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_SSE2 && !CONFIG_EMULATE_HARDWARE
|
||||
|
||||
#if HAVE_VSX && !CONFIG_EMULATE_HARDWARE && !CONFIG_VP9_HIGHBITDEPTH
|
||||
static const FuncInfo wht_vsx_func_info = {
|
||||
&fdct_wrapper<vp9_fwht4x4_c>, &idct_wrapper<vpx_iwht4x4_16_add_vsx>, 4, 1
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VSX, TransWHT,
|
||||
::testing::Values(make_tuple(0, &wht_vsx_func_info, 0,
|
||||
VPX_BITS_8)));
|
||||
#endif // HAVE_VSX && !CONFIG_EMULATE_HARDWARE
|
||||
} // namespace
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "test/ivf_video_source.h"
|
||||
#include "vpx/vp8dx.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#define NELEMENTS(x) static_cast<int>(sizeof(x) / sizeof(x[0]))
|
||||
|
||||
TEST(DecodeAPI, InvalidParams) {
|
||||
static const vpx_codec_iface_t *kCodecs[] = {
|
||||
#if CONFIG_VP8_DECODER
|
||||
&vpx_codec_vp8_dx_algo,
|
||||
#endif
|
||||
#if CONFIG_VP9_DECODER
|
||||
&vpx_codec_vp9_dx_algo,
|
||||
#endif
|
||||
};
|
||||
uint8_t buf[1] = { 0 };
|
||||
vpx_codec_ctx_t dec;
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_dec_init(NULL, NULL, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_dec_init(&dec, NULL, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_decode(NULL, NULL, 0, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_decode(NULL, buf, 0, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_decode(NULL, buf, NELEMENTS(buf), NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_decode(NULL, NULL, NELEMENTS(buf), NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_destroy(NULL));
|
||||
EXPECT_TRUE(vpx_codec_error(NULL) != NULL);
|
||||
|
||||
for (int i = 0; i < NELEMENTS(kCodecs); ++i) {
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_dec_init(NULL, kCodecs[i], NULL, 0));
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_dec_init(&dec, kCodecs[i], NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_UNSUP_BITSTREAM,
|
||||
vpx_codec_decode(&dec, buf, NELEMENTS(buf), NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_decode(&dec, NULL, NELEMENTS(buf), NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_decode(&dec, buf, 0, NULL, 0));
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_destroy(&dec));
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_VP8_DECODER
|
||||
TEST(DecodeAPI, OptionalParams) {
|
||||
vpx_codec_ctx_t dec;
|
||||
|
||||
#if CONFIG_ERROR_CONCEALMENT
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_dec_init(&dec, &vpx_codec_vp8_dx_algo, NULL,
|
||||
VPX_CODEC_USE_ERROR_CONCEALMENT));
|
||||
#else
|
||||
EXPECT_EQ(VPX_CODEC_INCAPABLE,
|
||||
vpx_codec_dec_init(&dec, &vpx_codec_vp8_dx_algo, NULL,
|
||||
VPX_CODEC_USE_ERROR_CONCEALMENT));
|
||||
#endif // CONFIG_ERROR_CONCEALMENT
|
||||
}
|
||||
#endif // CONFIG_VP8_DECODER
|
||||
|
||||
#if CONFIG_VP9_DECODER
|
||||
// Test VP9 codec controls after a decode error to ensure the code doesn't
|
||||
// misbehave.
|
||||
void TestVp9Controls(vpx_codec_ctx_t *dec) {
|
||||
static const int kControls[] = { VP8D_GET_LAST_REF_UPDATES,
|
||||
VP8D_GET_FRAME_CORRUPTED,
|
||||
VP9D_GET_DISPLAY_SIZE, VP9D_GET_FRAME_SIZE };
|
||||
int val[2];
|
||||
|
||||
for (int i = 0; i < NELEMENTS(kControls); ++i) {
|
||||
const vpx_codec_err_t res = vpx_codec_control_(dec, kControls[i], val);
|
||||
switch (kControls[i]) {
|
||||
case VP8D_GET_FRAME_CORRUPTED:
|
||||
EXPECT_EQ(VPX_CODEC_ERROR, res) << kControls[i];
|
||||
break;
|
||||
default: EXPECT_EQ(VPX_CODEC_OK, res) << kControls[i]; break;
|
||||
}
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_control_(dec, kControls[i], NULL));
|
||||
}
|
||||
|
||||
vp9_ref_frame_t ref;
|
||||
ref.idx = 0;
|
||||
EXPECT_EQ(VPX_CODEC_ERROR, vpx_codec_control(dec, VP9_GET_REFERENCE, &ref));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_control(dec, VP9_GET_REFERENCE, NULL));
|
||||
|
||||
vpx_ref_frame_t ref_copy;
|
||||
const int width = 352;
|
||||
const int height = 288;
|
||||
ASSERT_TRUE(
|
||||
vpx_img_alloc(&ref_copy.img, VPX_IMG_FMT_I420, width, height, 1) != NULL);
|
||||
ref_copy.frame_type = VP8_LAST_FRAME;
|
||||
EXPECT_EQ(VPX_CODEC_ERROR,
|
||||
vpx_codec_control(dec, VP8_COPY_REFERENCE, &ref_copy));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_control(dec, VP8_COPY_REFERENCE, NULL));
|
||||
vpx_img_free(&ref_copy.img);
|
||||
}
|
||||
|
||||
TEST(DecodeAPI, Vp9InvalidDecode) {
|
||||
const vpx_codec_iface_t *const codec = &vpx_codec_vp9_dx_algo;
|
||||
const char filename[] =
|
||||
"invalid-vp90-2-00-quantizer-00.webm.ivf.s5861_r01-05_b6-.v2.ivf";
|
||||
libvpx_test::IVFVideoSource video(filename);
|
||||
video.Init();
|
||||
video.Begin();
|
||||
ASSERT_TRUE(!HasFailure());
|
||||
|
||||
vpx_codec_ctx_t dec;
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_dec_init(&dec, codec, NULL, 0));
|
||||
const uint32_t frame_size = static_cast<uint32_t>(video.frame_size());
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
EXPECT_EQ(VPX_CODEC_MEM_ERROR,
|
||||
vpx_codec_decode(&dec, video.cxdata(), frame_size, NULL, 0));
|
||||
#else
|
||||
EXPECT_EQ(VPX_CODEC_UNSUP_BITSTREAM,
|
||||
vpx_codec_decode(&dec, video.cxdata(), frame_size, NULL, 0));
|
||||
#endif
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
EXPECT_EQ(NULL, vpx_codec_get_frame(&dec, &iter));
|
||||
|
||||
TestVp9Controls(&dec);
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_destroy(&dec));
|
||||
}
|
||||
|
||||
void TestPeekInfo(const uint8_t *const data, uint32_t data_sz,
|
||||
uint32_t peek_size) {
|
||||
const vpx_codec_iface_t *const codec = &vpx_codec_vp9_dx_algo;
|
||||
// Verify behavior of vpx_codec_decode. vpx_codec_decode doesn't even get
|
||||
// to decoder_peek_si_internal on frames of size < 8.
|
||||
if (data_sz >= 8) {
|
||||
vpx_codec_ctx_t dec;
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_dec_init(&dec, codec, NULL, 0));
|
||||
EXPECT_EQ((data_sz < peek_size) ? VPX_CODEC_UNSUP_BITSTREAM
|
||||
: VPX_CODEC_CORRUPT_FRAME,
|
||||
vpx_codec_decode(&dec, data, data_sz, NULL, 0));
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
EXPECT_EQ(NULL, vpx_codec_get_frame(&dec, &iter));
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_destroy(&dec));
|
||||
}
|
||||
|
||||
// Verify behavior of vpx_codec_peek_stream_info.
|
||||
vpx_codec_stream_info_t si;
|
||||
si.sz = sizeof(si);
|
||||
EXPECT_EQ((data_sz < peek_size) ? VPX_CODEC_UNSUP_BITSTREAM : VPX_CODEC_OK,
|
||||
vpx_codec_peek_stream_info(codec, data, data_sz, &si));
|
||||
}
|
||||
|
||||
TEST(DecodeAPI, Vp9PeekStreamInfo) {
|
||||
// The first 9 bytes are valid and the rest of the bytes are made up. Until
|
||||
// size 10, this should return VPX_CODEC_UNSUP_BITSTREAM and after that it
|
||||
// should return VPX_CODEC_CORRUPT_FRAME.
|
||||
const uint8_t data[32] = {
|
||||
0x85, 0xa4, 0xc1, 0xa1, 0x38, 0x81, 0xa3, 0x49, 0x83, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
for (uint32_t data_sz = 1; data_sz <= 32; ++data_sz) {
|
||||
TestPeekInfo(data, data_sz, 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DecodeAPI, Vp9PeekStreamInfoTruncated) {
|
||||
// This profile 1 header requires 10.25 bytes, ensure
|
||||
// vpx_codec_peek_stream_info doesn't over read.
|
||||
const uint8_t profile1_data[10] = { 0xa4, 0xe9, 0x30, 0x68, 0x53,
|
||||
0xe9, 0x30, 0x68, 0x53, 0x04 };
|
||||
|
||||
for (uint32_t data_sz = 1; data_sz <= 10; ++data_sz) {
|
||||
TestPeekInfo(profile1_data, data_sz, 11);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_VP9_DECODER
|
||||
|
||||
TEST(DecodeAPI, HighBitDepthCapability) {
|
||||
// VP8 should not claim VP9 HBD as a capability.
|
||||
#if CONFIG_VP8_DECODER
|
||||
const vpx_codec_caps_t vp8_caps = vpx_codec_get_caps(&vpx_codec_vp8_dx_algo);
|
||||
EXPECT_EQ(vp8_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
|
||||
#endif
|
||||
|
||||
#if CONFIG_VP9_DECODER
|
||||
const vpx_codec_caps_t vp9_caps = vpx_codec_get_caps(&vpx_codec_vp9_dx_algo);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, VPX_CODEC_CAP_HIGHBITDEPTH);
|
||||
#else
|
||||
EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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 <tuple>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/util.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class DecodeCorruptedFrameTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::testing::TestWithParam<
|
||||
std::tuple<const libvpx_test::CodecFactory *> > {
|
||||
public:
|
||||
DecodeCorruptedFrameTest() : EncoderTest(GET_PARAM(0)) {}
|
||||
|
||||
protected:
|
||||
virtual ~DecodeCorruptedFrameTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(::libvpx_test::kRealTime);
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 600;
|
||||
|
||||
// Set small key frame distance such that we insert more key frames.
|
||||
cfg_.kf_max_dist = 3;
|
||||
dec_cfg_.threads = 1;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) encoder->Control(VP8E_SET_CPUUSED, 7);
|
||||
}
|
||||
|
||||
virtual void MismatchHook(const vpx_image_t * /*img1*/,
|
||||
const vpx_image_t * /*img2*/) {}
|
||||
|
||||
virtual const vpx_codec_cx_pkt_t *MutateEncoderOutputHook(
|
||||
const vpx_codec_cx_pkt_t *pkt) {
|
||||
// Don't edit frame packet on key frame.
|
||||
if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) return pkt;
|
||||
if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) return pkt;
|
||||
|
||||
memcpy(&modified_pkt_, pkt, sizeof(*pkt));
|
||||
|
||||
// Halve the size so it's corrupted to decoder.
|
||||
modified_pkt_.data.frame.sz = modified_pkt_.data.frame.sz / 2;
|
||||
|
||||
return &modified_pkt_;
|
||||
}
|
||||
|
||||
virtual bool HandleDecodeResult(const vpx_codec_err_t res_dec,
|
||||
const libvpx_test::VideoSource & /*video*/,
|
||||
libvpx_test::Decoder *decoder) {
|
||||
EXPECT_NE(res_dec, VPX_CODEC_MEM_ERROR) << decoder->DecodeError();
|
||||
return VPX_CODEC_MEM_ERROR != res_dec;
|
||||
}
|
||||
|
||||
vpx_codec_cx_pkt_t modified_pkt_;
|
||||
};
|
||||
|
||||
TEST_P(DecodeCorruptedFrameTest, DecodeCorruptedFrame) {
|
||||
cfg_.rc_target_bitrate = 200;
|
||||
cfg_.g_error_resilient = 0;
|
||||
|
||||
::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
|
||||
30, 1, 0, 300);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
}
|
||||
|
||||
#if CONFIG_VP9
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
VP9, DecodeCorruptedFrameTest,
|
||||
::testing::Values(
|
||||
static_cast<const libvpx_test::CodecFactory *>(&libvpx_test::kVP9)));
|
||||
#endif // CONFIG_VP9
|
||||
|
||||
#if CONFIG_VP8
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
VP8, DecodeCorruptedFrameTest,
|
||||
::testing::Values(
|
||||
static_cast<const libvpx_test::CodecFactory *>(&libvpx_test::kVP8)));
|
||||
#endif // CONFIG_VP8
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/ivf_video_source.h"
|
||||
#include "test/md5_helper.h"
|
||||
#include "test/util.h"
|
||||
#include "test/webm_video_source.h"
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
#include "./ivfenc.h"
|
||||
#include "./vpx_version.h"
|
||||
|
||||
using std::make_tuple;
|
||||
|
||||
namespace {
|
||||
|
||||
#define VIDEO_NAME 0
|
||||
#define THREADS 1
|
||||
|
||||
const double kUsecsInSec = 1000000.0;
|
||||
const char kNewEncodeOutputFile[] = "new_encode.ivf";
|
||||
|
||||
/*
|
||||
DecodePerfTest takes a tuple of filename + number of threads to decode with
|
||||
*/
|
||||
typedef std::tuple<const char *, unsigned> DecodePerfParam;
|
||||
|
||||
const DecodePerfParam kVP9DecodePerfVectors[] = {
|
||||
make_tuple("vp90-2-bbb_426x240_tile_1x1_180kbps.webm", 1),
|
||||
make_tuple("vp90-2-bbb_640x360_tile_1x2_337kbps.webm", 2),
|
||||
make_tuple("vp90-2-bbb_854x480_tile_1x2_651kbps.webm", 2),
|
||||
make_tuple("vp90-2-bbb_1280x720_tile_1x4_1310kbps.webm", 4),
|
||||
make_tuple("vp90-2-bbb_1920x1080_tile_1x1_2581kbps.webm", 1),
|
||||
make_tuple("vp90-2-bbb_1920x1080_tile_1x4_2586kbps.webm", 4),
|
||||
make_tuple("vp90-2-bbb_1920x1080_tile_1x4_fpm_2304kbps.webm", 4),
|
||||
make_tuple("vp90-2-sintel_426x182_tile_1x1_171kbps.webm", 1),
|
||||
make_tuple("vp90-2-sintel_640x272_tile_1x2_318kbps.webm", 2),
|
||||
make_tuple("vp90-2-sintel_854x364_tile_1x2_621kbps.webm", 2),
|
||||
make_tuple("vp90-2-sintel_1280x546_tile_1x4_1257kbps.webm", 4),
|
||||
make_tuple("vp90-2-sintel_1920x818_tile_1x4_fpm_2279kbps.webm", 4),
|
||||
make_tuple("vp90-2-tos_426x178_tile_1x1_181kbps.webm", 1),
|
||||
make_tuple("vp90-2-tos_640x266_tile_1x2_336kbps.webm", 2),
|
||||
make_tuple("vp90-2-tos_854x356_tile_1x2_656kbps.webm", 2),
|
||||
make_tuple("vp90-2-tos_854x356_tile_1x2_fpm_546kbps.webm", 2),
|
||||
make_tuple("vp90-2-tos_1280x534_tile_1x4_1306kbps.webm", 4),
|
||||
make_tuple("vp90-2-tos_1280x534_tile_1x4_fpm_952kbps.webm", 4),
|
||||
make_tuple("vp90-2-tos_1920x800_tile_1x4_fpm_2335kbps.webm", 4),
|
||||
};
|
||||
|
||||
/*
|
||||
In order to reflect real world performance as much as possible, Perf tests
|
||||
*DO NOT* do any correctness checks. Please run them alongside correctness
|
||||
tests to ensure proper codec integrity. Furthermore, in this test we
|
||||
deliberately limit the amount of system calls we make to avoid OS
|
||||
preemption.
|
||||
|
||||
TODO(joshualitt) create a more detailed perf measurement test to collect
|
||||
power/temp/min max frame decode times/etc
|
||||
*/
|
||||
|
||||
class DecodePerfTest : public ::testing::TestWithParam<DecodePerfParam> {};
|
||||
|
||||
TEST_P(DecodePerfTest, PerfTest) {
|
||||
const char *const video_name = GET_PARAM(VIDEO_NAME);
|
||||
const unsigned threads = GET_PARAM(THREADS);
|
||||
|
||||
libvpx_test::WebMVideoSource video(video_name);
|
||||
video.Init();
|
||||
|
||||
vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
|
||||
cfg.threads = threads;
|
||||
libvpx_test::VP9Decoder decoder(cfg, 0);
|
||||
|
||||
vpx_usec_timer t;
|
||||
vpx_usec_timer_start(&t);
|
||||
|
||||
for (video.Begin(); video.cxdata() != NULL; video.Next()) {
|
||||
decoder.DecodeFrame(video.cxdata(), video.frame_size());
|
||||
}
|
||||
|
||||
vpx_usec_timer_mark(&t);
|
||||
const double elapsed_secs = double(vpx_usec_timer_elapsed(&t)) / kUsecsInSec;
|
||||
const unsigned frames = video.frame_number();
|
||||
const double fps = double(frames) / elapsed_secs;
|
||||
|
||||
printf("{\n");
|
||||
printf("\t\"type\" : \"decode_perf_test\",\n");
|
||||
printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
|
||||
printf("\t\"videoName\" : \"%s\",\n", video_name);
|
||||
printf("\t\"threadCount\" : %u,\n", threads);
|
||||
printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
|
||||
printf("\t\"totalFrames\" : %u,\n", frames);
|
||||
printf("\t\"framesPerSecond\" : %f\n", fps);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VP9, DecodePerfTest,
|
||||
::testing::ValuesIn(kVP9DecodePerfVectors));
|
||||
|
||||
class VP9NewEncodeDecodePerfTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
|
||||
protected:
|
||||
VP9NewEncodeDecodePerfTest()
|
||||
: EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)), speed_(0),
|
||||
outfile_(0), out_frames_(0) {}
|
||||
|
||||
virtual ~VP9NewEncodeDecodePerfTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(encoding_mode_);
|
||||
|
||||
cfg_.g_lag_in_frames = 25;
|
||||
cfg_.rc_min_quantizer = 2;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_dropframe_thresh = 0;
|
||||
cfg_.rc_undershoot_pct = 50;
|
||||
cfg_.rc_overshoot_pct = 50;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 600;
|
||||
cfg_.rc_resize_allowed = 0;
|
||||
cfg_.rc_end_usage = VPX_VBR;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
encoder->Control(VP8E_SET_CPUUSED, speed_);
|
||||
encoder->Control(VP9E_SET_FRAME_PARALLEL_DECODING, 1);
|
||||
encoder->Control(VP9E_SET_TILE_COLUMNS, 2);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) {
|
||||
const std::string data_path = getenv("LIBVPX_TEST_DATA_PATH");
|
||||
const std::string path_to_source = data_path + "/" + kNewEncodeOutputFile;
|
||||
outfile_ = fopen(path_to_source.c_str(), "wb");
|
||||
ASSERT_TRUE(outfile_ != NULL);
|
||||
}
|
||||
|
||||
virtual void EndPassHook() {
|
||||
if (outfile_ != NULL) {
|
||||
if (!fseek(outfile_, 0, SEEK_SET)) {
|
||||
ivf_write_file_header(outfile_, &cfg_, VP9_FOURCC, out_frames_);
|
||||
}
|
||||
fclose(outfile_);
|
||||
outfile_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
++out_frames_;
|
||||
|
||||
// Write initial file header if first frame.
|
||||
if (pkt->data.frame.pts == 0) {
|
||||
ivf_write_file_header(outfile_, &cfg_, VP9_FOURCC, out_frames_);
|
||||
}
|
||||
|
||||
// Write frame header and data.
|
||||
ivf_write_frame_header(outfile_, out_frames_, pkt->data.frame.sz);
|
||||
ASSERT_EQ(fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_),
|
||||
pkt->data.frame.sz);
|
||||
}
|
||||
|
||||
virtual bool DoDecode() const { return false; }
|
||||
|
||||
void set_speed(unsigned int speed) { speed_ = speed; }
|
||||
|
||||
private:
|
||||
libvpx_test::TestMode encoding_mode_;
|
||||
uint32_t speed_;
|
||||
FILE *outfile_;
|
||||
uint32_t out_frames_;
|
||||
};
|
||||
|
||||
struct EncodePerfTestVideo {
|
||||
EncodePerfTestVideo(const char *name_, uint32_t width_, uint32_t height_,
|
||||
uint32_t bitrate_, int frames_)
|
||||
: name(name_), width(width_), height(height_), bitrate(bitrate_),
|
||||
frames(frames_) {}
|
||||
const char *name;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bitrate;
|
||||
int frames;
|
||||
};
|
||||
|
||||
const EncodePerfTestVideo kVP9EncodePerfTestVectors[] = {
|
||||
EncodePerfTestVideo("niklas_1280_720_30.yuv", 1280, 720, 600, 470),
|
||||
};
|
||||
|
||||
TEST_P(VP9NewEncodeDecodePerfTest, PerfTest) {
|
||||
SetUp();
|
||||
|
||||
// TODO(JBB): Make this work by going through the set of given files.
|
||||
const int i = 0;
|
||||
const vpx_rational timebase = { 33333333, 1000000000 };
|
||||
cfg_.g_timebase = timebase;
|
||||
cfg_.rc_target_bitrate = kVP9EncodePerfTestVectors[i].bitrate;
|
||||
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
|
||||
const char *video_name = kVP9EncodePerfTestVectors[i].name;
|
||||
libvpx_test::I420VideoSource video(
|
||||
video_name, kVP9EncodePerfTestVectors[i].width,
|
||||
kVP9EncodePerfTestVectors[i].height, timebase.den, timebase.num, 0,
|
||||
kVP9EncodePerfTestVectors[i].frames);
|
||||
set_speed(2);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
|
||||
const uint32_t threads = 4;
|
||||
|
||||
libvpx_test::IVFVideoSource decode_video(kNewEncodeOutputFile);
|
||||
decode_video.Init();
|
||||
|
||||
vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
|
||||
cfg.threads = threads;
|
||||
libvpx_test::VP9Decoder decoder(cfg, 0);
|
||||
|
||||
vpx_usec_timer t;
|
||||
vpx_usec_timer_start(&t);
|
||||
|
||||
for (decode_video.Begin(); decode_video.cxdata() != NULL;
|
||||
decode_video.Next()) {
|
||||
decoder.DecodeFrame(decode_video.cxdata(), decode_video.frame_size());
|
||||
}
|
||||
|
||||
vpx_usec_timer_mark(&t);
|
||||
const double elapsed_secs =
|
||||
static_cast<double>(vpx_usec_timer_elapsed(&t)) / kUsecsInSec;
|
||||
const unsigned decode_frames = decode_video.frame_number();
|
||||
const double fps = static_cast<double>(decode_frames) / elapsed_secs;
|
||||
|
||||
printf("{\n");
|
||||
printf("\t\"type\" : \"decode_perf_test\",\n");
|
||||
printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
|
||||
printf("\t\"videoName\" : \"%s\",\n", kNewEncodeOutputFile);
|
||||
printf("\t\"threadCount\" : %u,\n", threads);
|
||||
printf("\t\"decodeTimeSecs\" : %f,\n", elapsed_secs);
|
||||
printf("\t\"totalFrames\" : %u,\n", decode_frames);
|
||||
printf("\t\"framesPerSecond\" : %f\n", fps);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(VP9NewEncodeDecodePerfTest,
|
||||
::testing::Values(::libvpx_test::kTwoPassGood));
|
||||
} // namespace
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/ivf_video_source.h"
|
||||
#include "test/test_vectors.h"
|
||||
#include "test/util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const unsigned int kNumFrames = 19;
|
||||
|
||||
class DecodeSvcTest : public ::libvpx_test::DecoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<const char *> {
|
||||
protected:
|
||||
DecodeSvcTest() : DecoderTest(GET_PARAM(::libvpx_test::kCodecFactoryParam)) {}
|
||||
virtual ~DecodeSvcTest() {}
|
||||
|
||||
virtual void PreDecodeFrameHook(
|
||||
const libvpx_test::CompressedVideoSource &video,
|
||||
libvpx_test::Decoder *decoder) {
|
||||
if (video.frame_number() == 0)
|
||||
decoder->Control(VP9_DECODE_SVC_SPATIAL_LAYER, spatial_layer_);
|
||||
}
|
||||
|
||||
virtual void DecompressedFrameHook(const vpx_image_t &img,
|
||||
const unsigned int frame_number) {
|
||||
ASSERT_EQ(img.d_w, width_);
|
||||
ASSERT_EQ(img.d_h, height_);
|
||||
total_frames_ = frame_number;
|
||||
}
|
||||
|
||||
int spatial_layer_;
|
||||
unsigned int width_;
|
||||
unsigned int height_;
|
||||
unsigned int total_frames_;
|
||||
};
|
||||
|
||||
// SVC test vector is 1280x720, with 3 spatial layers, and 20 frames.
|
||||
|
||||
// Decode the SVC test vector, which has 3 spatial layers, and decode up to
|
||||
// spatial layer 0. Verify the resolution of each decoded frame and the total
|
||||
// number of frames decoded. This results in 1/4x1/4 resolution (320x180).
|
||||
TEST_P(DecodeSvcTest, DecodeSvcTestUpToSpatialLayer0) {
|
||||
const std::string filename = GET_PARAM(1);
|
||||
std::unique_ptr<libvpx_test::CompressedVideoSource> video;
|
||||
video.reset(new libvpx_test::IVFVideoSource(filename));
|
||||
ASSERT_TRUE(video.get() != NULL);
|
||||
video->Init();
|
||||
total_frames_ = 0;
|
||||
spatial_layer_ = 0;
|
||||
width_ = 320;
|
||||
height_ = 180;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
|
||||
ASSERT_EQ(total_frames_, kNumFrames);
|
||||
}
|
||||
|
||||
// Decode the SVC test vector, which has 3 spatial layers, and decode up to
|
||||
// spatial layer 1. Verify the resolution of each decoded frame and the total
|
||||
// number of frames decoded. This results in 1/2x1/2 resolution (640x360).
|
||||
TEST_P(DecodeSvcTest, DecodeSvcTestUpToSpatialLayer1) {
|
||||
const std::string filename = GET_PARAM(1);
|
||||
std::unique_ptr<libvpx_test::CompressedVideoSource> video;
|
||||
video.reset(new libvpx_test::IVFVideoSource(filename));
|
||||
ASSERT_TRUE(video.get() != NULL);
|
||||
video->Init();
|
||||
total_frames_ = 0;
|
||||
spatial_layer_ = 1;
|
||||
width_ = 640;
|
||||
height_ = 360;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
|
||||
ASSERT_EQ(total_frames_, kNumFrames);
|
||||
}
|
||||
|
||||
// Decode the SVC test vector, which has 3 spatial layers, and decode up to
|
||||
// spatial layer 2. Verify the resolution of each decoded frame and the total
|
||||
// number of frames decoded. This results in the full resolution (1280x720).
|
||||
TEST_P(DecodeSvcTest, DecodeSvcTestUpToSpatialLayer2) {
|
||||
const std::string filename = GET_PARAM(1);
|
||||
std::unique_ptr<libvpx_test::CompressedVideoSource> video;
|
||||
video.reset(new libvpx_test::IVFVideoSource(filename));
|
||||
ASSERT_TRUE(video.get() != NULL);
|
||||
video->Init();
|
||||
total_frames_ = 0;
|
||||
spatial_layer_ = 2;
|
||||
width_ = 1280;
|
||||
height_ = 720;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
|
||||
ASSERT_EQ(total_frames_, kNumFrames);
|
||||
}
|
||||
|
||||
// Decode the SVC test vector, which has 3 spatial layers, and decode up to
|
||||
// spatial layer 10. Verify the resolution of each decoded frame and the total
|
||||
// number of frames decoded. This is beyond the number of spatial layers, so
|
||||
// the decoding should result in the full resolution (1280x720).
|
||||
TEST_P(DecodeSvcTest, DecodeSvcTestUpToSpatialLayer10) {
|
||||
const std::string filename = GET_PARAM(1);
|
||||
std::unique_ptr<libvpx_test::CompressedVideoSource> video;
|
||||
video.reset(new libvpx_test::IVFVideoSource(filename));
|
||||
ASSERT_TRUE(video.get() != NULL);
|
||||
video->Init();
|
||||
total_frames_ = 0;
|
||||
spatial_layer_ = 10;
|
||||
width_ = 1280;
|
||||
height_ = 720;
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(video.get()));
|
||||
ASSERT_EQ(total_frames_, kNumFrames);
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(
|
||||
DecodeSvcTest, ::testing::ValuesIn(libvpx_test::kVP9TestVectorsSvc,
|
||||
libvpx_test::kVP9TestVectorsSvc +
|
||||
libvpx_test::kNumVP9TestVectorsSvc));
|
||||
} // namespace
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/video_source.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
|
||||
const char kVP8Name[] = "WebM Project VP8";
|
||||
|
||||
vpx_codec_err_t Decoder::PeekStream(const uint8_t *cxdata, size_t size,
|
||||
vpx_codec_stream_info_t *stream_info) {
|
||||
return vpx_codec_peek_stream_info(
|
||||
CodecInterface(), cxdata, static_cast<unsigned int>(size), stream_info);
|
||||
}
|
||||
|
||||
vpx_codec_err_t Decoder::DecodeFrame(const uint8_t *cxdata, size_t size) {
|
||||
return DecodeFrame(cxdata, size, NULL);
|
||||
}
|
||||
|
||||
vpx_codec_err_t Decoder::DecodeFrame(const uint8_t *cxdata, size_t size,
|
||||
void *user_priv) {
|
||||
vpx_codec_err_t res_dec;
|
||||
InitOnce();
|
||||
API_REGISTER_STATE_CHECK(
|
||||
res_dec = vpx_codec_decode(
|
||||
&decoder_, cxdata, static_cast<unsigned int>(size), user_priv, 0));
|
||||
return res_dec;
|
||||
}
|
||||
|
||||
bool Decoder::IsVP8() const {
|
||||
const char *codec_name = GetDecoderName();
|
||||
return strncmp(kVP8Name, codec_name, sizeof(kVP8Name) - 1) == 0;
|
||||
}
|
||||
|
||||
void DecoderTest::HandlePeekResult(Decoder *const decoder,
|
||||
CompressedVideoSource *video,
|
||||
const vpx_codec_err_t res_peek) {
|
||||
const bool is_vp8 = decoder->IsVP8();
|
||||
if (is_vp8) {
|
||||
/* Vp8's implementation of PeekStream returns an error if the frame you
|
||||
* pass it is not a keyframe, so we only expect VPX_CODEC_OK on the first
|
||||
* frame, which must be a keyframe. */
|
||||
if (video->frame_number() == 0) {
|
||||
ASSERT_EQ(VPX_CODEC_OK, res_peek)
|
||||
<< "Peek return failed: " << vpx_codec_err_to_string(res_peek);
|
||||
}
|
||||
} else {
|
||||
/* The Vp9 implementation of PeekStream returns an error only if the
|
||||
* data passed to it isn't a valid Vp9 chunk. */
|
||||
ASSERT_EQ(VPX_CODEC_OK, res_peek)
|
||||
<< "Peek return failed: " << vpx_codec_err_to_string(res_peek);
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderTest::RunLoop(CompressedVideoSource *video,
|
||||
const vpx_codec_dec_cfg_t &dec_cfg) {
|
||||
Decoder *const decoder = codec_->CreateDecoder(dec_cfg, flags_);
|
||||
ASSERT_TRUE(decoder != NULL);
|
||||
bool end_of_file = false;
|
||||
|
||||
// Decode frames.
|
||||
for (video->Begin(); !::testing::Test::HasFailure() && !end_of_file;
|
||||
video->Next()) {
|
||||
PreDecodeFrameHook(*video, decoder);
|
||||
|
||||
vpx_codec_stream_info_t stream_info;
|
||||
stream_info.sz = sizeof(stream_info);
|
||||
|
||||
if (video->cxdata() != NULL) {
|
||||
const vpx_codec_err_t res_peek = decoder->PeekStream(
|
||||
video->cxdata(), video->frame_size(), &stream_info);
|
||||
HandlePeekResult(decoder, video, res_peek);
|
||||
ASSERT_FALSE(::testing::Test::HasFailure());
|
||||
|
||||
vpx_codec_err_t res_dec =
|
||||
decoder->DecodeFrame(video->cxdata(), video->frame_size());
|
||||
if (!HandleDecodeResult(res_dec, *video, decoder)) break;
|
||||
} else {
|
||||
// Signal end of the file to the decoder.
|
||||
const vpx_codec_err_t res_dec = decoder->DecodeFrame(NULL, 0);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res_dec) << decoder->DecodeError();
|
||||
end_of_file = true;
|
||||
}
|
||||
|
||||
DxDataIterator dec_iter = decoder->GetDxData();
|
||||
const vpx_image_t *img = NULL;
|
||||
|
||||
// Get decompressed data
|
||||
while (!::testing::Test::HasFailure() && (img = dec_iter.Next())) {
|
||||
DecompressedFrameHook(*img, video->frame_number());
|
||||
}
|
||||
}
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
void DecoderTest::RunLoop(CompressedVideoSource *video) {
|
||||
vpx_codec_dec_cfg_t dec_cfg = vpx_codec_dec_cfg_t();
|
||||
RunLoop(video, dec_cfg);
|
||||
}
|
||||
|
||||
void DecoderTest::set_cfg(const vpx_codec_dec_cfg_t &dec_cfg) {
|
||||
memcpy(&cfg_, &dec_cfg, sizeof(cfg_));
|
||||
}
|
||||
|
||||
void DecoderTest::set_flags(const vpx_codec_flags_t flags) { flags_ = flags; }
|
||||
|
||||
} // namespace libvpx_test
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 VPX_TEST_DECODE_TEST_DRIVER_H_
|
||||
#define VPX_TEST_DECODE_TEST_DRIVER_H_
|
||||
#include <cstring>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "./vpx_config.h"
|
||||
#include "vpx/vpx_decoder.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
|
||||
class CodecFactory;
|
||||
class CompressedVideoSource;
|
||||
|
||||
// Provides an object to handle decoding output
|
||||
class DxDataIterator {
|
||||
public:
|
||||
explicit DxDataIterator(vpx_codec_ctx_t *decoder)
|
||||
: decoder_(decoder), iter_(NULL) {}
|
||||
|
||||
const vpx_image_t *Next() { return vpx_codec_get_frame(decoder_, &iter_); }
|
||||
|
||||
private:
|
||||
vpx_codec_ctx_t *decoder_;
|
||||
vpx_codec_iter_t iter_;
|
||||
};
|
||||
|
||||
// Provides a simplified interface to manage one video decoding.
|
||||
// Similar to Encoder class, the exact services should be added
|
||||
// as more tests are added.
|
||||
class Decoder {
|
||||
public:
|
||||
explicit Decoder(vpx_codec_dec_cfg_t cfg)
|
||||
: cfg_(cfg), flags_(0), init_done_(false) {
|
||||
memset(&decoder_, 0, sizeof(decoder_));
|
||||
}
|
||||
|
||||
Decoder(vpx_codec_dec_cfg_t cfg, const vpx_codec_flags_t flag)
|
||||
: cfg_(cfg), flags_(flag), init_done_(false) {
|
||||
memset(&decoder_, 0, sizeof(decoder_));
|
||||
}
|
||||
|
||||
virtual ~Decoder() { vpx_codec_destroy(&decoder_); }
|
||||
|
||||
vpx_codec_err_t PeekStream(const uint8_t *cxdata, size_t size,
|
||||
vpx_codec_stream_info_t *stream_info);
|
||||
|
||||
vpx_codec_err_t DecodeFrame(const uint8_t *cxdata, size_t size);
|
||||
|
||||
vpx_codec_err_t DecodeFrame(const uint8_t *cxdata, size_t size,
|
||||
void *user_priv);
|
||||
|
||||
DxDataIterator GetDxData() { return DxDataIterator(&decoder_); }
|
||||
|
||||
void Control(int ctrl_id, int arg) { Control(ctrl_id, arg, VPX_CODEC_OK); }
|
||||
|
||||
void Control(int ctrl_id, const void *arg) {
|
||||
InitOnce();
|
||||
const vpx_codec_err_t res = vpx_codec_control_(&decoder_, ctrl_id, arg);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << DecodeError();
|
||||
}
|
||||
|
||||
void Control(int ctrl_id, int arg, vpx_codec_err_t expected_value) {
|
||||
InitOnce();
|
||||
const vpx_codec_err_t res = vpx_codec_control_(&decoder_, ctrl_id, arg);
|
||||
ASSERT_EQ(expected_value, res) << DecodeError();
|
||||
}
|
||||
|
||||
const char *DecodeError() {
|
||||
const char *detail = vpx_codec_error_detail(&decoder_);
|
||||
return detail ? detail : vpx_codec_error(&decoder_);
|
||||
}
|
||||
|
||||
// Passes the external frame buffer information to libvpx.
|
||||
vpx_codec_err_t SetFrameBufferFunctions(
|
||||
vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *user_priv) {
|
||||
InitOnce();
|
||||
return vpx_codec_set_frame_buffer_functions(&decoder_, cb_get, cb_release,
|
||||
user_priv);
|
||||
}
|
||||
|
||||
const char *GetDecoderName() const {
|
||||
return vpx_codec_iface_name(CodecInterface());
|
||||
}
|
||||
|
||||
bool IsVP8() const;
|
||||
|
||||
vpx_codec_ctx_t *GetDecoder() { return &decoder_; }
|
||||
|
||||
protected:
|
||||
virtual vpx_codec_iface_t *CodecInterface() const = 0;
|
||||
|
||||
void InitOnce() {
|
||||
if (!init_done_) {
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_dec_init(&decoder_, CodecInterface(), &cfg_, flags_);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << DecodeError();
|
||||
init_done_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
vpx_codec_ctx_t decoder_;
|
||||
vpx_codec_dec_cfg_t cfg_;
|
||||
vpx_codec_flags_t flags_;
|
||||
bool init_done_;
|
||||
};
|
||||
|
||||
// Common test functionality for all Decoder tests.
|
||||
class DecoderTest {
|
||||
public:
|
||||
// Main decoding loop
|
||||
virtual void RunLoop(CompressedVideoSource *video);
|
||||
virtual void RunLoop(CompressedVideoSource *video,
|
||||
const vpx_codec_dec_cfg_t &dec_cfg);
|
||||
|
||||
virtual void set_cfg(const vpx_codec_dec_cfg_t &dec_cfg);
|
||||
virtual void set_flags(const vpx_codec_flags_t flags);
|
||||
|
||||
// Hook to be called before decompressing every frame.
|
||||
virtual void PreDecodeFrameHook(const CompressedVideoSource & /*video*/,
|
||||
Decoder * /*decoder*/) {}
|
||||
|
||||
// Hook to be called to handle decode result. Return true to continue.
|
||||
virtual bool HandleDecodeResult(const vpx_codec_err_t res_dec,
|
||||
const CompressedVideoSource & /*video*/,
|
||||
Decoder *decoder) {
|
||||
EXPECT_EQ(VPX_CODEC_OK, res_dec) << decoder->DecodeError();
|
||||
return VPX_CODEC_OK == res_dec;
|
||||
}
|
||||
|
||||
// Hook to be called on every decompressed frame.
|
||||
virtual void DecompressedFrameHook(const vpx_image_t & /*img*/,
|
||||
const unsigned int /*frame_number*/) {}
|
||||
|
||||
// Hook to be called on peek result
|
||||
virtual void HandlePeekResult(Decoder *const decoder,
|
||||
CompressedVideoSource *video,
|
||||
const vpx_codec_err_t res_peek);
|
||||
|
||||
protected:
|
||||
explicit DecoderTest(const CodecFactory *codec)
|
||||
: codec_(codec), cfg_(), flags_(0) {}
|
||||
|
||||
virtual ~DecoderTest() {}
|
||||
|
||||
const CodecFactory *codec_;
|
||||
vpx_codec_dec_cfg_t cfg_;
|
||||
vpx_codec_flags_t flags_;
|
||||
};
|
||||
|
||||
} // namespace libvpx_test
|
||||
|
||||
#endif // VPX_TEST_DECODE_TEST_DRIVER_H_
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/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 the libvpx decode_to_md5 example. To add new tests to this
|
||||
## file, do the following:
|
||||
## 1. Write a shell function (this is your test).
|
||||
## 2. Add the function to decode_to_md5_tests (on a new line).
|
||||
##
|
||||
. $(dirname $0)/tools_common.sh
|
||||
|
||||
# Environment check: Make sure input is available:
|
||||
# $VP8_IVF_FILE and $VP9_IVF_FILE are required.
|
||||
decode_to_md5_verify_environment() {
|
||||
if [ ! -e "${VP8_IVF_FILE}" ] || [ ! -e "${VP9_IVF_FILE}" ]; then
|
||||
echo "Libvpx test data must exist in LIBVPX_TEST_DATA_PATH."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs decode_to_md5 on $1 and captures the md5 sum for the final frame. $2 is
|
||||
# interpreted as codec name and used solely to name the output file. $3 is the
|
||||
# expected md5 sum: It must match that of the final frame.
|
||||
decode_to_md5() {
|
||||
local decoder="${LIBVPX_BIN_PATH}/decode_to_md5${VPX_TEST_EXE_SUFFIX}"
|
||||
local input_file="$1"
|
||||
local codec="$2"
|
||||
local expected_md5="$3"
|
||||
local output_file="${VPX_TEST_OUTPUT_DIR}/decode_to_md5_${codec}"
|
||||
|
||||
if [ ! -x "${decoder}" ]; then
|
||||
elog "${decoder} does not exist or is not executable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "${VPX_TEST_PREFIX}" "${decoder}" "${input_file}" "${output_file}" \
|
||||
${devnull}
|
||||
|
||||
[ -e "${output_file}" ] || return 1
|
||||
|
||||
local md5_last_frame="$(tail -n1 "${output_file}" | awk '{print $1}')"
|
||||
local actual_md5="$(echo "${md5_last_frame}" | awk '{print $1}')"
|
||||
[ "${actual_md5}" = "${expected_md5}" ] || return 1
|
||||
}
|
||||
|
||||
decode_to_md5_vp8() {
|
||||
# expected MD5 sum for the last frame.
|
||||
local expected_md5="56794d911b02190212bca92f88ad60c6"
|
||||
|
||||
if [ "$(vp8_decode_available)" = "yes" ]; then
|
||||
decode_to_md5 "${VP8_IVF_FILE}" "vp8" "${expected_md5}"
|
||||
fi
|
||||
}
|
||||
|
||||
decode_to_md5_vp9() {
|
||||
# expected MD5 sum for the last frame.
|
||||
local expected_md5="2952c0eae93f3dadd1aa84c50d3fd6d2"
|
||||
|
||||
if [ "$(vp9_decode_available)" = "yes" ]; then
|
||||
decode_to_md5 "${VP9_IVF_FILE}" "vp9" "${expected_md5}"
|
||||
fi
|
||||
}
|
||||
|
||||
decode_to_md5_tests="decode_to_md5_vp8
|
||||
decode_to_md5_vp9"
|
||||
|
||||
run_tests decode_to_md5_verify_environment "${decode_to_md5_tests}"
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/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 the libvpx decode_with_drops example. To add new tests to
|
||||
## this file, do the following:
|
||||
## 1. Write a shell function (this is your test).
|
||||
## 2. Add the function to decode_with_drops_tests (on a new line).
|
||||
##
|
||||
. $(dirname $0)/tools_common.sh
|
||||
|
||||
# Environment check: Make sure input is available:
|
||||
# $VP8_IVF_FILE and $VP9_IVF_FILE are required.
|
||||
decode_with_drops_verify_environment() {
|
||||
if [ ! -e "${VP8_IVF_FILE}" ] || [ ! -e "${VP9_IVF_FILE}" ]; then
|
||||
echo "Libvpx test data must exist in LIBVPX_TEST_DATA_PATH."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs decode_with_drops on $1, $2 is interpreted as codec name and used solely
|
||||
# to name the output file. $3 is the drop mode, and is passed directly to
|
||||
# decode_with_drops.
|
||||
decode_with_drops() {
|
||||
local decoder="${LIBVPX_BIN_PATH}/decode_with_drops${VPX_TEST_EXE_SUFFIX}"
|
||||
local input_file="$1"
|
||||
local codec="$2"
|
||||
local output_file="${VPX_TEST_OUTPUT_DIR}/decode_with_drops_${codec}"
|
||||
local drop_mode="$3"
|
||||
|
||||
if [ ! -x "${decoder}" ]; then
|
||||
elog "${decoder} does not exist or is not executable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "${VPX_TEST_PREFIX}" "${decoder}" "${input_file}" "${output_file}" \
|
||||
"${drop_mode}" ${devnull}
|
||||
|
||||
[ -e "${output_file}" ] || return 1
|
||||
}
|
||||
|
||||
# Decodes $VP8_IVF_FILE while dropping frames, twice: once in sequence mode,
|
||||
# and once in pattern mode.
|
||||
# Note: This test assumes that $VP8_IVF_FILE has exactly 29 frames, and could
|
||||
# break if the file is modified.
|
||||
decode_with_drops_vp8() {
|
||||
if [ "$(vp8_decode_available)" = "yes" ]; then
|
||||
# Test sequence mode: Drop frames 2-28.
|
||||
decode_with_drops "${VP8_IVF_FILE}" "vp8" "2-28"
|
||||
|
||||
# Test pattern mode: Drop 3 of every 4 frames.
|
||||
decode_with_drops "${VP8_IVF_FILE}" "vp8" "3/4"
|
||||
fi
|
||||
}
|
||||
|
||||
# Decodes $VP9_IVF_FILE while dropping frames, twice: once in sequence mode,
|
||||
# and once in pattern mode.
|
||||
# Note: This test assumes that $VP9_IVF_FILE has exactly 20 frames, and could
|
||||
# break if the file is modified.
|
||||
decode_with_drops_vp9() {
|
||||
if [ "$(vp9_decode_available)" = "yes" ]; then
|
||||
# Test sequence mode: Drop frames 2-28.
|
||||
decode_with_drops "${VP9_IVF_FILE}" "vp9" "2-19"
|
||||
|
||||
# Test pattern mode: Drop 3 of every 4 frames.
|
||||
decode_with_drops "${VP9_IVF_FILE}" "vp9" "3/4"
|
||||
fi
|
||||
}
|
||||
|
||||
decode_with_drops_tests="decode_with_drops_vp8
|
||||
decode_with_drops_vp9"
|
||||
|
||||
run_tests decode_with_drops_verify_environment "${decode_with_drops_tests}"
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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 "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#define NELEMENTS(x) static_cast<int>(sizeof(x) / sizeof(x[0]))
|
||||
|
||||
TEST(EncodeAPI, InvalidParams) {
|
||||
static const vpx_codec_iface_t *kCodecs[] = {
|
||||
#if CONFIG_VP8_ENCODER
|
||||
&vpx_codec_vp8_cx_algo,
|
||||
#endif
|
||||
#if CONFIG_VP9_ENCODER
|
||||
&vpx_codec_vp9_cx_algo,
|
||||
#endif
|
||||
};
|
||||
uint8_t buf[1] = { 0 };
|
||||
vpx_image_t img;
|
||||
vpx_codec_ctx_t enc;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
|
||||
EXPECT_EQ(&img, vpx_img_wrap(&img, VPX_IMG_FMT_I420, 1, 1, 1, buf));
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_enc_init(NULL, NULL, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_enc_init(&enc, NULL, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_encode(NULL, NULL, 0, 0, 0, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_encode(NULL, &img, 0, 0, 0, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM, vpx_codec_destroy(NULL));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_config_default(NULL, NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_config_default(NULL, &cfg, 0));
|
||||
EXPECT_TRUE(vpx_codec_error(NULL) != NULL);
|
||||
|
||||
for (int i = 0; i < NELEMENTS(kCodecs); ++i) {
|
||||
SCOPED_TRACE(vpx_codec_iface_name(kCodecs[i]));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_init(NULL, kCodecs[i], NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_init(&enc, kCodecs[i], NULL, 0));
|
||||
EXPECT_EQ(VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_config_default(kCodecs[i], &cfg, 1));
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_config_default(kCodecs[i], &cfg, 0));
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_init(&enc, kCodecs[i], &cfg, 0));
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_encode(&enc, NULL, 0, 0, 0, 0));
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_destroy(&enc));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EncodeAPI, HighBitDepthCapability) {
|
||||
// VP8 should not claim VP9 HBD as a capability.
|
||||
#if CONFIG_VP8_ENCODER
|
||||
const vpx_codec_caps_t vp8_caps = vpx_codec_get_caps(&vpx_codec_vp8_cx_algo);
|
||||
EXPECT_EQ(vp8_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
|
||||
#endif
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
const vpx_codec_caps_t vp9_caps = vpx_codec_get_caps(&vpx_codec_vp9_cx_algo);
|
||||
#if CONFIG_VP9_HIGHBITDEPTH
|
||||
EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, VPX_CODEC_CAP_HIGHBITDEPTH);
|
||||
#else
|
||||
EXPECT_EQ(vp9_caps & VPX_CODEC_CAP_HIGHBITDEPTH, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_VP8_ENCODER
|
||||
TEST(EncodeAPI, ImageSizeSetting) {
|
||||
const int width = 711;
|
||||
const int height = 360;
|
||||
const int bps = 12;
|
||||
vpx_image_t img;
|
||||
vpx_codec_ctx_t enc;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
uint8_t *img_buf = reinterpret_cast<uint8_t *>(
|
||||
calloc(width * height * bps / 8, sizeof(*img_buf)));
|
||||
vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &cfg, 0);
|
||||
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
|
||||
vpx_img_wrap(&img, VPX_IMG_FMT_I420, width, height, 1, img_buf);
|
||||
|
||||
vpx_codec_enc_init(&enc, vpx_codec_vp8_cx(), &cfg, 0);
|
||||
|
||||
EXPECT_EQ(VPX_CODEC_OK, vpx_codec_encode(&enc, &img, 0, 1, 0, 0));
|
||||
|
||||
free(img_buf);
|
||||
|
||||
vpx_codec_destroy(&enc);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set up 2 spatial streams with 2 temporal layers per stream, and generate
|
||||
// invalid configuration by setting the temporal layer rate allocation
|
||||
// (ts_target_bitrate[]) to 0 for both layers. This should fail independent of
|
||||
// CONFIG_MULTI_RES_ENCODING.
|
||||
TEST(EncodeAPI, MultiResEncode) {
|
||||
static const vpx_codec_iface_t *kCodecs[] = {
|
||||
#if CONFIG_VP8_ENCODER
|
||||
&vpx_codec_vp8_cx_algo,
|
||||
#endif
|
||||
#if CONFIG_VP9_ENCODER
|
||||
&vpx_codec_vp9_cx_algo,
|
||||
#endif
|
||||
};
|
||||
const int width = 1280;
|
||||
const int height = 720;
|
||||
const int width_down = width / 2;
|
||||
const int height_down = height / 2;
|
||||
const int target_bitrate = 1000;
|
||||
const int framerate = 30;
|
||||
|
||||
for (int c = 0; c < NELEMENTS(kCodecs); ++c) {
|
||||
const vpx_codec_iface_t *const iface = kCodecs[c];
|
||||
vpx_codec_ctx_t enc[2];
|
||||
vpx_codec_enc_cfg_t cfg[2];
|
||||
vpx_rational_t dsf[2] = { { 2, 1 }, { 2, 1 } };
|
||||
|
||||
memset(enc, 0, sizeof(enc));
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vpx_codec_enc_config_default(iface, &cfg[i], 0);
|
||||
}
|
||||
|
||||
/* Highest-resolution encoder settings */
|
||||
cfg[0].g_w = width;
|
||||
cfg[0].g_h = height;
|
||||
cfg[0].rc_dropframe_thresh = 0;
|
||||
cfg[0].rc_end_usage = VPX_CBR;
|
||||
cfg[0].rc_resize_allowed = 0;
|
||||
cfg[0].rc_min_quantizer = 2;
|
||||
cfg[0].rc_max_quantizer = 56;
|
||||
cfg[0].rc_undershoot_pct = 100;
|
||||
cfg[0].rc_overshoot_pct = 15;
|
||||
cfg[0].rc_buf_initial_sz = 500;
|
||||
cfg[0].rc_buf_optimal_sz = 600;
|
||||
cfg[0].rc_buf_sz = 1000;
|
||||
cfg[0].g_error_resilient = 1; /* Enable error resilient mode */
|
||||
cfg[0].g_lag_in_frames = 0;
|
||||
|
||||
cfg[0].kf_mode = VPX_KF_AUTO;
|
||||
cfg[0].kf_min_dist = 3000;
|
||||
cfg[0].kf_max_dist = 3000;
|
||||
|
||||
cfg[0].rc_target_bitrate = target_bitrate; /* Set target bitrate */
|
||||
cfg[0].g_timebase.num = 1; /* Set fps */
|
||||
cfg[0].g_timebase.den = framerate;
|
||||
|
||||
memcpy(&cfg[1], &cfg[0], sizeof(cfg[0]));
|
||||
cfg[1].rc_target_bitrate = 500;
|
||||
cfg[1].g_w = width_down;
|
||||
cfg[1].g_h = height_down;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
cfg[i].ts_number_layers = 2;
|
||||
cfg[i].ts_periodicity = 2;
|
||||
cfg[i].ts_rate_decimator[0] = 2;
|
||||
cfg[i].ts_rate_decimator[1] = 1;
|
||||
cfg[i].ts_layer_id[0] = 0;
|
||||
cfg[i].ts_layer_id[1] = 1;
|
||||
// Invalid parameters.
|
||||
cfg[i].ts_target_bitrate[0] = 0;
|
||||
cfg[i].ts_target_bitrate[1] = 0;
|
||||
}
|
||||
|
||||
// VP9 should report incapable, VP8 invalid for all configurations.
|
||||
const char kVP9Name[] = "WebM Project VP9";
|
||||
const bool is_vp9 = strncmp(kVP9Name, vpx_codec_iface_name(iface),
|
||||
sizeof(kVP9Name) - 1) == 0;
|
||||
EXPECT_EQ(is_vp9 ? VPX_CODEC_INCAPABLE : VPX_CODEC_INVALID_PARAM,
|
||||
vpx_codec_enc_init_multi(&enc[0], iface, &cfg[0], 2, 0, &dsf[0]));
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vpx_codec_destroy(&enc[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "./vpx_config.h"
|
||||
#include "./vpx_version.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
#include "test/util.h"
|
||||
#include "test/y4m_video_source.h"
|
||||
#include "vpx_ports/vpx_timer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxPsnr = 100;
|
||||
const double kUsecsInSec = 1000000.0;
|
||||
|
||||
struct EncodePerfTestVideo {
|
||||
EncodePerfTestVideo(const char *name_, uint32_t width_, uint32_t height_,
|
||||
uint32_t bitrate_, int frames_)
|
||||
: name(name_), width(width_), height(height_), bitrate(bitrate_),
|
||||
frames(frames_) {}
|
||||
const char *name;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bitrate;
|
||||
int frames;
|
||||
};
|
||||
|
||||
const EncodePerfTestVideo kVP9EncodePerfTestVectors[] = {
|
||||
EncodePerfTestVideo("desktop_640_360_30.yuv", 640, 360, 200, 2484),
|
||||
EncodePerfTestVideo("kirland_640_480_30.yuv", 640, 480, 200, 300),
|
||||
EncodePerfTestVideo("macmarcomoving_640_480_30.yuv", 640, 480, 200, 987),
|
||||
EncodePerfTestVideo("macmarcostationary_640_480_30.yuv", 640, 480, 200, 718),
|
||||
EncodePerfTestVideo("niklas_640_480_30.yuv", 640, 480, 200, 471),
|
||||
EncodePerfTestVideo("tacomanarrows_640_480_30.yuv", 640, 480, 200, 300),
|
||||
EncodePerfTestVideo("tacomasmallcameramovement_640_480_30.yuv", 640, 480, 200,
|
||||
300),
|
||||
EncodePerfTestVideo("thaloundeskmtg_640_480_30.yuv", 640, 480, 200, 300),
|
||||
EncodePerfTestVideo("niklas_1280_720_30.yuv", 1280, 720, 600, 470),
|
||||
};
|
||||
|
||||
const int kEncodePerfTestSpeeds[] = { 5, 6, 7, 8, 9 };
|
||||
const int kEncodePerfTestThreads[] = { 1, 2, 4 };
|
||||
|
||||
#define NELEMENTS(x) (sizeof((x)) / sizeof((x)[0]))
|
||||
|
||||
class VP9EncodePerfTest
|
||||
: public ::libvpx_test::EncoderTest,
|
||||
public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
|
||||
protected:
|
||||
VP9EncodePerfTest()
|
||||
: EncoderTest(GET_PARAM(0)), min_psnr_(kMaxPsnr), nframes_(0),
|
||||
encoding_mode_(GET_PARAM(1)), speed_(0), threads_(1) {}
|
||||
|
||||
virtual ~VP9EncodePerfTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
InitializeConfig();
|
||||
SetMode(encoding_mode_);
|
||||
|
||||
cfg_.g_lag_in_frames = 0;
|
||||
cfg_.rc_min_quantizer = 2;
|
||||
cfg_.rc_max_quantizer = 56;
|
||||
cfg_.rc_dropframe_thresh = 0;
|
||||
cfg_.rc_undershoot_pct = 50;
|
||||
cfg_.rc_overshoot_pct = 50;
|
||||
cfg_.rc_buf_sz = 1000;
|
||||
cfg_.rc_buf_initial_sz = 500;
|
||||
cfg_.rc_buf_optimal_sz = 600;
|
||||
cfg_.rc_resize_allowed = 0;
|
||||
cfg_.rc_end_usage = VPX_CBR;
|
||||
cfg_.g_error_resilient = 1;
|
||||
cfg_.g_threads = threads_;
|
||||
}
|
||||
|
||||
virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
|
||||
::libvpx_test::Encoder *encoder) {
|
||||
if (video->frame() == 0) {
|
||||
const int log2_tile_columns = 3;
|
||||
encoder->Control(VP8E_SET_CPUUSED, speed_);
|
||||
encoder->Control(VP9E_SET_TILE_COLUMNS, log2_tile_columns);
|
||||
encoder->Control(VP9E_SET_FRAME_PARALLEL_DECODING, 1);
|
||||
encoder->Control(VP8E_SET_ENABLEAUTOALTREF, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void BeginPassHook(unsigned int /*pass*/) {
|
||||
min_psnr_ = kMaxPsnr;
|
||||
nframes_ = 0;
|
||||
}
|
||||
|
||||
virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
|
||||
if (pkt->data.psnr.psnr[0] < min_psnr_) {
|
||||
min_psnr_ = pkt->data.psnr.psnr[0];
|
||||
}
|
||||
}
|
||||
|
||||
// for performance reasons don't decode
|
||||
virtual bool DoDecode() const { return false; }
|
||||
|
||||
double min_psnr() const { return min_psnr_; }
|
||||
|
||||
void set_speed(unsigned int speed) { speed_ = speed; }
|
||||
|
||||
void set_threads(unsigned int threads) { threads_ = threads; }
|
||||
|
||||
private:
|
||||
double min_psnr_;
|
||||
unsigned int nframes_;
|
||||
libvpx_test::TestMode encoding_mode_;
|
||||
unsigned speed_;
|
||||
unsigned int threads_;
|
||||
};
|
||||
|
||||
TEST_P(VP9EncodePerfTest, PerfTest) {
|
||||
for (size_t i = 0; i < NELEMENTS(kVP9EncodePerfTestVectors); ++i) {
|
||||
for (size_t j = 0; j < NELEMENTS(kEncodePerfTestSpeeds); ++j) {
|
||||
for (size_t k = 0; k < NELEMENTS(kEncodePerfTestThreads); ++k) {
|
||||
if (kVP9EncodePerfTestVectors[i].width < 512 &&
|
||||
kEncodePerfTestThreads[k] > 1) {
|
||||
continue;
|
||||
} else if (kVP9EncodePerfTestVectors[i].width < 1024 &&
|
||||
kEncodePerfTestThreads[k] > 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
set_threads(kEncodePerfTestThreads[k]);
|
||||
SetUp();
|
||||
|
||||
const vpx_rational timebase = { 33333333, 1000000000 };
|
||||
cfg_.g_timebase = timebase;
|
||||
cfg_.rc_target_bitrate = kVP9EncodePerfTestVectors[i].bitrate;
|
||||
|
||||
init_flags_ = VPX_CODEC_USE_PSNR;
|
||||
|
||||
const unsigned frames = kVP9EncodePerfTestVectors[i].frames;
|
||||
const char *video_name = kVP9EncodePerfTestVectors[i].name;
|
||||
libvpx_test::I420VideoSource video(
|
||||
video_name, kVP9EncodePerfTestVectors[i].width,
|
||||
kVP9EncodePerfTestVectors[i].height, timebase.den, timebase.num, 0,
|
||||
kVP9EncodePerfTestVectors[i].frames);
|
||||
set_speed(kEncodePerfTestSpeeds[j]);
|
||||
|
||||
vpx_usec_timer t;
|
||||
vpx_usec_timer_start(&t);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
|
||||
|
||||
vpx_usec_timer_mark(&t);
|
||||
const double elapsed_secs = vpx_usec_timer_elapsed(&t) / kUsecsInSec;
|
||||
const double fps = frames / elapsed_secs;
|
||||
const double minimum_psnr = min_psnr();
|
||||
std::string display_name(video_name);
|
||||
if (kEncodePerfTestThreads[k] > 1) {
|
||||
char thread_count[32];
|
||||
snprintf(thread_count, sizeof(thread_count), "_t-%d",
|
||||
kEncodePerfTestThreads[k]);
|
||||
display_name += thread_count;
|
||||
}
|
||||
|
||||
printf("{\n");
|
||||
printf("\t\"type\" : \"encode_perf_test\",\n");
|
||||
printf("\t\"version\" : \"%s\",\n", VERSION_STRING_NOSP);
|
||||
printf("\t\"videoName\" : \"%s\",\n", display_name.c_str());
|
||||
printf("\t\"encodeTimeSecs\" : %f,\n", elapsed_secs);
|
||||
printf("\t\"totalFrames\" : %u,\n", frames);
|
||||
printf("\t\"framesPerSecond\" : %f,\n", fps);
|
||||
printf("\t\"minPsnr\" : %f,\n", minimum_psnr);
|
||||
printf("\t\"speed\" : %d,\n", kEncodePerfTestSpeeds[j]);
|
||||
printf("\t\"threads\" : %d\n", kEncodePerfTestThreads[k]);
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VP9_INSTANTIATE_TEST_CASE(VP9EncodePerfTest,
|
||||
::testing::Values(::libvpx_test::kRealTime));
|
||||
} // namespace
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
|
||||
#include "./vpx_config.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/encode_test_driver.h"
|
||||
#include "test/register_state_check.h"
|
||||
#include "test/video_source.h"
|
||||
|
||||
namespace libvpx_test {
|
||||
void Encoder::InitEncoder(VideoSource *video) {
|
||||
vpx_codec_err_t res;
|
||||
const vpx_image_t *img = video->img();
|
||||
|
||||
if (video->img() && !encoder_.priv) {
|
||||
cfg_.g_w = img->d_w;
|
||||
cfg_.g_h = img->d_h;
|
||||
cfg_.g_timebase = video->timebase();
|
||||
cfg_.rc_twopass_stats_in = stats_->buf();
|
||||
|
||||
res = vpx_codec_enc_init(&encoder_, CodecInterface(), &cfg_, init_flags_);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
|
||||
|
||||
#if CONFIG_VP9_ENCODER
|
||||
if (CodecInterface() == &vpx_codec_vp9_cx_algo) {
|
||||
// Default to 1 tile column for VP9.
|
||||
const int log2_tile_columns = 0;
|
||||
res = vpx_codec_control_(&encoder_, VP9E_SET_TILE_COLUMNS,
|
||||
log2_tile_columns);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if CONFIG_VP8_ENCODER
|
||||
ASSERT_EQ(&vpx_codec_vp8_cx_algo, CodecInterface())
|
||||
<< "Unknown Codec Interface";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Encoder::EncodeFrame(VideoSource *video, const unsigned long frame_flags) {
|
||||
if (video->img()) {
|
||||
EncodeFrameInternal(*video, frame_flags);
|
||||
} else {
|
||||
Flush();
|
||||
}
|
||||
|
||||
// Handle twopass stats
|
||||
CxDataIterator iter = GetCxData();
|
||||
|
||||
while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
|
||||
if (pkt->kind != VPX_CODEC_STATS_PKT) continue;
|
||||
|
||||
stats_->Append(*pkt);
|
||||
}
|
||||
}
|
||||
|
||||
void Encoder::EncodeFrameInternal(const VideoSource &video,
|
||||
const unsigned long frame_flags) {
|
||||
vpx_codec_err_t res;
|
||||
const vpx_image_t *img = video.img();
|
||||
|
||||
// Handle frame resizing
|
||||
if (cfg_.g_w != img->d_w || cfg_.g_h != img->d_h) {
|
||||
cfg_.g_w = img->d_w;
|
||||
cfg_.g_h = img->d_h;
|
||||
res = vpx_codec_enc_config_set(&encoder_, &cfg_);
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
|
||||
}
|
||||
|
||||
// Encode the frame
|
||||
API_REGISTER_STATE_CHECK(res = vpx_codec_encode(&encoder_, img, video.pts(),
|
||||
video.duration(), frame_flags,
|
||||
deadline_));
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
|
||||
}
|
||||
|
||||
void Encoder::Flush() {
|
||||
const vpx_codec_err_t res =
|
||||
vpx_codec_encode(&encoder_, NULL, 0, 0, 0, deadline_);
|
||||
if (!encoder_.priv)
|
||||
ASSERT_EQ(VPX_CODEC_ERROR, res) << EncoderError();
|
||||
else
|
||||
ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError();
|
||||
}
|
||||
|
||||
void EncoderTest::InitializeConfig() {
|
||||
const vpx_codec_err_t res = codec_->DefaultEncoderConfig(&cfg_, 0);
|
||||
dec_cfg_ = vpx_codec_dec_cfg_t();
|
||||
ASSERT_EQ(VPX_CODEC_OK, res);
|
||||
}
|
||||
|
||||
void EncoderTest::SetMode(TestMode mode) {
|
||||
switch (mode) {
|
||||
case kRealTime: deadline_ = VPX_DL_REALTIME; break;
|
||||
|
||||
case kOnePassGood:
|
||||
case kTwoPassGood: deadline_ = VPX_DL_GOOD_QUALITY; break;
|
||||
|
||||
case kOnePassBest:
|
||||
case kTwoPassBest: deadline_ = VPX_DL_BEST_QUALITY; break;
|
||||
|
||||
default: ASSERT_TRUE(false) << "Unexpected mode " << mode;
|
||||
}
|
||||
|
||||
if (mode == kTwoPassGood || mode == kTwoPassBest) {
|
||||
passes_ = 2;
|
||||
} else {
|
||||
passes_ = 1;
|
||||
}
|
||||
}
|
||||
// The function should return "true" most of the time, therefore no early
|
||||
// break-out is implemented within the match checking process.
|
||||
static bool compare_img(const vpx_image_t *img1, const vpx_image_t *img2) {
|
||||
bool match = (img1->fmt == img2->fmt) && (img1->cs == img2->cs) &&
|
||||
(img1->d_w == img2->d_w) && (img1->d_h == img2->d_h);
|
||||
|
||||
if (!match) return false;
|
||||
|
||||
const unsigned int width_y = img1->d_w;
|
||||
const unsigned int height_y = img1->d_h;
|
||||
unsigned int i;
|
||||
for (i = 0; i < height_y; ++i) {
|
||||
match = (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
|
||||
img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
|
||||
width_y) == 0) &&
|
||||
match;
|
||||
}
|
||||
const unsigned int width_uv = (img1->d_w + 1) >> 1;
|
||||
const unsigned int height_uv = (img1->d_h + 1) >> 1;
|
||||
for (i = 0; i < height_uv; ++i) {
|
||||
match = (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
|
||||
img2->planes[VPX_PLANE_U] + i * img2->stride[VPX_PLANE_U],
|
||||
width_uv) == 0) &&
|
||||
match;
|
||||
}
|
||||
for (i = 0; i < height_uv; ++i) {
|
||||
match = (memcmp(img1->planes[VPX_PLANE_V] + i * img1->stride[VPX_PLANE_V],
|
||||
img2->planes[VPX_PLANE_V] + i * img2->stride[VPX_PLANE_V],
|
||||
width_uv) == 0) &&
|
||||
match;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void EncoderTest::MismatchHook(const vpx_image_t * /*img1*/,
|
||||
const vpx_image_t * /*img2*/) {
|
||||
ASSERT_TRUE(0) << "Encode/Decode mismatch found";
|
||||
}
|
||||
|
||||
void EncoderTest::RunLoop(VideoSource *video) {
|
||||
vpx_codec_dec_cfg_t dec_cfg = vpx_codec_dec_cfg_t();
|
||||
|
||||
stats_.Reset();
|
||||
|
||||
ASSERT_TRUE(passes_ == 1 || passes_ == 2);
|
||||
for (unsigned int pass = 0; pass < passes_; pass++) {
|
||||
last_pts_ = 0;
|
||||
|
||||
if (passes_ == 1) {
|
||||
cfg_.g_pass = VPX_RC_ONE_PASS;
|
||||
} else if (pass == 0) {
|
||||
cfg_.g_pass = VPX_RC_FIRST_PASS;
|
||||
} else {
|
||||
cfg_.g_pass = VPX_RC_LAST_PASS;
|
||||
}
|
||||
|
||||
BeginPassHook(pass);
|
||||
std::unique_ptr<Encoder> encoder(
|
||||
codec_->CreateEncoder(cfg_, deadline_, init_flags_, &stats_));
|
||||
ASSERT_TRUE(encoder.get() != NULL);
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(video->Begin());
|
||||
encoder->InitEncoder(video);
|
||||
ASSERT_FALSE(::testing::Test::HasFatalFailure());
|
||||
|
||||
unsigned long dec_init_flags = 0; // NOLINT
|
||||
// Use fragment decoder if encoder outputs partitions.
|
||||
// NOTE: fragment decoder and partition encoder are only supported by VP8.
|
||||
if (init_flags_ & VPX_CODEC_USE_OUTPUT_PARTITION) {
|
||||
dec_init_flags |= VPX_CODEC_USE_INPUT_FRAGMENTS;
|
||||
}
|
||||
std::unique_ptr<Decoder> decoder(
|
||||
codec_->CreateDecoder(dec_cfg, dec_init_flags));
|
||||
bool again;
|
||||
for (again = true; again; video->Next()) {
|
||||
again = (video->img() != NULL);
|
||||
|
||||
PreEncodeFrameHook(video);
|
||||
PreEncodeFrameHook(video, encoder.get());
|
||||
encoder->EncodeFrame(video, frame_flags_);
|
||||
|
||||
PostEncodeFrameHook(encoder.get());
|
||||
|
||||
CxDataIterator iter = encoder->GetCxData();
|
||||
|
||||
bool has_cxdata = false;
|
||||
bool has_dxdata = false;
|
||||
while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
|
||||
pkt = MutateEncoderOutputHook(pkt);
|
||||
again = true;
|
||||
switch (pkt->kind) {
|
||||
case VPX_CODEC_CX_FRAME_PKT:
|
||||
has_cxdata = true;
|
||||
if (decoder.get() != NULL && DoDecode()) {
|
||||
PreDecodeFrameHook(video, decoder.get());
|
||||
vpx_codec_err_t res_dec = decoder->DecodeFrame(
|
||||
(const uint8_t *)pkt->data.frame.buf, pkt->data.frame.sz);
|
||||
|
||||
if (!HandleDecodeResult(res_dec, *video, decoder.get())) break;
|
||||
|
||||
has_dxdata = true;
|
||||
}
|
||||
ASSERT_GE(pkt->data.frame.pts, last_pts_);
|
||||
last_pts_ = pkt->data.frame.pts;
|
||||
FramePktHook(pkt);
|
||||
break;
|
||||
|
||||
case VPX_CODEC_PSNR_PKT: PSNRPktHook(pkt); break;
|
||||
|
||||
case VPX_CODEC_STATS_PKT: StatsPktHook(pkt); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the decoder when there are no more fragments.
|
||||
if ((init_flags_ & VPX_CODEC_USE_OUTPUT_PARTITION) && has_dxdata) {
|
||||
const vpx_codec_err_t res_dec = decoder->DecodeFrame(NULL, 0);
|
||||
if (!HandleDecodeResult(res_dec, *video, decoder.get())) break;
|
||||
}
|
||||
|
||||
if (has_dxdata && has_cxdata) {
|
||||
const vpx_image_t *img_enc = encoder->GetPreviewFrame();
|
||||
DxDataIterator dec_iter = decoder->GetDxData();
|
||||
const vpx_image_t *img_dec = dec_iter.Next();
|
||||
if (img_enc && img_dec) {
|
||||
const bool res = compare_img(img_enc, img_dec);
|
||||
if (!res) { // Mismatch
|
||||
MismatchHook(img_enc, img_dec);
|
||||
}
|
||||
}
|
||||
if (img_dec) DecompressedFrameHook(*img_dec, video->pts());
|
||||
}
|
||||
if (!Continue()) break;
|
||||
}
|
||||
|
||||
EndPassHook();
|
||||
|
||||
if (!Continue()) break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libvpx_test
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user