Rough merge of master into experimental Creates a merge between the master and experimental branches. Fixes a number of conflicts in the build system to allow *either* VP8 or VP9 to be built. Specifically either: $ configure --disable-vp9 $ configure --disable-vp8 --disable-unit-tests VP9 still exports its symbols and files as VP8, so that will be resolved in the next commit. Unit tests are broken in VP9, but this isn't a new issue. They are fixed upstream on origin/experimental as of this writing, but rebasing this merge proved difficult, so will tackle that in a second merge commit. Change-Id: I2b7d852c18efd58d1ebc621b8041fe0260442c21
diff --git a/test/acm_random.h b/test/acm_random.h index dcd1bba..514894e 100644 --- a/test/acm_random.h +++ b/test/acm_random.h
@@ -19,6 +19,10 @@ class ACMRandom { public: + ACMRandom() { + Reset(DeterministicSeed()); + } + explicit ACMRandom(int seed) { Reset(seed); }
diff --git a/test/altref_test.cc b/test/altref_test.cc new file mode 100644 index 0000000..ca05577 --- /dev/null +++ b/test/altref_test.cc
@@ -0,0 +1,71 @@ +/* + * 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/encode_test_driver.h" +#include "test/i420_video_source.h" + +namespace { + +// lookahead range: [kLookAheadMin, kLookAheadMax). +const int kLookAheadMin = 5; +const int kLookAheadMax = 26; + +class AltRefTest : public libvpx_test::EncoderTest, + public ::testing::TestWithParam<int> { + protected: + AltRefTest() : altref_count_(0) {} + virtual ~AltRefTest() {} + + virtual void SetUp() { + InitializeConfig(); + SetMode(libvpx_test::kTwoPassGood); + } + + virtual void BeginPassHook(unsigned int pass) { + altref_count_ = 0; + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video, + libvpx_test::Encoder *encoder) { + if (video->frame() == 1) { + 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 = GetParam(); + + 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); +} + +INSTANTIATE_TEST_CASE_P(NonZeroLag, AltRefTest, + ::testing::Range(kLookAheadMin, kLookAheadMax)); +} // namespace
diff --git a/test/boolcoder_test.cc b/test/boolcoder_test.cc index 119c77a..4e21be8 100644 --- a/test/boolcoder_test.cc +++ b/test/boolcoder_test.cc
@@ -8,26 +8,28 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include "third_party/googletest/src/include/gtest/gtest.h" - extern "C" { -#include "vp9/encoder/boolhuff.h" -#include "vp9/decoder/dboolhuff.h" +#include "vp8/encoder/boolhuff.h" +#include "vp8/decoder/dboolhuff.h" } -#include "acm_random.h" -#include "vpx/vpx_integer.h" +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> -using libvpx_test::ACMRandom; +#include "test/acm_random.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "vpx/vpx_integer.h" namespace { const int num_tests = 10; } // namespace +using libvpx_test::ACMRandom; + TEST(VP8, TestBitIO) { ACMRandom rnd(ACMRandom::DeterministicSeed()); for (int n = 0; n < num_tests; ++n) { @@ -38,15 +40,15 @@ for (int i = 0; i < bits_to_test; ++i) { const int parity = i & 1; probas[i] = - (method == 0) ? 0 : (method == 1) ? 255 : - (method == 2) ? 128 : - (method == 3) ? rnd.Rand8() : - (method == 4) ? (parity ? 0 : 255) : + (method == 0) ? 0 : (method == 1) ? 255 : + (method == 2) ? 128 : + (method == 3) ? rnd.Rand8() : + (method == 4) ? (parity ? 0 : 255) : // alternate between low and high proba: (method == 5) ? (parity ? rnd(128) : 255 - rnd(128)) : (method == 6) ? - (parity ? rnd(64) : 255 - rnd(64)) : - (parity ? rnd(32) : 255 - rnd(32)); + (parity ? rnd(64) : 255 - rnd(64)) : + (parity ? rnd(32) : 255 - rnd(32)); } for (int bit_method = 0; bit_method <= 3; ++bit_method) { const int random_seed = 6432; @@ -54,7 +56,7 @@ ACMRandom bit_rnd(random_seed); BOOL_CODER bw; uint8_t bw_buffer[buffer_size]; - vp8_start_encode(&bw, bw_buffer); + vp8_start_encode(&bw, bw_buffer, bw_buffer + buffer_size); int bit = (bit_method == 0) ? 0 : (bit_method == 1) ? 1 : 0; for (int i = 0; i < bits_to_test; ++i) { @@ -78,7 +80,7 @@ bit = bit_rnd(2); } GTEST_ASSERT_EQ(vp8dx_decode_bool(&br, probas[i]), bit) - << "pos: " << i << " / " << bits_to_test + << "pos: "<< i << " / " << bits_to_test << " bit_method: " << bit_method << " method: " << method; }
diff --git a/test/config_test.cc b/test/config_test.cc new file mode 100644 index 0000000..c4da46e --- /dev/null +++ b/test/config_test.cc
@@ -0,0 +1,61 @@ +/* + * 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/encode_test_driver.h" +#include "test/video_source.h" + +namespace { + +class ConfigTest : public ::libvpx_test::EncoderTest, + public ::testing::TestWithParam<enum libvpx_test::TestMode> { + public: + ConfigTest() : frame_count_in_(0), frame_count_out_(0), frame_count_max_(0) {} + + protected: + virtual void SetUp() { + InitializeConfig(); + SetMode(GetParam()); + } + + 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_; + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + 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_); +} + +INSTANTIATE_TEST_CASE_P(OnePassModes, ConfigTest, ONE_PASS_TEST_MODES); +} // namespace
diff --git a/test/cq_test.cc b/test/cq_test.cc new file mode 100644 index 0000000..42ee2a2 --- /dev/null +++ b/test/cq_test.cc
@@ -0,0 +1,106 @@ +/* + * 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 "third_party/googletest/src/include/gtest/gtest.h" +#include "test/encode_test_driver.h" +#include "test/i420_video_source.h" + +// CQ level range: [kCQLevelMin, kCQLevelMax). +const int kCQLevelMin = 4; +const int kCQLevelMax = 63; +const int kCQLevelStep = 8; +const int kCQTargetBitrate = 2000; + +namespace { + +class CQTest : public libvpx_test::EncoderTest, + public ::testing::TestWithParam<int> { + protected: + CQTest() : cq_level_(GetParam()) { 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 bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video, + libvpx_test::Encoder *encoder) { + if (video->frame() == 1) { + 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 file_size() const { return file_size_; } + int n_frames() const { return n_frames_; } + + private: + int cq_level_; + int file_size_; + double psnr_; + int n_frames_; +}; + +int prev_actual_bitrate = kCQTargetBitrate; +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 int cq_actual_bitrate = file_size() * 8 * 30 / (n_frames() * 1000); + EXPECT_LE(cq_actual_bitrate, kCQTargetBitrate); + EXPECT_LE(cq_actual_bitrate, prev_actual_bitrate); + prev_actual_bitrate = 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); +} + +INSTANTIATE_TEST_CASE_P(CQLevelRange, CQTest, + ::testing::Range(kCQLevelMin, kCQLevelMax, + kCQLevelStep)); +} // namespace
diff --git a/test/datarate_test.cc b/test/datarate_test.cc new file mode 100644 index 0000000..f2a2031 --- /dev/null +++ b/test/datarate_test.cc
@@ -0,0 +1,169 @@ +/* + * 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 "test/encode_test_driver.h" +#include "test/i420_video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +namespace { + +class DatarateTest : public ::libvpx_test::EncoderTest, + public ::testing::TestWithParam<enum libvpx_test::TestMode> { + protected: + virtual void SetUp() { + InitializeConfig(); + SetMode(GetParam()); + ResetModel(); + } + + virtual void ResetModel() { + last_pts_ = 0; + bits_in_buffer_model_ = cfg_.rc_target_bitrate * cfg_.rc_buf_initial_sz; + frame_number_ = 0; + first_drop_ = 0; + bits_total_ = 0; + duration_ = 0.0; + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video, + ::libvpx_test::Encoder *encoder) { + const vpx_rational_t tb = video->timebase(); + timebase_ = static_cast<double>(tb.num) / tb.den; + duration_ = 0; + } + + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) { + // Time since last timestamp = duration. + vpx_codec_pts_t duration = pkt->data.frame.pts - last_pts_; + + // TODO(jimbankoski): Remove these lines when the issue: + // http://code.google.com/p/webm/issues/detail?id=496 is fixed. + // For now the codec assumes buffer starts at starting buffer rate + // plus one frame's time. + if (last_pts_ == 0) + duration = 1; + + // Add to the buffer the bits we'd expect from a constant bitrate server. + bits_in_buffer_model_ += duration * timebase_ * cfg_.rc_target_bitrate + * 1000; + + /* Test the buffer model here before subtracting the frame. Do so because + * the way the leaky bucket model works in libvpx is to allow the buffer to + * empty - and then stop showing frames until we've got enough bits to + * show one. */ + ASSERT_GE(bits_in_buffer_model_, 0) << "Buffer Underrun at frame " + << pkt->data.frame.pts; + + const int frame_size_in_bits = pkt->data.frame.sz * 8; + + // Subtract from the buffer the bits associated with a played back frame. + bits_in_buffer_model_ -= frame_size_in_bits; + + // Update the running total of bits for end of test datarate checks. + bits_total_ += frame_size_in_bits ; + + // If first drop not set and we have a drop set it to this time. + if (!first_drop_ && duration > 1) + first_drop_ = last_pts_ + 1; + + // Update the most recent pts. + last_pts_ = pkt->data.frame.pts; + + // We update this so that we can calculate the datarate minus the last + // frame encoded in the file. + bits_in_last_frame_ = frame_size_in_bits; + + ++frame_number_; + } + + virtual void EndPassHook(void) { + if (bits_total_) { + const double file_size_in_kb = bits_total_ / 1000; /* bits per kilobit */ + + duration_ = (last_pts_ + 1) * timebase_; + + // Effective file datarate includes the time spent prebuffering. + effective_datarate_ = (bits_total_ - bits_in_last_frame_) / 1000.0 + / (cfg_.rc_buf_initial_sz / 1000.0 + duration_); + + file_datarate_ = file_size_in_kb / duration_; + } + } + + vpx_codec_pts_t last_pts_; + int bits_in_buffer_model_; + double timebase_; + int frame_number_; + vpx_codec_pts_t first_drop_; + int64_t bits_total_; + double duration_; + double file_datarate_; + double effective_datarate_; + int bits_in_last_frame_; +}; + +TEST_P(DatarateTest, BasicBufferModel) { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_dropframe_thresh = 1; + cfg_.rc_max_quantizer = 56; + cfg_.rc_end_usage = VPX_CBR; + // 2 pass cbr datarate control has a bug hidden by the small # of + // frames selected in this encode. The problem is that even if the buffer is + // negative we produce a keyframe on a cutscene. Ignoring datarate + // constraints + // TODO(jimbankoski): ( Fix when issue + // http://code.google.com/p/webm/issues/detail?id=495 is addressed. ) + ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, 140); + + for (int i = 70; i < 700; i += 200) { + cfg_.rc_target_bitrate = i; + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_) + << " The datarate for the file exceeds the target!"; + + ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.3) + << " The datarate for the file missed the target!"; + } +} + +TEST_P(DatarateTest, ChangingDropFrameThresh) { + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_max_quantizer = 36; + cfg_.rc_end_usage = VPX_CBR; + cfg_.rc_target_bitrate = 200; + cfg_.kf_mode = VPX_KF_DISABLED; + + const int frame_count = 40; + ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, frame_count); + + // Here we check that the first dropped frame gets earlier and earlier + // as the drop frame threshold is increased. + + const int kDropFrameThreshTestStep = 30; + vpx_codec_pts_t last_drop = frame_count; + for (int i = 1; i < 91; i += kDropFrameThreshTestStep) { + cfg_.rc_dropframe_thresh = i; + ResetModel(); + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_LE(first_drop_, last_drop) + << " The first dropped frame for drop_thresh " << i + << " > first dropped frame for drop_thresh " + << i - kDropFrameThreshTestStep; + last_drop = first_drop_; + } +} + +INSTANTIATE_TEST_CASE_P(AllModes, DatarateTest, ALL_TEST_MODES); +} // namespace
diff --git a/test/decode_test_driver.cc b/test/decode_test_driver.cc new file mode 100644 index 0000000..3610f02 --- /dev/null +++ b/test/decode_test_driver.cc
@@ -0,0 +1,46 @@ +/* + * 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 "test/decode_test_driver.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "test/video_source.h" + +namespace libvpx_test { +#if CONFIG_VP8_DECODER +void Decoder::DecodeFrame(const uint8_t *cxdata, int size) { + if (!decoder_.priv) { + const vpx_codec_err_t res_init = vpx_codec_dec_init(&decoder_, + &vpx_codec_vp8_dx_algo, + &cfg_, 0); + ASSERT_EQ(VPX_CODEC_OK, res_init) << DecodeError(); + } + + const vpx_codec_err_t res_dec = vpx_codec_decode(&decoder_, + cxdata, size, NULL, 0); + ASSERT_EQ(VPX_CODEC_OK, res_dec) << DecodeError(); +} + +void DecoderTest::RunLoop(CompressedVideoSource *video) { + vpx_codec_dec_cfg_t dec_cfg = {0}; + Decoder decoder(dec_cfg, 0); + + // Decode frames. + for (video->Begin(); video->cxdata(); video->Next()) { + decoder.DecodeFrame(video->cxdata(), video->frame_size()); + + DxDataIterator dec_iter = decoder.GetDxData(); + const vpx_image_t *img = NULL; + + // Get decompressed data + while ((img = dec_iter.Next())) + DecompressedFrameHook(*img, video->frame_number()); + } +} +#endif +} // namespace libvpx_test
diff --git a/test/decode_test_driver.h b/test/decode_test_driver.h new file mode 100644 index 0000000..6408bee --- /dev/null +++ b/test/decode_test_driver.h
@@ -0,0 +1,97 @@ +/* + * 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 TEST_DECODE_TEST_DRIVER_H_ +#define 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" +#include "vpx/vp8dx.h" + +namespace libvpx_test { + +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. +// +// TODO: similar to Encoder class, the exact services should be +// added as more tests are added. +class Decoder { + public: + Decoder(vpx_codec_dec_cfg_t cfg, unsigned long deadline) + : cfg_(cfg), deadline_(deadline) { + memset(&decoder_, 0, sizeof(decoder_)); + } + + ~Decoder() { + vpx_codec_destroy(&decoder_); + } + + void DecodeFrame(const uint8_t *cxdata, int size); + + DxDataIterator GetDxData() { + return DxDataIterator(&decoder_); + } + + void set_deadline(unsigned long deadline) { + deadline_ = deadline; + } + + void Control(int ctrl_id, int arg) { + const vpx_codec_err_t res = vpx_codec_control_(&decoder_, ctrl_id, arg); + ASSERT_EQ(VPX_CODEC_OK, res) << DecodeError(); + } + + protected: + const char *DecodeError() { + const char *detail = vpx_codec_error_detail(&decoder_); + return detail ? detail : vpx_codec_error(&decoder_); + } + + vpx_codec_ctx_t decoder_; + vpx_codec_dec_cfg_t cfg_; + unsigned int deadline_; +}; + +// Common test functionality for all Decoder tests. +class DecoderTest { + public: + // Main loop. + virtual void RunLoop(CompressedVideoSource *video); + + // Hook to be called on every decompressed frame. + virtual void DecompressedFrameHook(const vpx_image_t& img, + const unsigned int frame_number) {} + + protected: + DecoderTest() {} + + virtual ~DecoderTest() {} +}; + +} // namespace libvpx_test + +#endif // TEST_DECODE_TEST_DRIVER_H_
diff --git a/test/encode_test_driver.cc b/test/encode_test_driver.cc new file mode 100644 index 0000000..ebb3959 --- /dev/null +++ b/test/encode_test_driver.cc
@@ -0,0 +1,204 @@ +/* + * 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 "vpx_config.h" +#include "test/encode_test_driver.h" +#if CONFIG_VP8_DECODER +#include "test/decode_test_driver.h" +#endif +#include "test/video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +namespace libvpx_test { +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 first frame initialization + if (!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_, &vpx_codec_vp8_cx_algo, &cfg_, + init_flags_); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); + } + + // 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 + res = vpx_codec_encode(&encoder_, + video.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_); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); +} + +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->d_w == img2->d_w) && + (img1->d_h == img2->d_h); + + 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::RunLoop(VideoSource *video) { +#if CONFIG_VP8_DECODER + vpx_codec_dec_cfg_t dec_cfg = {0}; +#endif + + stats_.Reset(); + + 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); + Encoder encoder(cfg_, deadline_, init_flags_, &stats_); +#if CONFIG_VP8_DECODER + Decoder decoder(dec_cfg, 0); + bool has_cxdata = false; +#endif + bool again; + for (again = true, video->Begin(); again; video->Next()) { + again = video->img() != NULL; + + PreEncodeFrameHook(video); + PreEncodeFrameHook(video, &encoder); + encoder.EncodeFrame(video, frame_flags_); + + CxDataIterator iter = encoder.GetCxData(); + + while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) { + again = true; + + switch (pkt->kind) { + case VPX_CODEC_CX_FRAME_PKT: +#if CONFIG_VP8_DECODER + has_cxdata = true; + decoder.DecodeFrame((const uint8_t*)pkt->data.frame.buf, + pkt->data.frame.sz); +#endif + 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; + + default: + break; + } + } + +#if CONFIG_VP8_DECODER + if (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); + ASSERT_TRUE(res)<< "Encoder/Decoder mismatch found."; + } + } +#endif + if (!Continue()) + break; + } + + EndPassHook(); + + if (!Continue()) + break; + } +} +} // namespace libvpx_test
diff --git a/test/encode_test_driver.h b/test/encode_test_driver.h new file mode 100644 index 0000000..0141fa9 --- /dev/null +++ b/test/encode_test_driver.h
@@ -0,0 +1,197 @@ +/* + * 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 TEST_ENCODE_TEST_DRIVER_H_ +#define TEST_ENCODE_TEST_DRIVER_H_ +#include <string> +#include <vector> +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "vpx/vpx_encoder.h" +#include "vpx/vp8cx.h" + +namespace libvpx_test { + +class VideoSource; + +enum TestMode { + kRealTime, + kOnePassGood, + kOnePassBest, + kTwoPassGood, + kTwoPassBest +}; +#define ALL_TEST_MODES ::testing::Values(::libvpx_test::kRealTime, \ + ::libvpx_test::kOnePassGood, \ + ::libvpx_test::kOnePassBest, \ + ::libvpx_test::kTwoPassGood, \ + ::libvpx_test::kTwoPassBest) + +#define ONE_PASS_TEST_MODES ::testing::Values(::libvpx_test::kRealTime, \ + ::libvpx_test::kOnePassGood, \ + ::libvpx_test::kOnePassBest) + + +// Provides an object to handle the libvpx get_cx_data() iteration pattern +class CxDataIterator { + public: + explicit CxDataIterator(vpx_codec_ctx_t *encoder) + : encoder_(encoder), iter_(NULL) {} + + const vpx_codec_cx_pkt_t *Next() { + return vpx_codec_get_cx_data(encoder_, &iter_); + } + + private: + vpx_codec_ctx_t *encoder_; + vpx_codec_iter_t iter_; +}; + +// Implements an in-memory store for libvpx twopass statistics +class TwopassStatsStore { + public: + void Append(const vpx_codec_cx_pkt_t &pkt) { + buffer_.append(reinterpret_cast<char *>(pkt.data.twopass_stats.buf), + pkt.data.twopass_stats.sz); + } + + vpx_fixed_buf_t buf() { + const vpx_fixed_buf_t buf = { &buffer_[0], buffer_.size() }; + return buf; + } + + void Reset() { + buffer_.clear(); + } + + protected: + std::string buffer_; +}; + + +// Provides a simplified interface to manage one video encoding pass, given +// a configuration and video source. +// +// TODO(jkoleszar): The exact services it provides and the appropriate +// level of abstraction will be fleshed out as more tests are written. +class Encoder { + public: + Encoder(vpx_codec_enc_cfg_t cfg, unsigned long deadline, + const unsigned long init_flags, TwopassStatsStore *stats) + : cfg_(cfg), deadline_(deadline), init_flags_(init_flags), stats_(stats) { + memset(&encoder_, 0, sizeof(encoder_)); + } + + ~Encoder() { + vpx_codec_destroy(&encoder_); + } + + CxDataIterator GetCxData() { + return CxDataIterator(&encoder_); + } + + const vpx_image_t *GetPreviewFrame() { + return vpx_codec_get_preview_frame(&encoder_); + } + // This is a thin wrapper around vpx_codec_encode(), so refer to + // vpx_encoder.h for its semantics. + void EncodeFrame(VideoSource *video, const unsigned long frame_flags); + + // Convenience wrapper for EncodeFrame() + void EncodeFrame(VideoSource *video) { + EncodeFrame(video, 0); + } + + void Control(int ctrl_id, int arg) { + const vpx_codec_err_t res = vpx_codec_control_(&encoder_, ctrl_id, arg); + ASSERT_EQ(VPX_CODEC_OK, res) << EncoderError(); + } + + void set_deadline(unsigned long deadline) { + deadline_ = deadline; + } + + protected: + const char *EncoderError() { + const char *detail = vpx_codec_error_detail(&encoder_); + return detail ? detail : vpx_codec_error(&encoder_); + } + + // Encode an image + void EncodeFrameInternal(const VideoSource &video, + const unsigned long frame_flags); + + // Flush the encoder on EOS + void Flush(); + + vpx_codec_ctx_t encoder_; + vpx_codec_enc_cfg_t cfg_; + unsigned long deadline_; + unsigned long init_flags_; + TwopassStatsStore *stats_; +}; + +// Common test functionality for all Encoder tests. +// +// This class is a mixin which provides the main loop common to all +// encoder tests. It provides hooks which can be overridden by subclasses +// to implement each test's specific behavior, while centralizing the bulk +// of the boilerplate. Note that it doesn't inherit the gtest testing +// classes directly, so that tests can be parameterized differently. +class EncoderTest { + protected: + EncoderTest() : abort_(false), init_flags_(0), frame_flags_(0), + last_pts_(0) {} + + virtual ~EncoderTest() {} + + // Initialize the cfg_ member with the default configuration. + void InitializeConfig() { + const vpx_codec_err_t res = vpx_codec_enc_config_default( + &vpx_codec_vp8_cx_algo, &cfg_, 0); + ASSERT_EQ(VPX_CODEC_OK, res); + } + + // Map the TestMode enum to the deadline_ and passes_ variables. + void SetMode(TestMode mode); + + // Main loop. + virtual void RunLoop(VideoSource *video); + + // Hook to be called at the beginning of a pass. + virtual void BeginPassHook(unsigned int pass) {} + + // Hook to be called at the end of a pass. + virtual void EndPassHook() {} + + // Hook to be called before encoding a frame. + virtual void PreEncodeFrameHook(VideoSource *video) {} + virtual void PreEncodeFrameHook(VideoSource *video, Encoder *encoder) {} + + // Hook to be called on every compressed data packet. + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {} + + // Hook to be called on every PSNR packet. + virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {} + + // Hook to determine whether the encode loop should continue. + virtual bool Continue() const { return !abort_; } + + bool abort_; + vpx_codec_enc_cfg_t cfg_; + unsigned int passes_; + unsigned long deadline_; + TwopassStatsStore stats_; + unsigned long init_flags_; + unsigned long frame_flags_; + vpx_codec_pts_t last_pts_; +}; + +} // namespace libvpx_test + +#endif // TEST_ENCODE_TEST_DRIVER_H_
diff --git a/test/error_resilience_test.cc b/test/error_resilience_test.cc new file mode 100644 index 0000000..25c6731 --- /dev/null +++ b/test/error_resilience_test.cc
@@ -0,0 +1,90 @@ +/* + 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/encode_test_driver.h" +#include "test/i420_video_source.h" + +namespace { + +class ErrorResilienceTest : public libvpx_test::EncoderTest, + public ::testing::TestWithParam<int> { + protected: + ErrorResilienceTest() { + psnr_ = 0.0; + nframes_ = 0; + encoding_mode_ = static_cast<libvpx_test::TestMode>(GetParam()); + } + virtual ~ErrorResilienceTest() {} + + virtual void SetUp() { + InitializeConfig(); + SetMode(encoding_mode_); + } + + virtual void BeginPassHook(unsigned int /*pass*/) { + psnr_ = 0.0; + nframes_ = 0; + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) { + psnr_ += pkt->data.psnr.psnr[0]; + nframes_++; + } + + double GetAveragePsnr() const { + if (nframes_) + return psnr_ / nframes_; + return 0.0; + } + + private: + double psnr_; + unsigned int nframes_; + libvpx_test::TestMode encoding_mode_; +}; + +TEST_P(ErrorResilienceTest, OnVersusOff) { + const vpx_rational timebase = { 33333333, 1000000000 }; + cfg_.g_timebase = timebase; + cfg_.rc_target_bitrate = 2000; + cfg_.g_lag_in_frames = 25; + + init_flags_ = VPX_CODEC_USE_PSNR; + + libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + timebase.den, timebase.num, 0, 30); + + // Error resilient mode OFF. + cfg_.g_error_resilient = 0; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + const double psnr_resilience_off = GetAveragePsnr(); + EXPECT_GT(psnr_resilience_off, 25.0); + + // Error resilient mode ON. + cfg_.g_error_resilient = 1; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + const double psnr_resilience_on = GetAveragePsnr(); + EXPECT_GT(psnr_resilience_on, 25.0); + + // Test that turning on error resilient mode hurts by 10% at most. + if (psnr_resilience_off > 0.0) { + const double psnr_ratio = psnr_resilience_on / psnr_resilience_off; + EXPECT_GE(psnr_ratio, 0.9); + EXPECT_LE(psnr_ratio, 1.1); + } +} + +INSTANTIATE_TEST_CASE_P(OnOffTest, ErrorResilienceTest, + ONE_PASS_TEST_MODES); +} // namespace
diff --git a/test/i420_video_source.h b/test/i420_video_source.h new file mode 100644 index 0000000..219bd33 --- /dev/null +++ b/test/i420_video_source.h
@@ -0,0 +1,117 @@ +/* + * 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 TEST_I420_VIDEO_SOURCE_H_ +#define TEST_I420_VIDEO_SOURCE_H_ +#include <cstdio> +#include <cstdlib> + +#include "test/video_source.h" + +namespace libvpx_test { + +// This class extends VideoSource to allow parsing of raw yv12 +// so that we can do actual file encodes. +class I420VideoSource : public VideoSource { + public: + I420VideoSource(const std::string &file_name, + unsigned int width, unsigned int height, + int rate_numerator, int rate_denominator, + unsigned int start, int limit) + : file_name_(file_name), + input_file_(NULL), + img_(NULL), + start_(start), + limit_(limit), + frame_(0), + width_(0), + height_(0), + framerate_numerator_(rate_numerator), + framerate_denominator_(rate_denominator) { + + // This initializes raw_sz_, width_, height_ and allocates an img. + SetSize(width, height); + } + + virtual ~I420VideoSource() { + vpx_img_free(img_); + if (input_file_) + fclose(input_file_); + } + + virtual void Begin() { + if (input_file_) + fclose(input_file_); + input_file_ = OpenTestDataFile(file_name_); + ASSERT_TRUE(input_file_) << "Input file open failed. Filename: " + << file_name_; + if (start_) { + fseek(input_file_, raw_sz_ * start_, SEEK_SET); + } + + frame_ = start_; + FillFrame(); + } + + virtual void Next() { + ++frame_; + FillFrame(); + } + + virtual vpx_image_t *img() const { return (frame_ < limit_) ? img_ : NULL; } + + // Models a stream where Timebase = 1/FPS, so pts == frame. + virtual vpx_codec_pts_t pts() const { return frame_; } + + virtual unsigned long duration() const { return 1; } + + virtual vpx_rational_t timebase() const { + const vpx_rational_t t = { framerate_denominator_, framerate_numerator_ }; + return t; + } + + virtual unsigned int frame() const { return frame_; } + + virtual unsigned int limit() const { return limit_; } + + void SetSize(unsigned int width, unsigned int height) { + if (width != width_ || height != height_) { + vpx_img_free(img_); + img_ = vpx_img_alloc(NULL, VPX_IMG_FMT_VPXI420, width, height, 1); + ASSERT_TRUE(img_ != NULL); + width_ = width; + height_ = height; + raw_sz_ = width * height * 3 / 2; + } + } + + virtual void FillFrame() { + // Read a frame from input_file. + if (fread(img_->img_data, raw_sz_, 1, input_file_) == 0) { + limit_ = frame_; + } + } + + protected: + std::string file_name_; + FILE *input_file_; + vpx_image_t *img_; + size_t raw_sz_; + unsigned int start_; + unsigned int limit_; + unsigned int frame_; + unsigned int width_; + unsigned int height_; + unsigned int framerate_numerator_; + unsigned int framerate_denominator_; +}; + +} // namespace libvpx_test + +#endif // TEST_I420_VIDEO_SOURCE_H_
diff --git a/test/idctllm_test.cc b/test/idctllm_test.cc new file mode 100644 index 0000000..dd42e22 --- /dev/null +++ b/test/idctllm_test.cc
@@ -0,0 +1,125 @@ +/* + * 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. + */ + + +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +} +#include "third_party/googletest/src/include/gtest/gtest.h" + +typedef void (*idct_fn_t)(short *input, unsigned char *pred_ptr, + int pred_stride, unsigned char *dst_ptr, + int dst_stride); +namespace { +class IDCTTest : public ::testing::TestWithParam<idct_fn_t> +{ + protected: + virtual void SetUp() + { + int i; + + UUT = GetParam(); + memset(input, 0, sizeof(input)); + /* Set up guard blocks */ + for(i=0; i<256; i++) + output[i] = ((i&0xF)<4&&(i<64))?0:-1; + } + + idct_fn_t UUT; + short input[16]; + unsigned char output[256]; + unsigned char predict[256]; +}; + +TEST_P(IDCTTest, TestGuardBlocks) +{ + int i; + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << i; + else + EXPECT_EQ(255, output[i]); +} + +TEST_P(IDCTTest, TestAllZeros) +{ + int i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAllOnes) +{ + int i; + + input[0] = 4; + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAddOne) +{ + int i; + + for(i=0; i<256; i++) + predict[i] = i; + + input[0] = 4; + UUT(input, predict, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(i+1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestWithData) +{ + int i; + + for(i=0; i<16; i++) + input[i] = i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) > 3 || i>63) + EXPECT_EQ(255, output[i]) << "i==" << i; + else if(i == 0) + EXPECT_EQ(11, output[i]) << "i==" << i; + else if(i == 34) + EXPECT_EQ(1, output[i]) << "i==" << i; + else if(i == 2 || i == 17 || i == 32) + EXPECT_EQ(3, output[i]) << "i==" << i; + else + EXPECT_EQ(0, output[i]) << "i==" << i; +} + +INSTANTIATE_TEST_CASE_P(C, IDCTTest, + ::testing::Values(vp8_short_idct4x4llm_c)); +#if HAVE_MMX +INSTANTIATE_TEST_CASE_P(MMX, IDCTTest, + ::testing::Values(vp8_short_idct4x4llm_mmx)); +#endif +}
diff --git a/test/intrapred_test.cc b/test/intrapred_test.cc new file mode 100644 index 0000000..d2e0d61 --- /dev/null +++ b/test/intrapred_test.cc
@@ -0,0 +1,354 @@ +/* + * 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 <string.h> +#include "test/acm_random.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +#include "vp8/common/blockd.h" +#include "vpx_mem/vpx_mem.h" +} + +namespace { + +using libvpx_test::ACMRandom; + +class IntraPredBase { + protected: + void SetupMacroblock(uint8_t *data, int block_size, int stride, + int num_planes) { + memset(&mb_, 0, sizeof(mb_)); + memset(&mi_, 0, sizeof(mi_)); + mb_.up_available = 1; + mb_.left_available = 1; + mb_.mode_info_context = &mi_; + stride_ = stride; + block_size_ = block_size; + num_planes_ = num_planes; + for (int p = 0; p < num_planes; p++) + data_ptr_[p] = data + stride * (block_size + 1) * p + + stride + block_size; + } + + void FillRandom() { + // Fill edges with random data + ACMRandom rnd(ACMRandom::DeterministicSeed()); + for (int p = 0; p < num_planes_; p++) { + for (int x = -1 ; x <= block_size_; x++) + data_ptr_[p][x - stride_] = rnd.Rand8(); + for (int y = 0; y < block_size_; y++) + data_ptr_[p][y * stride_ - 1] = rnd.Rand8(); + } + } + + virtual void Predict(MB_PREDICTION_MODE mode) = 0; + + void SetLeftUnavailable() { + mb_.left_available = 0; + for (int p = 0; p < num_planes_; p++) + for (int i = -1; i < block_size_; ++i) + data_ptr_[p][stride_ * i - 1] = 129; + } + + void SetTopUnavailable() { + mb_.up_available = 0; + for (int p = 0; p < num_planes_; p++) + memset(&data_ptr_[p][-1 - stride_], 127, block_size_ + 2); + } + + void SetTopLeftUnavailable() { + SetLeftUnavailable(); + SetTopUnavailable(); + } + + int BlockSizeLog2Min1() const { + switch (block_size_) { + case 16: + return 3; + case 8: + return 2; + default: + return 0; + } + } + + // check DC prediction output against a reference + void CheckDCPrediction() const { + for (int p = 0; p < num_planes_; p++) { + // calculate expected DC + int expected; + if (mb_.up_available || mb_.left_available) { + int sum = 0, shift = BlockSizeLog2Min1() + mb_.up_available + + mb_.left_available; + if (mb_.up_available) + for (int x = 0; x < block_size_; x++) + sum += data_ptr_[p][x - stride_]; + if (mb_.left_available) + for (int y = 0; y < block_size_; y++) + sum += data_ptr_[p][y * stride_ - 1]; + expected = (sum + (1 << (shift - 1))) >> shift; + } else + expected = 0x80; + + // check that all subsequent lines are equal to the first + for (int y = 1; y < block_size_; ++y) + ASSERT_EQ(0, memcmp(data_ptr_[p], &data_ptr_[p][y * stride_], + block_size_)); + // within the first line, ensure that each pixel has the same value + for (int x = 1; x < block_size_; ++x) + ASSERT_EQ(data_ptr_[p][0], data_ptr_[p][x]); + // now ensure that that pixel has the expected (DC) value + ASSERT_EQ(expected, data_ptr_[p][0]); + } + } + + // check V prediction output against a reference + void CheckVPrediction() const { + // check that all lines equal the top border + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + ASSERT_EQ(0, memcmp(&data_ptr_[p][-stride_], + &data_ptr_[p][y * stride_], block_size_)); + } + + // check H prediction output against a reference + void CheckHPrediction() const { + // for each line, ensure that each pixel is equal to the left border + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + for (int x = 0; x < block_size_; x++) + ASSERT_EQ(data_ptr_[p][-1 + y * stride_], + data_ptr_[p][x + y * stride_]); + } + + static int ClipByte(int value) { + if (value > 255) + return 255; + else if (value < 0) + return 0; + return value; + } + + // check TM prediction output against a reference + void CheckTMPrediction() const { + for (int p = 0; p < num_planes_; p++) + for (int y = 0; y < block_size_; y++) + for (int x = 0; x < block_size_; x++) { + const int expected = ClipByte(data_ptr_[p][x - stride_] + + data_ptr_[p][stride_ * y - 1] + - data_ptr_[p][-1 - stride_]); + ASSERT_EQ(expected, data_ptr_[p][y * stride_ + x]); + } + } + + // Actual test + void RunTest() { + { + SCOPED_TRACE("DC_PRED"); + FillRandom(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED LEFT"); + FillRandom(); + SetLeftUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED TOP"); + FillRandom(); + SetTopUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("DC_PRED TOP_LEFT"); + FillRandom(); + SetTopLeftUnavailable(); + Predict(DC_PRED); + CheckDCPrediction(); + } + { + SCOPED_TRACE("H_PRED"); + FillRandom(); + Predict(H_PRED); + CheckHPrediction(); + } + { + SCOPED_TRACE("V_PRED"); + FillRandom(); + Predict(V_PRED); + CheckVPrediction(); + } + { + SCOPED_TRACE("TM_PRED"); + FillRandom(); + Predict(TM_PRED); + CheckTMPrediction(); + } + } + + MACROBLOCKD mb_; + MODE_INFO mi_; + uint8_t *data_ptr_[2]; // in the case of Y, only [0] is used + int stride_; + int block_size_; + int num_planes_; +}; + +typedef void (*intra_pred_y_fn_t)(MACROBLOCKD *x, + uint8_t *yabove_row, + uint8_t *yleft, + int left_stride, + uint8_t *ypred_ptr, + int y_stride); + +class IntraPredYTest : public ::testing::TestWithParam<intra_pred_y_fn_t>, + protected IntraPredBase { + public: + static void SetUpTestCase() { + data_array_ = reinterpret_cast<uint8_t*>( + vpx_memalign(kDataAlignment, kDataBufferSize)); + } + + static void TearDownTestCase() { + vpx_free(data_array_); + data_array_ = NULL; + } + + protected: + static const int kBlockSize = 16; + static const int kDataAlignment = 16; + static const int kStride = kBlockSize * 3; + // We use 48 so that the data pointer of the first pixel in each row of + // each macroblock is 16-byte aligned, and this gives us access to the + // top-left and top-right corner pixels belonging to the top-left/right + // macroblocks. + // We use 17 lines so we have one line above us for top-prediction. + static const int kDataBufferSize = kStride * (kBlockSize + 1); + + virtual void SetUp() { + pred_fn_ = GetParam(); + SetupMacroblock(data_array_, kBlockSize, kStride, 1); + } + + virtual void Predict(MB_PREDICTION_MODE mode) { + mb_.mode_info_context->mbmi.mode = mode; + pred_fn_(&mb_, data_ptr_[0] - kStride, data_ptr_[0] - 1, kStride, + data_ptr_[0], kStride); + } + + intra_pred_y_fn_t pred_fn_; + static uint8_t* data_array_; +}; + +uint8_t* IntraPredYTest::data_array_ = NULL; + +TEST_P(IntraPredYTest, IntraPredTests) { + RunTest(); +} + +INSTANTIATE_TEST_CASE_P(C, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_c)); +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_sse2)); +#endif +#if HAVE_SSSE3 +INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredYTest, + ::testing::Values( + vp8_build_intra_predictors_mby_s_ssse3)); +#endif + +typedef void (*intra_pred_uv_fn_t)(MACROBLOCKD *x, + uint8_t *uabove_row, + uint8_t *vabove_row, + uint8_t *uleft, + uint8_t *vleft, + int left_stride, + uint8_t *upred_ptr, + uint8_t *vpred_ptr, + int pred_stride); + +class IntraPredUVTest : public ::testing::TestWithParam<intra_pred_uv_fn_t>, + protected IntraPredBase { + public: + static void SetUpTestCase() { + data_array_ = reinterpret_cast<uint8_t*>( + vpx_memalign(kDataAlignment, kDataBufferSize)); + } + + static void TearDownTestCase() { + vpx_free(data_array_); + data_array_ = NULL; + } + + protected: + static const int kBlockSize = 8; + static const int kDataAlignment = 8; + static const int kStride = kBlockSize * 3; + // We use 24 so that the data pointer of the first pixel in each row of + // each macroblock is 8-byte aligned, and this gives us access to the + // top-left and top-right corner pixels belonging to the top-left/right + // macroblocks. + // We use 9 lines so we have one line above us for top-prediction. + // [0] = U, [1] = V + static const int kDataBufferSize = 2 * kStride * (kBlockSize + 1); + + virtual void SetUp() { + pred_fn_ = GetParam(); + SetupMacroblock(data_array_, kBlockSize, kStride, 2); + } + + virtual void Predict(MB_PREDICTION_MODE mode) { + mb_.mode_info_context->mbmi.uv_mode = mode; + pred_fn_(&mb_, data_ptr_[0] - kStride, data_ptr_[1] - kStride, + data_ptr_[0] - 1, data_ptr_[1] - 1, kStride, + data_ptr_[0], data_ptr_[1], kStride); + } + + intra_pred_uv_fn_t pred_fn_; + // We use 24 so that the data pointer of the first pixel in each row of + // each macroblock is 8-byte aligned, and this gives us access to the + // top-left and top-right corner pixels belonging to the top-left/right + // macroblocks. + // We use 9 lines so we have one line above us for top-prediction. + // [0] = U, [1] = V + static uint8_t* data_array_; +}; + +uint8_t* IntraPredUVTest::data_array_ = NULL; + +TEST_P(IntraPredUVTest, IntraPredTests) { + RunTest(); +} + +INSTANTIATE_TEST_CASE_P(C, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_c)); +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_sse2)); +#endif +#if HAVE_SSSE3 +INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredUVTest, + ::testing::Values( + vp8_build_intra_predictors_mbuv_s_ssse3)); +#endif + +} // namespace
diff --git a/test/ivf_video_source.h b/test/ivf_video_source.h new file mode 100644 index 0000000..48c3a7d --- /dev/null +++ b/test/ivf_video_source.h
@@ -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. + */ +#ifndef TEST_IVF_VIDEO_SOURCE_H_ +#define TEST_IVF_VIDEO_SOURCE_H_ +#include <cstdio> +#include <cstdlib> +#include <new> +#include <string> +#include "test/video_source.h" + +namespace libvpx_test { +const unsigned int kCodeBufferSize = 256 * 1024; +const unsigned int kIvfFileHdrSize = 32; +const unsigned int kIvfFrameHdrSize = 12; + +static unsigned int MemGetLe32(const uint8_t *mem) { + return (mem[3] << 24) | (mem[2] << 16) | (mem[1] << 8) | (mem[0]); +} + +// This class extends VideoSource to allow parsing of ivf files, +// so that we can do actual file decodes. +class IVFVideoSource : public CompressedVideoSource { + public: + IVFVideoSource(const std::string &file_name) + : file_name_(file_name), + input_file_(NULL), + compressed_frame_buf_(NULL), + frame_sz_(0), + frame_(0), + end_of_file_(false) { + } + + virtual ~IVFVideoSource() { + delete[] compressed_frame_buf_; + + if (input_file_) + fclose(input_file_); + } + + virtual void Init() { + // Allocate a buffer for read in the compressed video frame. + compressed_frame_buf_ = new uint8_t[libvpx_test::kCodeBufferSize]; + ASSERT_TRUE(compressed_frame_buf_) << "Allocate frame buffer failed"; + } + + virtual void Begin() { + input_file_ = OpenTestDataFile(file_name_); + ASSERT_TRUE(input_file_) << "Input file open failed. Filename: " + << file_name_; + + // Read file header + uint8_t file_hdr[kIvfFileHdrSize]; + ASSERT_EQ(kIvfFileHdrSize, fread(file_hdr, 1, kIvfFileHdrSize, input_file_)) + << "File header read failed."; + // Check file header + ASSERT_TRUE(file_hdr[0] == 'D' && file_hdr[1] == 'K' && file_hdr[2] == 'I' + && file_hdr[3] == 'F') << "Input is not an IVF file."; + + FillFrame(); + } + + virtual void Next() { + ++frame_; + FillFrame(); + } + + void FillFrame() { + uint8_t frame_hdr[kIvfFrameHdrSize]; + // Check frame header and read a frame from input_file. + if (fread(frame_hdr, 1, kIvfFrameHdrSize, input_file_) + != kIvfFrameHdrSize) { + end_of_file_ = true; + } else { + end_of_file_ = false; + + frame_sz_ = MemGetLe32(frame_hdr); + ASSERT_LE(frame_sz_, kCodeBufferSize) + << "Frame is too big for allocated code buffer"; + ASSERT_EQ(frame_sz_, + fread(compressed_frame_buf_, 1, frame_sz_, input_file_)) + << "Failed to read complete frame"; + } + } + + virtual const uint8_t *cxdata() const { + return end_of_file_ ? NULL : compressed_frame_buf_; + } + virtual const unsigned int frame_size() const { return frame_sz_; } + virtual const unsigned int frame_number() const { return frame_; } + + protected: + std::string file_name_; + FILE *input_file_; + uint8_t *compressed_frame_buf_; + unsigned int frame_sz_; + unsigned int frame_; + bool end_of_file_; +}; + +} // namespace libvpx_test + +#endif // TEST_IVF_VIDEO_SOURCE_H_
diff --git a/test/keyframe_test.cc b/test/keyframe_test.cc new file mode 100644 index 0000000..d0c81df --- /dev/null +++ b/test/keyframe_test.cc
@@ -0,0 +1,145 @@ +/* + * 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 "test/encode_test_driver.h" +#include "test/i420_video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +namespace { + +class KeyframeTest : public ::libvpx_test::EncoderTest, + public ::testing::TestWithParam<enum libvpx_test::TestMode> { + protected: + virtual void SetUp() { + InitializeConfig(); + SetMode(GetParam()); + kf_count_ = 0; + kf_count_max_ = INT_MAX; + kf_do_force_kf_ = false; + set_cpu_used_ = 0; + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video, + ::libvpx_test::Encoder *encoder) { + if (kf_do_force_kf_) + frame_flags_ = (video->frame() % 3) ? 0 : VPX_EFLAG_FORCE_KF; + if (set_cpu_used_ && video->frame() == 1) + encoder->Control(VP8E_SET_CPUUSED, set_cpu_used_); + } + + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) { + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + kf_pts_list_.push_back(pkt->data.frame.pts); + kf_count_++; + abort_ |= kf_count_ > kf_count_max_; + } + } + + bool kf_do_force_kf_; + int kf_count_; + int kf_count_max_; + std::vector<vpx_codec_pts_t> kf_pts_list_; + int set_cpu_used_; +}; + +TEST_P(KeyframeTest, TestRandomVideoSource) { + // Validate that encoding the RandomVideoSource produces multiple keyframes. + // This validates the results of the TestDisableKeyframes test. + kf_count_max_ = 2; // early exit successful tests. + + ::libvpx_test::RandomVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // In realtime mode - auto placed keyframes are exceedingly rare, don't + // bother with this check if(GetParam() > 0) + if(GetParam() > 0) + EXPECT_GT(kf_count_, 1); +} + +TEST_P(KeyframeTest, TestDisableKeyframes) { + cfg_.kf_mode = VPX_KF_DISABLED; + kf_count_max_ = 1; // early exit failed tests. + + ::libvpx_test::RandomVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + EXPECT_EQ(1, kf_count_); +} + +TEST_P(KeyframeTest, TestForceKeyframe) { + cfg_.kf_mode = VPX_KF_DISABLED; + kf_do_force_kf_ = true; + + ::libvpx_test::DummyVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // verify that every third frame is a keyframe. + for (std::vector<vpx_codec_pts_t>::const_iterator iter = kf_pts_list_.begin(); + iter != kf_pts_list_.end(); ++iter) { + ASSERT_EQ(0, *iter % 3) << "Unexpected keyframe at frame " << *iter; + } +} + +TEST_P(KeyframeTest, TestKeyframeMaxDistance) { + cfg_.kf_max_dist = 25; + + ::libvpx_test::DummyVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // verify that keyframe interval matches kf_max_dist + for (std::vector<vpx_codec_pts_t>::const_iterator iter = kf_pts_list_.begin(); + iter != kf_pts_list_.end(); ++iter) { + ASSERT_EQ(0, *iter % 25) << "Unexpected keyframe at frame " << *iter; + } +} + +TEST_P(KeyframeTest, TestAutoKeyframe) { + cfg_.kf_mode = VPX_KF_AUTO; + kf_do_force_kf_ = false; + + // Force a deterministic speed step in Real Time mode, as the faster modes + // may not produce a keyframe like we expect. This is necessary when running + // on very slow environments (like Valgrind). The step -11 was determined + // experimentally as the fastest mode that still throws the keyframe. + if (deadline_ == VPX_DL_REALTIME) + set_cpu_used_ = -11; + + // This clip has a cut scene every 30 frames -> Frame 0, 30, 60, 90, 120. + // I check only the first 40 frames to make sure there's a keyframe at frame + // 0 and 30. + ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, 40); + + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + // In realtime mode - auto placed keyframes are exceedingly rare, don't + // bother with this check + if(GetParam() > 0) + EXPECT_EQ(2u, kf_pts_list_.size()) << " Not the right number of keyframes "; + + // Verify that keyframes match the file keyframes in the file. + for (std::vector<vpx_codec_pts_t>::const_iterator iter = kf_pts_list_.begin(); + iter != kf_pts_list_.end(); ++iter) { + + if (deadline_ == VPX_DL_REALTIME && *iter > 0) + EXPECT_EQ(0, (*iter - 1) % 30) << "Unexpected keyframe at frame " + << *iter; + else + EXPECT_EQ(0, *iter % 30) << "Unexpected keyframe at frame " << *iter; + } +} + +INSTANTIATE_TEST_CASE_P(AllModes, KeyframeTest, ALL_TEST_MODES); +} // namespace
diff --git a/test/pp_filter_test.cc b/test/pp_filter_test.cc new file mode 100644 index 0000000..af2f3bd --- /dev/null +++ b/test/pp_filter_test.cc
@@ -0,0 +1,106 @@ +/* + * 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" +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +#include "vpx/vpx_integer.h" +#include "vpx_mem/vpx_mem.h" +} + +typedef void (*post_proc_func_t)(unsigned char *src_ptr, + unsigned char *dst_ptr, + int src_pixels_per_line, + int dst_pixels_per_line, + int cols, + unsigned char *flimit, + int size); + +namespace { + +class Vp8PostProcessingFilterTest + : public ::testing::TestWithParam<post_proc_func_t> {}; + +// Test routine for the VP8 post-processing function +// vp8_post_proc_down_and_across_mb_row_c. + +TEST_P(Vp8PostProcessingFilterTest, FilterOutputCheck) { + // Size of the underlying data block that will be filtered. + const int block_width = 16; + const int block_height = 16; + + // 5-tap filter needs 2 padding rows above and below the block in the input. + const int input_width = block_width; + const int input_height = block_height + 4; + const int input_stride = input_width; + const int input_size = input_width * input_height; + + // Filter extends output block by 8 samples at left and right edges. + const int output_width = block_width + 16; + const int output_height = block_height; + const int output_stride = output_width; + const int output_size = output_width * output_height; + + uint8_t *const src_image = + reinterpret_cast<uint8_t*>(vpx_calloc(input_size, 1)); + uint8_t *const dst_image = + reinterpret_cast<uint8_t*>(vpx_calloc(output_size, 1)); + + // Pointers to top-left pixel of block in the input and output images. + uint8_t *const src_image_ptr = src_image + (input_stride << 1); + uint8_t *const dst_image_ptr = dst_image + 8; + uint8_t *const flimits = reinterpret_cast<uint8_t *>(vpx_memalign(16, block_width)); + (void)vpx_memset(flimits, 255, block_width); + + // Initialize pixels in the input: + // block pixels to value 1, + // border pixels to value 10. + (void)vpx_memset(src_image, 10, input_size); + uint8_t *pixel_ptr = src_image_ptr; + for (int i = 0; i < block_height; ++i) { + for (int j = 0; j < block_width; ++j) { + pixel_ptr[j] = 1; + } + pixel_ptr += input_stride; + } + + // Initialize pixels in the output to 99. + (void)vpx_memset(dst_image, 99, output_size); + + GetParam()(src_image_ptr, dst_image_ptr, input_stride, + output_stride, block_width, flimits, 16); + + static const uint8_t expected_data[block_height] = { + 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4 + }; + + pixel_ptr = dst_image_ptr; + for (int i = 0; i < block_height; ++i) { + for (int j = 0; j < block_width; ++j) { + EXPECT_EQ(expected_data[i], pixel_ptr[j]) + << "Vp8PostProcessingFilterTest failed with invalid filter output"; + } + pixel_ptr += output_stride; + } + + vpx_free(src_image); + vpx_free(dst_image); + vpx_free(flimits); +}; + +INSTANTIATE_TEST_CASE_P(C, Vp8PostProcessingFilterTest, + ::testing::Values(vp8_post_proc_down_and_across_mb_row_c)); + +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, Vp8PostProcessingFilterTest, + ::testing::Values(vp8_post_proc_down_and_across_mb_row_sse2)); +#endif + +} // namespace
diff --git a/test/resize_test.cc b/test/resize_test.cc new file mode 100644 index 0000000..c846157 --- /dev/null +++ b/test/resize_test.cc
@@ -0,0 +1,104 @@ +/* + * 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 "test/encode_test_driver.h" +#include "test/video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + +namespace { + +const unsigned int kInitialWidth = 320; +const unsigned int kInitialHeight = 240; + +unsigned int ScaleForFrameNumber(unsigned int frame, unsigned int val) { + if (frame < 10) + return val; + if (frame < 20) + return val / 2; + if (frame < 30) + return val * 2 / 3; + if (frame < 40) + return val / 4; + if (frame < 50) + return val * 7 / 8; + return val; +} + +class ResizingVideoSource : public ::libvpx_test::DummyVideoSource { + public: + ResizingVideoSource() { + SetSize(kInitialWidth, kInitialHeight); + limit_ = 60; + } + + protected: + virtual void Next() { + ++frame_; + SetSize(ScaleForFrameNumber(frame_, kInitialWidth), + ScaleForFrameNumber(frame_, kInitialHeight)); + FillFrame(); + } +}; + +class ResizeTest : public ::libvpx_test::EncoderTest, + public ::testing::TestWithParam<enum libvpx_test::TestMode> { + protected: + struct FrameInfo { + FrameInfo(vpx_codec_pts_t _pts, unsigned int _w, unsigned int _h) + : pts(_pts), w(_w), h(_h) {} + + vpx_codec_pts_t pts; + unsigned int w; + unsigned int h; + }; + + virtual void SetUp() { + InitializeConfig(); + SetMode(GetParam()); + } + + virtual bool Continue() const { + return !HasFatalFailure() && !abort_; + } + + virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) { + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + const unsigned char *buf = + reinterpret_cast<const unsigned char *>(pkt->data.frame.buf); + const unsigned int w = (buf[6] | (buf[7] << 8)) & 0x3fff; + const unsigned int h = (buf[8] | (buf[9] << 8)) & 0x3fff; + + frame_info_list_.push_back(FrameInfo(pkt->data.frame.pts, w, h)); + } + } + + std::vector< FrameInfo > frame_info_list_; +}; + +TEST_P(ResizeTest, TestExternalResizeWorks) { + ResizingVideoSource video; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + + for (std::vector<FrameInfo>::iterator info = frame_info_list_.begin(); + info != frame_info_list_.end(); ++info) { + const vpx_codec_pts_t pts = info->pts; + const unsigned int expected_w = ScaleForFrameNumber(pts, kInitialWidth); + const unsigned int expected_h = ScaleForFrameNumber(pts, kInitialHeight); + + EXPECT_EQ(expected_w, info->w) + << "Frame " << pts << "had unexpected width"; + EXPECT_EQ(expected_h, info->h) + << "Frame " << pts << "had unexpected height"; + } +} + +INSTANTIATE_TEST_CASE_P(OnePass, ResizeTest, ONE_PASS_TEST_MODES); +} // namespace
diff --git a/test/sad_test.cc b/test/sad_test.cc new file mode 100644 index 0000000..2b562e6 --- /dev/null +++ b/test/sad_test.cc
@@ -0,0 +1,250 @@ +/* + * 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 <string.h> +#include <limits.h> +#include <stdio.h> + +extern "C" { +#include "./vpx_config.h" +#include "./vpx_rtcd.h" +#include "vp8/common/blockd.h" +#include "vpx_mem/vpx_mem.h" +} + +#include "test/acm_random.h" +#include "test/util.h" +#include "third_party/googletest/src/include/gtest/gtest.h" + + +typedef unsigned int (*sad_m_by_n_fn_t)(const unsigned char *source_ptr, + int source_stride, + const unsigned char *reference_ptr, + int reference_stride, + unsigned int max_sad); + +using libvpx_test::ACMRandom; + +namespace { +class SADTest : public PARAMS(int, int, sad_m_by_n_fn_t) { + public: + 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; + } + + protected: + static const int kDataAlignment = 16; + static const int kDataBufferSize = 16 * 32; + + virtual void SetUp() { + sad_fn_ = GET_PARAM(2); + height_ = GET_PARAM(1); + width_ = GET_PARAM(0); + source_stride_ = width_ * 2; + reference_stride_ = width_ * 2; + rnd_.Reset(ACMRandom::DeterministicSeed()); + } + + sad_m_by_n_fn_t sad_fn_; + virtual unsigned int SAD(unsigned int max_sad) { + return sad_fn_(source_data_, source_stride_, + reference_data_, reference_stride_, + max_sad); + } + + // Sum of Absolute Differences. Given two blocks, calculate the absolute + // difference between two pixels in the same relative location; accumulate. + unsigned int ReferenceSAD(unsigned int max_sad) { + unsigned int sad = 0; + + for (int h = 0; h < height_; ++h) { + for (int w = 0; w < width_; ++w) { + sad += abs(source_data_[h * source_stride_ + w] + - reference_data_[h * reference_stride_ + w]); + } + if (sad > max_sad) { + break; + } + } + return sad; + } + + void FillConstant(uint8_t *data, int stride, uint8_t fill_constant) { + for (int h = 0; h < height_; ++h) { + for (int w = 0; w < width_; ++w) { + data[h * stride + w] = fill_constant; + } + } + } + + void FillRandom(uint8_t *data, int stride) { + for (int h = 0; h < height_; ++h) { + for (int w = 0; w < width_; ++w) { + data[h * stride + w] = rnd_.Rand8(); + } + } + } + + void CheckSad(unsigned int max_sad) { + unsigned int reference_sad, exp_sad; + + reference_sad = ReferenceSAD(max_sad); + exp_sad = SAD(max_sad); + + if (reference_sad <= max_sad) { + ASSERT_EQ(exp_sad, reference_sad); + } else { + // Alternative implementations are not required to check max_sad + ASSERT_GE(exp_sad, reference_sad); + } + } + + // Handle blocks up to 16x16 with stride up to 32 + int height_, width_; + static uint8_t* source_data_; + int source_stride_; + static uint8_t* reference_data_; + int reference_stride_; + + ACMRandom rnd_; +}; + +uint8_t* SADTest::source_data_ = NULL; +uint8_t* SADTest::reference_data_ = NULL; + +TEST_P(SADTest, MaxRef) { + FillConstant(source_data_, source_stride_, 0); + FillConstant(reference_data_, reference_stride_, 255); + CheckSad(UINT_MAX); +} + +TEST_P(SADTest, MaxSrc) { + FillConstant(source_data_, source_stride_, 255); + FillConstant(reference_data_, reference_stride_, 0); + CheckSad(UINT_MAX); +} + +TEST_P(SADTest, ShortRef) { + int tmp_stride = reference_stride_; + reference_stride_ >>= 1; + FillRandom(source_data_, source_stride_); + FillRandom(reference_data_, reference_stride_); + CheckSad(UINT_MAX); + reference_stride_ = tmp_stride; +} + +TEST_P(SADTest, UnalignedRef) { + // The reference frame, but not the source frame, may be unaligned for + // certain types of searches. + int tmp_stride = reference_stride_; + reference_stride_ -= 1; + FillRandom(source_data_, source_stride_); + FillRandom(reference_data_, reference_stride_); + CheckSad(UINT_MAX); + reference_stride_ = tmp_stride; +} + +TEST_P(SADTest, ShortSrc) { + int tmp_stride = source_stride_; + source_stride_ >>= 1; + FillRandom(source_data_, source_stride_); + FillRandom(reference_data_, reference_stride_); + CheckSad(UINT_MAX); + source_stride_ = tmp_stride; +} + +TEST_P(SADTest, MaxSAD) { + // Verify that, when max_sad is set, the implementation does not return a + // value lower than the reference. + FillConstant(source_data_, source_stride_, 255); + FillConstant(reference_data_, reference_stride_, 0); + CheckSad(128); +} + +using std::tr1::make_tuple; + +const sad_m_by_n_fn_t sad_16x16_c = vp8_sad16x16_c; +const sad_m_by_n_fn_t sad_8x16_c = vp8_sad8x16_c; +const sad_m_by_n_fn_t sad_16x8_c = vp8_sad16x8_c; +const sad_m_by_n_fn_t sad_8x8_c = vp8_sad8x8_c; +const sad_m_by_n_fn_t sad_4x4_c = vp8_sad4x4_c; +INSTANTIATE_TEST_CASE_P(C, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_c), + make_tuple(8, 16, sad_8x16_c), + make_tuple(16, 8, sad_16x8_c), + make_tuple(8, 8, sad_8x8_c), + make_tuple(4, 4, sad_4x4_c))); + +// ARM tests +#if HAVE_MEDIA +const sad_m_by_n_fn_t sad_16x16_armv6 = vp8_sad16x16_armv6; +INSTANTIATE_TEST_CASE_P(MEDIA, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_armv6))); + +#endif +#if HAVE_NEON +const sad_m_by_n_fn_t sad_16x16_neon = vp8_sad16x16_neon; +const sad_m_by_n_fn_t sad_8x16_neon = vp8_sad8x16_neon; +const sad_m_by_n_fn_t sad_16x8_neon = vp8_sad16x8_neon; +const sad_m_by_n_fn_t sad_8x8_neon = vp8_sad8x8_neon; +const sad_m_by_n_fn_t sad_4x4_neon = vp8_sad4x4_neon; +INSTANTIATE_TEST_CASE_P(NEON, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_neon), + make_tuple(8, 16, sad_8x16_neon), + make_tuple(16, 8, sad_16x8_neon), + make_tuple(8, 8, sad_8x8_neon), + make_tuple(4, 4, sad_4x4_neon))); +#endif + +// X86 tests +#if HAVE_MMX +const sad_m_by_n_fn_t sad_16x16_mmx = vp8_sad16x16_mmx; +const sad_m_by_n_fn_t sad_8x16_mmx = vp8_sad8x16_mmx; +const sad_m_by_n_fn_t sad_16x8_mmx = vp8_sad16x8_mmx; +const sad_m_by_n_fn_t sad_8x8_mmx = vp8_sad8x8_mmx; +const sad_m_by_n_fn_t sad_4x4_mmx = vp8_sad4x4_mmx; +INSTANTIATE_TEST_CASE_P(MMX, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_mmx), + make_tuple(8, 16, sad_8x16_mmx), + make_tuple(16, 8, sad_16x8_mmx), + make_tuple(8, 8, sad_8x8_mmx), + make_tuple(4, 4, sad_4x4_mmx))); +#endif +#if HAVE_SSE2 +const sad_m_by_n_fn_t sad_16x16_wmt = vp8_sad16x16_wmt; +const sad_m_by_n_fn_t sad_8x16_wmt = vp8_sad8x16_wmt; +const sad_m_by_n_fn_t sad_16x8_wmt = vp8_sad16x8_wmt; +const sad_m_by_n_fn_t sad_8x8_wmt = vp8_sad8x8_wmt; +const sad_m_by_n_fn_t sad_4x4_wmt = vp8_sad4x4_wmt; +INSTANTIATE_TEST_CASE_P(SSE2, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_wmt), + make_tuple(8, 16, sad_8x16_wmt), + make_tuple(16, 8, sad_16x8_wmt), + make_tuple(8, 8, sad_8x8_wmt), + make_tuple(4, 4, sad_4x4_wmt))); +#endif +#if HAVE_SSSE3 +const sad_m_by_n_fn_t sad_16x16_sse3 = vp8_sad16x16_sse3; +INSTANTIATE_TEST_CASE_P(SSE3, SADTest, ::testing::Values( + make_tuple(16, 16, sad_16x16_sse3))); +#endif + +} // namespace
diff --git a/test/set_roi.cc b/test/set_roi.cc new file mode 100644 index 0000000..3b6112e --- /dev/null +++ b/test/set_roi.cc
@@ -0,0 +1,182 @@ +/* + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "vpx/vpx_integer.h" +#include "vpx_mem/vpx_mem.h" +extern "C" { +#include "vp8/encoder/onyx_int.h" +} + +namespace { + +TEST(Vp8RoiMapTest, ParameterCheck) { + int delta_q[MAX_MB_SEGMENTS] = { -2, -25, 0, 31 }; + int delta_lf[MAX_MB_SEGMENTS] = { -2, -25, 0, 31 }; + unsigned int threshold[MAX_MB_SEGMENTS] = { 0, 100, 200, 300 }; + + const int internalq_trans[] = { + 0, 1, 2, 3, 4, 5, 7, 8, + 9, 10, 12, 13, 15, 17, 18, 19, + 20, 21, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 33, 35, 37, 39, 41, + 43, 45, 47, 49, 51, 53, 55, 57, + 59, 61, 64, 67, 70, 73, 76, 79, + 82, 85, 88, 91, 94, 97, 100, 103, + 106, 109, 112, 115, 118, 121, 124, 127, + }; + + // Initialize elements of cpi with valid defaults. + VP8_COMP cpi; + cpi.mb.e_mbd.mb_segement_abs_delta = SEGMENT_DELTADATA; + cpi.cyclic_refresh_mode_enabled = 0; + cpi.mb.e_mbd.segmentation_enabled = 0; + cpi.mb.e_mbd.update_mb_segmentation_map = 0; + cpi.mb.e_mbd.update_mb_segmentation_data = 0; + cpi.common.mb_rows = 240 >> 4; + cpi.common.mb_cols = 320 >> 4; + const int mbs = (cpi.common.mb_rows * cpi.common.mb_cols); + vpx_memset(cpi.segment_feature_data, 0, sizeof(cpi.segment_feature_data)); + + // Segment map + cpi.segmentation_map = reinterpret_cast<unsigned char *>(vpx_calloc(mbs, 1)); + + // Allocate memory for the source memory map. + unsigned char *roi_map = + reinterpret_cast<unsigned char *>(vpx_calloc(mbs, 1)); + vpx_memset(&roi_map[mbs >> 2], 1, (mbs >> 2)); + vpx_memset(&roi_map[mbs >> 1], 2, (mbs >> 2)); + vpx_memset(&roi_map[mbs -(mbs >> 2)], 3, (mbs >> 2)); + + // Do a test call with valid parameters. + int roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows, + cpi.common.mb_cols, delta_q, delta_lf, + threshold); + EXPECT_EQ(0, roi_retval) + << "vp8_set_roimap roi failed with default test parameters"; + + // Check that the values in the cpi structure get set as expected. + if (roi_retval == 0) { + // Check that the segment map got set. + const int mapcompare = memcmp(roi_map, cpi.segmentation_map, mbs); + EXPECT_EQ(0, mapcompare) << "segment map error"; + + // Check the q deltas (note the need to translate into + // the interanl range of 0-127. + for (int i = 0; i < MAX_MB_SEGMENTS; ++i) { + const int transq = internalq_trans[abs(delta_q[i])]; + if (abs(cpi.segment_feature_data[MB_LVL_ALT_Q][i]) != transq) { + EXPECT_EQ(transq, cpi.segment_feature_data[MB_LVL_ALT_Q][i]) + << "segment delta_q error"; + break; + } + } + + // Check the loop filter deltas + for (int i = 0; i < MAX_MB_SEGMENTS; ++i) { + if (cpi.segment_feature_data[MB_LVL_ALT_LF][i] != delta_lf[i]) { + EXPECT_EQ(delta_lf[i], cpi.segment_feature_data[MB_LVL_ALT_LF][i]) + << "segment delta_lf error"; + break; + } + } + + // Check the breakout thresholds + for (int i = 0; i < MAX_MB_SEGMENTS; ++i) { + unsigned int breakout = + static_cast<unsigned int>(cpi.segment_encode_breakout[i]); + + if (threshold[i] != breakout) { + EXPECT_EQ(threshold[i], breakout) + << "breakout threshold error"; + break; + } + } + + // Segmentation, and segmentation update flages should be set. + EXPECT_EQ(1, cpi.mb.e_mbd.segmentation_enabled) + << "segmentation_enabled error"; + EXPECT_EQ(1, cpi.mb.e_mbd.update_mb_segmentation_map) + << "update_mb_segmentation_map error"; + EXPECT_EQ(1, cpi.mb.e_mbd.update_mb_segmentation_data) + << "update_mb_segmentation_data error"; + + + // Try a range of delta q and lf parameters (some legal, some not) + for (int i = 0; i < 1000; ++i) { + int rand_deltas[4]; + int deltas_valid; + rand_deltas[0] = (rand() % 160) - 80; + rand_deltas[1] = (rand() % 160) - 80; + rand_deltas[2] = (rand() % 160) - 80; + rand_deltas[3] = (rand() % 160) - 80; + + deltas_valid = ((abs(rand_deltas[0]) <= 63) && + (abs(rand_deltas[1]) <= 63) && + (abs(rand_deltas[2]) <= 63) && + (abs(rand_deltas[3]) <= 63)) ? 0 : -1; + + // Test with random delta q values. + roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows, + cpi.common.mb_cols, rand_deltas, + delta_lf, threshold); + EXPECT_EQ(deltas_valid, roi_retval) << "dq range check error"; + + // One delta_q error shown at a time + if (deltas_valid != roi_retval) + break; + + // Test with random loop filter values. + roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows, + cpi.common.mb_cols, delta_q, + rand_deltas, threshold); + EXPECT_EQ(deltas_valid, roi_retval) << "dlf range check error"; + + // One delta loop filter error shown at a time + if (deltas_valid != roi_retval) + break; + } + + // Test that we report and error if cyclic refresh is enabled. + cpi.cyclic_refresh_mode_enabled = 1; + roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows, + cpi.common.mb_cols, delta_q, + delta_lf, threshold); + EXPECT_EQ(-1, roi_retval) << "cyclic refresh check error"; + cpi.cyclic_refresh_mode_enabled = 0; + + // Test invalid number of rows or colums. + roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows + 1, + cpi.common.mb_cols, delta_q, + delta_lf, threshold); + EXPECT_EQ(-1, roi_retval) << "MB rows bounds check error"; + + roi_retval = vp8_set_roimap(&cpi, roi_map, cpi.common.mb_rows, + cpi.common.mb_cols - 1, delta_q, + delta_lf, threshold); + EXPECT_EQ(-1, roi_retval) << "MB cols bounds check error"; + } + + // Free allocated memory + if (cpi.segmentation_map) + vpx_free(cpi.segmentation_map); + if (roi_map) + vpx_free(roi_map); +}; + +} // namespace
diff --git a/test/sixtap_predict_test.cc b/test/sixtap_predict_test.cc new file mode 100644 index 0000000..06f14a1 --- /dev/null +++ b/test/sixtap_predict_test.cc
@@ -0,0 +1,222 @@ +/* +* 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 "test/acm_random.h" +#include "test/util.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +extern "C" { +#include "./vpx_config.h" +#include "./vpx_rtcd.h" +#include "vpx/vpx_integer.h" +#include "vpx_mem/vpx_mem.h" +} + +namespace { + +typedef void (*sixtap_predict_fn_t)(uint8_t *src_ptr, + int src_pixels_per_line, + int xoffset, + int yoffset, + uint8_t *dst_ptr, + int dst_pitch); + +class SixtapPredictTest : public PARAMS(int, int, sixtap_predict_fn_t) { + public: + static void SetUpTestCase() { + src_ = reinterpret_cast<uint8_t*>(vpx_memalign(kDataAlignment, kSrcSize)); + dst_ = reinterpret_cast<uint8_t*>(vpx_memalign(kDataAlignment, kDstSize)); + dst_c_ = reinterpret_cast<uint8_t*>(vpx_memalign(kDataAlignment, kDstSize)); + } + + static void TearDownTestCase() { + vpx_free(src_); + src_ = NULL; + vpx_free(dst_); + dst_ = NULL; + vpx_free(dst_c_); + dst_c_ = NULL; + } + + protected: + // Make test arrays big enough for 16x16 functions. Six-tap filters + // need 5 extra pixels outside of the macroblock. + static const int kSrcStride = 21; + static const int kDstStride = 16; + static const int kDataAlignment = 16; + static const int kSrcSize = kSrcStride * kSrcStride + 1; + static const int kDstSize = kDstStride * kDstStride; + + virtual void SetUp() { + width_ = GET_PARAM(0); + height_ = GET_PARAM(1); + sixtap_predict_ = GET_PARAM(2); + memset(src_, 0, sizeof(src_)); + memset(dst_, 0, sizeof(dst_)); + memset(dst_c_, 0, sizeof(dst_c_)); + } + + int width_; + int height_; + sixtap_predict_fn_t sixtap_predict_; + // The src stores the macroblock we will filter on, and makes it 1 byte larger + // in order to test unaligned access. The result is stored in dst and dst_c(c + // reference code result). + static uint8_t* src_; + static uint8_t* dst_; + static uint8_t* dst_c_; +}; + +uint8_t* SixtapPredictTest::src_ = NULL; +uint8_t* SixtapPredictTest::dst_ = NULL; +uint8_t* SixtapPredictTest::dst_c_ = NULL; + +TEST_P(SixtapPredictTest, TestWithPresetData) { + // Test input + static const uint8_t test_data[kSrcSize] = { + 216, 184, 4, 191, 82, 92, 41, 0, 1, 226, 236, 172, 20, 182, 42, 226, 177, + 79, 94, 77, 179, 203, 206, 198, 22, 192, 19, 75, 17, 192, 44, 233, 120, + 48, 168, 203, 141, 210, 203, 143, 180, 184, 59, 201, 110, 102, 171, 32, + 182, 10, 109, 105, 213, 60, 47, 236, 253, 67, 55, 14, 3, 99, 247, 124, + 148, 159, 71, 34, 114, 19, 177, 38, 203, 237, 239, 58, 83, 155, 91, 10, + 166, 201, 115, 124, 5, 163, 104, 2, 231, 160, 16, 234, 4, 8, 103, 153, + 167, 174, 187, 26, 193, 109, 64, 141, 90, 48, 200, 174, 204, 36, 184, + 114, 237, 43, 238, 242, 207, 86, 245, 182, 247, 6, 161, 251, 14, 8, 148, + 182, 182, 79, 208, 120, 188, 17, 6, 23, 65, 206, 197, 13, 242, 126, 128, + 224, 170, 110, 211, 121, 197, 200, 47, 188, 207, 208, 184, 221, 216, 76, + 148, 143, 156, 100, 8, 89, 117, 14, 112, 183, 221, 54, 197, 208, 180, 69, + 176, 94, 180, 131, 215, 121, 76, 7, 54, 28, 216, 238, 249, 176, 58, 142, + 64, 215, 242, 72, 49, 104, 87, 161, 32, 52, 216, 230, 4, 141, 44, 181, + 235, 224, 57, 195, 89, 134, 203, 144, 162, 163, 126, 156, 84, 185, 42, + 148, 145, 29, 221, 194, 134, 52, 100, 166, 105, 60, 140, 110, 201, 184, + 35, 181, 153, 93, 121, 243, 227, 68, 131, 134, 232, 2, 35, 60, 187, 77, + 209, 76, 106, 174, 15, 241, 227, 115, 151, 77, 175, 36, 187, 121, 221, + 223, 47, 118, 61, 168, 105, 32, 237, 236, 167, 213, 238, 202, 17, 170, + 24, 226, 247, 131, 145, 6, 116, 117, 121, 11, 194, 41, 48, 126, 162, 13, + 93, 209, 131, 154, 122, 237, 187, 103, 217, 99, 60, 200, 45, 78, 115, 69, + 49, 106, 200, 194, 112, 60, 56, 234, 72, 251, 19, 120, 121, 182, 134, 215, + 135, 10, 114, 2, 247, 46, 105, 209, 145, 165, 153, 191, 243, 12, 5, 36, + 119, 206, 231, 231, 11, 32, 209, 83, 27, 229, 204, 149, 155, 83, 109, 35, + 93, 223, 37, 84, 14, 142, 37, 160, 52, 191, 96, 40, 204, 101, 77, 67, 52, + 53, 43, 63, 85, 253, 147, 113, 226, 96, 6, 125, 179, 115, 161, 17, 83, + 198, 101, 98, 85, 139, 3, 137, 75, 99, 178, 23, 201, 255, 91, 253, 52, + 134, 60, 138, 131, 208, 251, 101, 48, 2, 227, 228, 118, 132, 245, 202, + 75, 91, 44, 160, 231, 47, 41, 50, 147, 220, 74, 92, 219, 165, 89, 16 + }; + + // Expected result + static const uint8_t expected_dst[kDstSize] = { + 117, 102, 74, 135, 42, 98, 175, 206, 70, 73, 222, 197, 50, 24, 39, 49, 38, + 105, 90, 47, 169, 40, 171, 215, 200, 73, 109, 141, 53, 85, 177, 164, 79, + 208, 124, 89, 212, 18, 81, 145, 151, 164, 217, 153, 91, 154, 102, 102, + 159, 75, 164, 152, 136, 51, 213, 219, 186, 116, 193, 224, 186, 36, 231, + 208, 84, 211, 155, 167, 35, 59, 42, 76, 216, 149, 73, 201, 78, 149, 184, + 100, 96, 196, 189, 198, 188, 235, 195, 117, 129, 120, 129, 49, 25, 133, + 113, 69, 221, 114, 70, 143, 99, 157, 108, 189, 140, 78, 6, 55, 65, 240, + 255, 245, 184, 72, 90, 100, 116, 131, 39, 60, 234, 167, 33, 160, 88, 185, + 200, 157, 159, 176, 127, 151, 138, 102, 168, 106, 170, 86, 82, 219, 189, + 76, 33, 115, 197, 106, 96, 198, 136, 97, 141, 237, 151, 98, 137, 191, + 185, 2, 57, 95, 142, 91, 255, 185, 97, 137, 76, 162, 94, 173, 131, 193, + 161, 81, 106, 72, 135, 222, 234, 137, 66, 137, 106, 243, 210, 147, 95, + 15, 137, 110, 85, 66, 16, 96, 167, 147, 150, 173, 203, 140, 118, 196, + 84, 147, 160, 19, 95, 101, 123, 74, 132, 202, 82, 166, 12, 131, 166, + 189, 170, 159, 85, 79, 66, 57, 152, 132, 203, 194, 0, 1, 56, 146, 180, + 224, 156, 28, 83, 181, 79, 76, 80, 46, 160, 175, 59, 106, 43, 87, 75, + 136, 85, 189, 46, 71, 200, 90 + }; + + uint8_t *src = const_cast<uint8_t*>(test_data); + + sixtap_predict_(&src[kSrcStride * 2 + 2 + 1], kSrcStride, + 2, 2, dst_, kDstStride); + + for (int i = 0; i < height_; ++i) + for (int j = 0; j < width_; ++j) + ASSERT_EQ(expected_dst[i * kDstStride + j], dst_[i * kDstStride + j]) + << "i==" << (i * width_ + j); +} + +using libvpx_test::ACMRandom; + +TEST_P(SixtapPredictTest, TestWithRandomData) { + ACMRandom rnd(ACMRandom::DeterministicSeed()); + for (int i = 0; i < kSrcSize; ++i) + src_[i] = rnd.Rand8(); + + // Run tests for all possible offsets. + for (int xoffset = 0; xoffset < 8; ++xoffset) { + for (int yoffset = 0; yoffset < 8; ++yoffset) { + // Call c reference function. + // Move start point to next pixel to test if the function reads + // unaligned data correctly. + vp8_sixtap_predict16x16_c(&src_[kSrcStride * 2 + 2 + 1], kSrcStride, + xoffset, yoffset, dst_c_, kDstStride); + + // Run test. + sixtap_predict_(&src_[kSrcStride * 2 + 2 + 1], kSrcStride, + xoffset, yoffset, dst_, kDstStride); + + for (int i = 0; i < height_; ++i) + for (int j = 0; j < width_; ++j) + ASSERT_EQ(dst_c_[i * kDstStride + j], dst_[i * kDstStride + j]) + << "i==" << (i * width_ + j); + } + } +} + +using std::tr1::make_tuple; + +const sixtap_predict_fn_t sixtap_16x16_c = vp8_sixtap_predict16x16_c; +const sixtap_predict_fn_t sixtap_8x8_c = vp8_sixtap_predict8x8_c; +const sixtap_predict_fn_t sixtap_8x4_c = vp8_sixtap_predict8x4_c; +const sixtap_predict_fn_t sixtap_4x4_c = vp8_sixtap_predict4x4_c; +INSTANTIATE_TEST_CASE_P( + C, SixtapPredictTest, ::testing::Values( + make_tuple(16, 16, sixtap_16x16_c), + make_tuple(8, 8, sixtap_8x8_c), + make_tuple(8, 4, sixtap_8x4_c), + make_tuple(4, 4, sixtap_4x4_c))); +#if HAVE_MMX +const sixtap_predict_fn_t sixtap_16x16_mmx = vp8_sixtap_predict16x16_mmx; +const sixtap_predict_fn_t sixtap_8x8_mmx = vp8_sixtap_predict8x8_mmx; +const sixtap_predict_fn_t sixtap_8x4_mmx = vp8_sixtap_predict8x4_mmx; +const sixtap_predict_fn_t sixtap_4x4_mmx = vp8_sixtap_predict4x4_mmx; +INSTANTIATE_TEST_CASE_P( + MMX, SixtapPredictTest, ::testing::Values( + make_tuple(16, 16, sixtap_16x16_mmx), + make_tuple(8, 8, sixtap_8x8_mmx), + make_tuple(8, 4, sixtap_8x4_mmx), + make_tuple(4, 4, sixtap_4x4_mmx))); +#endif +#if HAVE_SSE2 +const sixtap_predict_fn_t sixtap_16x16_sse2 = vp8_sixtap_predict16x16_sse2; +const sixtap_predict_fn_t sixtap_8x8_sse2 = vp8_sixtap_predict8x8_sse2; +const sixtap_predict_fn_t sixtap_8x4_sse2 = vp8_sixtap_predict8x4_sse2; +INSTANTIATE_TEST_CASE_P( + SSE2, SixtapPredictTest, ::testing::Values( + make_tuple(16, 16, sixtap_16x16_sse2), + make_tuple(8, 8, sixtap_8x8_sse2), + make_tuple(8, 4, sixtap_8x4_sse2))); +#endif +#if HAVE_SSSE3 +const sixtap_predict_fn_t sixtap_16x16_ssse3 = vp8_sixtap_predict16x16_ssse3; +const sixtap_predict_fn_t sixtap_8x8_ssse3 = vp8_sixtap_predict8x8_ssse3; +const sixtap_predict_fn_t sixtap_8x4_ssse3 = vp8_sixtap_predict8x4_ssse3; +const sixtap_predict_fn_t sixtap_4x4_ssse3 = vp8_sixtap_predict4x4_ssse3; +INSTANTIATE_TEST_CASE_P( + SSSE3, SixtapPredictTest, ::testing::Values( + make_tuple(16, 16, sixtap_16x16_ssse3), + make_tuple(8, 8, sixtap_8x8_ssse3), + make_tuple(8, 4, sixtap_8x4_ssse3), + make_tuple(4, 4, sixtap_4x4_ssse3))); +#endif +} // namespace
diff --git a/test/subtract_test.cc b/test/subtract_test.cc new file mode 100644 index 0000000..99363de --- /dev/null +++ b/test/subtract_test.cc
@@ -0,0 +1,113 @@ +/* + * 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/acm_random.h" +extern "C" { +#include "vpx_config.h" +#include "vpx_rtcd.h" +#include "vp8/common/blockd.h" +#include "vp8/encoder/block.h" +#include "vpx_mem/vpx_mem.h" +} + +typedef void (*subtract_b_fn_t)(BLOCK *be, BLOCKD *bd, int pitch); + +namespace { + +class SubtractBlockTest : public ::testing::TestWithParam<subtract_b_fn_t> {}; + +using libvpx_test::ACMRandom; + +TEST_P(SubtractBlockTest, SimpleSubtract) { + ACMRandom rnd(ACMRandom::DeterministicSeed()); + BLOCK be; + BLOCKD bd; + // in libvpx, this stride is always 16 + const int kDiffPredStride = 16; + const int kSrcStride[] = {32, 16, 8, 4, 0}; + const int kBlockWidth = 4; + const int kBlockHeight = 4; + + // Allocate... align to 16 for mmx/sse tests + uint8_t *source = reinterpret_cast<uint8_t*>( + vpx_memalign(16, kBlockHeight * kSrcStride[0] * sizeof(*source))); + be.src_diff = reinterpret_cast<int16_t*>( + vpx_memalign(16, kBlockHeight * kDiffPredStride * sizeof(*be.src_diff))); + bd.predictor = reinterpret_cast<unsigned char*>( + vpx_memalign(16, kBlockHeight * kDiffPredStride * sizeof(*bd.predictor))); + + for(int i = 0; kSrcStride[i] > 0; ++i) { + // start at block0 + be.src = 0; + be.base_src = &source; + be.src_stride = kSrcStride[i]; + + // set difference + int16_t *src_diff = be.src_diff; + for (int r = 0; r < kBlockHeight; ++r) { + for (int c = 0; c < kBlockWidth; ++c) { + src_diff[c] = 0xa5a5; + } + src_diff += kDiffPredStride; + } + + // set destination + uint8_t *base_src = *be.base_src; + for (int r = 0; r < kBlockHeight; ++r) { + for (int c = 0; c < kBlockWidth; ++c) { + base_src[c] = rnd.Rand8(); + } + base_src += be.src_stride; + } + + // set predictor + uint8_t *predictor = bd.predictor; + for (int r = 0; r < kBlockHeight; ++r) { + for (int c = 0; c < kBlockWidth; ++c) { + predictor[c] = rnd.Rand8(); + } + predictor += kDiffPredStride; + } + + GetParam()(&be, &bd, kDiffPredStride); + + base_src = *be.base_src; + src_diff = be.src_diff; + predictor = bd.predictor; + for (int r = 0; r < kBlockHeight; ++r) { + for (int c = 0; c < kBlockWidth; ++c) { + EXPECT_EQ(base_src[c], (src_diff[c] + predictor[c])) << "r = " << r + << ", c = " << c; + } + src_diff += kDiffPredStride; + predictor += kDiffPredStride; + base_src += be.src_stride; + } + } + vpx_free(be.src_diff); + vpx_free(source); + vpx_free(bd.predictor); +} + +INSTANTIATE_TEST_CASE_P(C, SubtractBlockTest, + ::testing::Values(vp8_subtract_b_c)); + +#if HAVE_MMX +INSTANTIATE_TEST_CASE_P(MMX, SubtractBlockTest, + ::testing::Values(vp8_subtract_b_mmx)); +#endif + +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, SubtractBlockTest, + ::testing::Values(vp8_subtract_b_sse2)); +#endif + +} // namespace
diff --git a/test/test-data.sha1 b/test/test-data.sha1 index 8d40242..c1b6a83 100644 --- a/test/test-data.sha1 +++ b/test/test-data.sha1
@@ -1 +1,123 @@ d5dfb0151c9051f8c85999255645d7a23916d3c0 hantro_collage_w352h288.yuv +5184c46ddca8b1fadd16742e8500115bc8f749da vp80-00-comprehensive-001.ivf +65bf1bbbced81b97bd030f376d1b7f61a224793f vp80-00-comprehensive-002.ivf +906b4c1e99eb734504c504b3f1ad8052137ce672 vp80-00-comprehensive-003.ivf +ec144b1af53af895db78355785650b96dd3f0ade vp80-00-comprehensive-004.ivf +afc7091785c62f1c121c4554a2830c30704587d9 vp80-00-comprehensive-005.ivf +42ea9d55c818145d06a9b633b8e85c6a6164fd3e vp80-00-comprehensive-006.ivf +e5b3a73ab79fe024c14309d653d6bed92902ee3b vp80-00-comprehensive-007.ivf +f3c50a58875930adfb84525c0ef59d7e4c08540c vp80-00-comprehensive-008.ivf +4b2841fdb83db51ae322096ae468bbb9dc2c8362 vp80-00-comprehensive-009.ivf +efbff736e3a91ab6a98c5bc2dce65d645944c7b1 vp80-00-comprehensive-010.ivf +6b315102cae008d22a3d2c231be92cb704a222f8 vp80-00-comprehensive-011.ivf +f3214a4fea14c2d5ec689936c1613f274c859ee8 vp80-00-comprehensive-012.ivf +e4094e96d308c8a35b74c480a43d853c5294cd34 vp80-00-comprehensive-013.ivf +5b0adfaf60a69e0aaf3ec021a39d0a68fc0e1b5a vp80-00-comprehensive-014.ivf +e8467688ddf26b5000664f904faf0d70506aa653 vp80-00-comprehensive-015.ivf +aab55582337dfd2a39ff54fb2576a91910d49337 vp80-00-comprehensive-016.ivf +1ba24724f80203c9bae4f1d0f99d534721980016 vp80-00-comprehensive-017.ivf +143a15512b46f436280ddb4d0e6411eb4af434f2 vp80-00-comprehensive-018.ivf +c5baeaf5714fdfb3a8bc960a8e33ac438e83b16b vp80-01-intra-1400.ivf +f383955229afe3408453e316d11553d923ca60d5 vp80-01-intra-1411.ivf +84e1f4343f174c9f3c83f834bac3196fb325bf2c vp80-01-intra-1416.ivf +fb6e712a47dd57a28a3727d2ae2c97a8b7c7ca51 vp80-01-intra-1417.ivf +71ea772d3e9d315b8cbecf41207b8a237c34853b vp80-02-inter-1402.ivf +d85dbc4271525dcd128c503f936fe69091d1f8d0 vp80-02-inter-1412.ivf +d4e5d3ad56511867d025f93724d090f92ba6ec3d vp80-02-inter-1418.ivf +91791cbcc37c60f35dbd8090bacb54e5ec6dd4fa vp80-02-inter-1424.ivf +17fbfe2fea70f6e2f3fa6ca4efaae6c0b03b5f02 vp80-03-segmentation-01.ivf +3c3600dbbcde08e20d54c66fe3b7eadd4f09bdbb vp80-03-segmentation-02.ivf +c156778d5340967d4b369c490848076e92f1f875 vp80-03-segmentation-03.ivf +d25dcff6c60e87a1af70945b8911b6b4998533b0 vp80-03-segmentation-04.ivf +362baba2ce454c9db21218f35e81c27a5ed0b730 vp80-03-segmentation-1401.ivf +d223ae7ee748ce07e74c4679bfd219e84aa9f4b0 vp80-03-segmentation-1403.ivf +033adf7f3a13836a3f1cffcb87c1972900f2b5c6 vp80-03-segmentation-1407.ivf +4d51dfbf9f3e2c590ec99d1d6f59dd731d04375f vp80-03-segmentation-1408.ivf +f37a62b197c2600d75e0ccfbb31b60efdedac251 vp80-03-segmentation-1409.ivf +eb25bd7bfba5b2f6935018a930f42d123b1e7fcd vp80-03-segmentation-1410.ivf +b9d5c436663a30c27cfff84b53a002e501258843 vp80-03-segmentation-1413.ivf +6da92b9d1a180cc3a8afe348ab12258f5a37be1a vp80-03-segmentation-1414.ivf +a4f5842602886bd669f115f93d8a35c035cb0948 vp80-03-segmentation-1415.ivf +f295dceb8ef278b77251b3f9df8aee22e161d547 vp80-03-segmentation-1425.ivf +198dbf9f36f733200e432664cc8c5752d59779de vp80-03-segmentation-1426.ivf +7704804e32f5de976803929934a7fafe101ac7b0 vp80-03-segmentation-1427.ivf +831ccd862ea95ca025d2f3bd8b88678752f5416d vp80-03-segmentation-1432.ivf +b3c11978529289f9109f2766fcaba3ebc40e11ef vp80-03-segmentation-1435.ivf +a835a731f5520ebfc1002c40121264d0020559ac vp80-03-segmentation-1436.ivf +1d1732942f773bb2a5775fcb9689b1579ce28eab vp80-03-segmentation-1437.ivf +db04799adfe089dfdf74dbd43cc05ede7161f99e vp80-03-segmentation-1441.ivf +7caf39b3f20cfd52b998210878062e52a5edf1e6 vp80-03-segmentation-1442.ivf +3607f6bb4ee106c38fa1ea370dc4ff8b8cde2261 vp80-04-partitions-1404.ivf +93cc323b6b6867f1b12dd48773424549c6960a6b vp80-04-partitions-1405.ivf +047eedb14b865bdac8a3538e63801054e0295e9c vp80-04-partitions-1406.ivf +0f1233bd2bc33f56ce5e495dbd455d122339f384 vp80-05-sharpness-1428.ivf +51767fc136488a9535c2a4c38067c542ee2048df vp80-05-sharpness-1429.ivf +9805aa107672de25d6fb8c35e20d06deca5efe18 vp80-05-sharpness-1430.ivf +61db6b965f9c27aebe71b85bf2d5877e58e4bbdf vp80-05-sharpness-1431.ivf +10420d266290d2923555f84af38eeb96edbd3ae8 vp80-05-sharpness-1433.ivf +3ed24f9a80cddfdf75824ba95cdb4ff9286cb443 vp80-05-sharpness-1434.ivf +c87599cbecd72d4cd4f7ace3313b7a6bc6eb8163 vp80-05-sharpness-1438.ivf +aff51d865c2621b60510459244ea83e958e4baed vp80-05-sharpness-1439.ivf +da386e72b19b5485a6af199c5eb60ef25e510dd1 vp80-05-sharpness-1440.ivf +6759a095203d96ccd267ce09b1b050b8cc4c2f1f vp80-05-sharpness-1443.ivf +db55ec7fd02c864ba996ff060b25b1e08611330b vp80-00-comprehensive-001.ivf.md5 +29db0ad011cba1e45f856d5623cd38dac3e3bf19 vp80-00-comprehensive-002.ivf.md5 +e84f258f69e173e7d68f8f8c037a0a3766902182 vp80-00-comprehensive-003.ivf.md5 +eb7912eaf69559a16fd82bc3f5fb1524cf4a4466 vp80-00-comprehensive-004.ivf.md5 +4206f71c94894bd5b5b376f6c09b3817dbc65206 vp80-00-comprehensive-005.ivf.md5 +4f89b356f6f2fecb928f330a10f804f00f5325f5 vp80-00-comprehensive-006.ivf.md5 +2813236a32964dd8007e17648bcf035a20fcda6c vp80-00-comprehensive-007.ivf.md5 +10746c72098f872803c900e17c5680e451f5f498 vp80-00-comprehensive-008.ivf.md5 +39a23d0692ce64421a7bb7cdf6ccec5928d37fff vp80-00-comprehensive-009.ivf.md5 +f6e3de8931a0cc659bda8fbc14050346955e72d4 vp80-00-comprehensive-010.ivf.md5 +101683ec195b6e944f7cd1e468fc8921439363e6 vp80-00-comprehensive-011.ivf.md5 +1f592751ce46d8688998fa0fa4fbdcda0fd4058c vp80-00-comprehensive-012.ivf.md5 +6066176f90ca790251e795fca1a5797d59999841 vp80-00-comprehensive-013.ivf.md5 +2656da94ba93691f23edc4d60b3a09e2be46c217 vp80-00-comprehensive-014.ivf.md5 +c6e0d5f5d61460c8ac8edfa4e701f10312c03133 vp80-00-comprehensive-015.ivf.md5 +ee60fee501d8493e34e8d6a1fe315b51ed09b24a vp80-00-comprehensive-016.ivf.md5 +9f1914ceffcad4546c0a29de3ef591d8bea304dc vp80-00-comprehensive-017.ivf.md5 +e0305178fe288a9fd8082b39e2d03181edb19054 vp80-00-comprehensive-018.ivf.md5 +612494da2fa799cc9d76dcdd835ae6c7cb2e5c05 vp80-01-intra-1400.ivf.md5 +48ea06097ac8269c5e8c2131d3d0639f431fcf0e vp80-01-intra-1411.ivf.md5 +6e2ab4e7677ad0ba868083ca6bc387ee922b400c vp80-01-intra-1416.ivf.md5 +eca0a90348959ce3854142f8d8641b13050e8349 vp80-01-intra-1417.ivf.md5 +920feea203145d5c2258a91c4e6991934a79a99e vp80-02-inter-1402.ivf.md5 +f71d97909fe2b3dd65be7e1f56c72237f0cef200 vp80-02-inter-1412.ivf.md5 +e911254569a30bbb2a237ff8b79f69ed9da0672d vp80-02-inter-1418.ivf.md5 +58c789c50c9bb9cc90580bed291164a0939d28ba vp80-02-inter-1424.ivf.md5 +ff3e2f441327b9c20a0b37c524e0f5a48a36de7b vp80-03-segmentation-01.ivf.md5 +0791f417f076a542ae66fbc3426ab4d94cbd6c75 vp80-03-segmentation-02.ivf.md5 +722e50f1a6a91c34302d68681faffc1c26d1cc57 vp80-03-segmentation-03.ivf.md5 +c701f1885bcfb27fb8e70cc65606b289172ef889 vp80-03-segmentation-04.ivf.md5 +f79bc9ec189a2b4807632a3d0c5bf04a178b5300 vp80-03-segmentation-1401.ivf.md5 +b9aa4c74c0219b639811c44760d0b24cd8bb436a vp80-03-segmentation-1403.ivf.md5 +70d5a2207ca1891bcaebd5cf6dd88ce8d57b4334 vp80-03-segmentation-1407.ivf.md5 +265f962ee781531f9a93b9309461316fd32b2a1d vp80-03-segmentation-1408.ivf.md5 +0c4ecbbd6dc042d30e626d951b65f460dd6cd563 vp80-03-segmentation-1409.ivf.md5 +cf779af36a937f06570a0fca9db64ba133451dee vp80-03-segmentation-1410.ivf.md5 +0e6c5036d51ab078842f133934926c598a9cff02 vp80-03-segmentation-1413.ivf.md5 +eb3930aaf229116c80d507516c34759c3f6cdf69 vp80-03-segmentation-1414.ivf.md5 +123d6c0f72ee87911c4ae7538e87b7d163b22d6c vp80-03-segmentation-1415.ivf.md5 +e70551d1a38920e097a5d8782390b79ecaeb7505 vp80-03-segmentation-1425.ivf.md5 +44e8f4117e46dbb302b2cfd81171cc1a1846e431 vp80-03-segmentation-1426.ivf.md5 +52636e54aee5f95bbace37021bd67de5db767e9a vp80-03-segmentation-1427.ivf.md5 +b1ad3eff20215c28e295b15ef3636ed926d59cba vp80-03-segmentation-1432.ivf.md5 +24c22a552fa28a90e5978f67f57181cc2d7546d7 vp80-03-segmentation-1435.ivf.md5 +96c49c390abfced18a7a8c9b9ea10af778e10edb vp80-03-segmentation-1436.ivf.md5 +f95eb6214571434f1f73ab7833b9ccdf47588020 vp80-03-segmentation-1437.ivf.md5 +1c0700ca27c9b0090a7747a4b0b4dc21d1843181 vp80-03-segmentation-1441.ivf.md5 +81d4f23ca32667ee958bae579c8f5e97ba72eb97 vp80-03-segmentation-1442.ivf.md5 +272efcef07a3a30fbca51bfd566063d8258ec0be vp80-04-partitions-1404.ivf.md5 +66ed219ab812ac801b256d35cf495d193d4cf478 vp80-04-partitions-1405.ivf.md5 +36083f37f56f502bd60ec5e07502ee9e6b8699b0 vp80-04-partitions-1406.ivf.md5 +6ca909bf168a64c09415626294665dc1be3d1973 vp80-05-sharpness-1428.ivf.md5 +1667d2ee2334e5fdea8a8a866f4ccf3cf76f033a vp80-05-sharpness-1429.ivf.md5 +71bcbe5357d36a19df5b07fbe3e27bffa8893f0a vp80-05-sharpness-1430.ivf.md5 +89a09b1dffce2d55770a89e58d9925c70ef79bf8 vp80-05-sharpness-1431.ivf.md5 +08444a18b4e6ba3450c0796dd728d48c399a2dc9 vp80-05-sharpness-1433.ivf.md5 +6d6223719a90c13e848aa2a8a6642098cdb5977a vp80-05-sharpness-1434.ivf.md5 +41d70bb5fa45bc88da1604a0af466930b8dd77b5 vp80-05-sharpness-1438.ivf.md5 +086c56378df81b6cee264d7540a7b8f2b405c7a4 vp80-05-sharpness-1439.ivf.md5 +d32dc2c4165eb266ea4c23c14a45459b363def32 vp80-05-sharpness-1440.ivf.md5 +8c69dc3d8e563f56ffab5ad1e400d9e689dd23df vp80-05-sharpness-1443.ivf.md5 \ No newline at end of file
diff --git a/test/test.mk b/test/test.mk index 129c188..3c6d44c 100644 --- a/test/test.mk +++ b/test/test.mk
@@ -1,10 +1,186 @@ -LIBVPX_TEST_SRCS-yes += test.mk LIBVPX_TEST_SRCS-yes += acm_random.h -LIBVPX_TEST_SRCS-yes += boolcoder_test.cc -LIBVPX_TEST_SRCS-yes += dct16x16_test.cc -LIBVPX_TEST_SRCS-yes += fdct4x4_test.cc -LIBVPX_TEST_SRCS-yes += fdct8x8_test.cc -LIBVPX_TEST_SRCS-yes += idct8x8_test.cc +LIBVPX_TEST_SRCS-yes += test.mk LIBVPX_TEST_SRCS-yes += test_libvpx.cc +LIBVPX_TEST_SRCS-yes += util.h +LIBVPX_TEST_SRCS-yes += video_source.h -LIBVPX_TEST_DATA-yes += hantro_collage_w352h288.yuv +## +## BLACK BOX TESTS +## +## Black box tests only use the public API. +## +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += altref_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += config_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += cq_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += datarate_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += encode_test_driver.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += encode_test_driver.h +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += error_resilience_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += i420_video_source.h +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += keyframe_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += resize_test.cc + +LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += ../md5_utils.h ../md5_utils.c +LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += decode_test_driver.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += decode_test_driver.h +LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += ivf_video_source.h +LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += test_vector_test.cc +## +## WHITE BOX TESTS +## +## Whitebox tests invoke functions not exposed via the public API. Certain +## shared library builds don't make these functions accessible. +## +ifeq ($(CONFIG_SHARED),) + +# These tests require both the encoder and decoder to be built. +ifeq ($(CONFIG_VP8_ENCODER)$(CONFIG_VP8_DECODER),yesyes) +LIBVPX_TEST_SRCS-yes += boolcoder_test.cc +endif + +LIBVPX_TEST_SRCS-yes += idctllm_test.cc +LIBVPX_TEST_SRCS-yes += intrapred_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_POSTPROC) += pp_filter_test.cc +LIBVPX_TEST_SRCS-yes += sad_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += set_roi.cc +LIBVPX_TEST_SRCS-yes += sixtap_predict_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += subtract_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += vp8_fdct4x4_test.cc + +# VP9 tests +LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += fdct4x4_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += fdct8x8_test.cc +LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += dct16x16_test.cc +ifneq ($(CONFIG_VP9_ENCODER)$(CONFIG_VP9_DECODER),) +LIBVPX_TEST_SRCS-yes += idct8x8_test.cc +endif + +endif + + +## +## TEST DATA +## +LIBVPX_TEST_DATA-$(CONFIG_VP8_ENCODER) += hantro_collage_w352h288.yuv +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-001.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-002.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-003.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-004.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-005.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-006.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-007.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-008.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-009.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-010.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-011.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-012.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-013.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-014.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-015.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-016.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-017.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-018.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1400.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1411.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1416.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1417.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1402.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1412.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1418.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1424.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-01.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-02.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-03.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-04.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1401.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1403.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1407.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1408.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1409.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1410.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1413.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1414.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1415.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1425.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1426.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1427.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1432.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1435.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1436.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1437.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1441.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1442.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1404.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1405.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1406.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1428.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1429.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1430.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1431.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1433.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1434.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1438.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1439.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1440.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1443.ivf +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-001.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-002.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-003.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-004.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-005.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-006.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-007.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-008.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-009.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-010.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-011.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-012.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-013.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-014.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-015.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-016.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-017.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-018.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1400.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1411.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1416.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-01-intra-1417.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1402.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1412.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1418.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-02-inter-1424.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1401.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1403.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1407.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1408.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1409.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1410.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1413.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1414.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1415.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1425.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1426.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1427.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1432.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1435.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1436.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1437.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1441.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-1442.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-01.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-02.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-03.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-03-segmentation-04.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1404.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1405.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-04-partitions-1406.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1428.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1429.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1430.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1431.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1433.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1434.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1438.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1439.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1440.ivf.md5 +LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-05-sharpness-1443.ivf.md5
diff --git a/test/test_libvpx.cc b/test/test_libvpx.cc index 924aa2e..2b9b0c2 100644 --- a/test/test_libvpx.cc +++ b/test/test_libvpx.cc
@@ -26,7 +26,7 @@ ::testing::InitGoogleTest(&argc, argv); #if ARCH_X86 || ARCH_X86_64 - int simd_caps = x86_simd_caps(); + const int simd_caps = x86_simd_caps(); if (!(simd_caps & HAS_MMX)) append_gtest_filter(":-MMX/*"); if (!(simd_caps & HAS_SSE))
diff --git a/test/test_vector_test.cc b/test/test_vector_test.cc new file mode 100644 index 0000000..938457b --- /dev/null +++ b/test/test_vector_test.cc
@@ -0,0 +1,144 @@ +/* + 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 <cstdio> +#include <cstdlib> +#include <string> +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "test/decode_test_driver.h" +#include "test/ivf_video_source.h" +extern "C" { +#include "./md5_utils.h" +#include "vpx_mem/vpx_mem.h" +} + +#if defined(_MSC_VER) +#define snprintf sprintf_s +#endif + +namespace { +// There are 61 test vectors in total. +const char *kTestVectors[] = { + "vp80-00-comprehensive-001.ivf", + "vp80-00-comprehensive-002.ivf", "vp80-00-comprehensive-003.ivf", + "vp80-00-comprehensive-004.ivf", "vp80-00-comprehensive-005.ivf", + "vp80-00-comprehensive-006.ivf", "vp80-00-comprehensive-007.ivf", + "vp80-00-comprehensive-008.ivf", "vp80-00-comprehensive-009.ivf", + "vp80-00-comprehensive-010.ivf", "vp80-00-comprehensive-011.ivf", + "vp80-00-comprehensive-012.ivf", "vp80-00-comprehensive-013.ivf", + "vp80-00-comprehensive-014.ivf", "vp80-00-comprehensive-015.ivf", + "vp80-00-comprehensive-016.ivf", "vp80-00-comprehensive-017.ivf", + "vp80-00-comprehensive-018.ivf", "vp80-01-intra-1400.ivf", + "vp80-01-intra-1411.ivf", "vp80-01-intra-1416.ivf", + "vp80-01-intra-1417.ivf", "vp80-02-inter-1402.ivf", + "vp80-02-inter-1412.ivf", "vp80-02-inter-1418.ivf", + "vp80-02-inter-1424.ivf", "vp80-03-segmentation-01.ivf", + "vp80-03-segmentation-02.ivf", "vp80-03-segmentation-03.ivf", + "vp80-03-segmentation-04.ivf", "vp80-03-segmentation-1401.ivf", + "vp80-03-segmentation-1403.ivf", "vp80-03-segmentation-1407.ivf", + "vp80-03-segmentation-1408.ivf", "vp80-03-segmentation-1409.ivf", + "vp80-03-segmentation-1410.ivf", "vp80-03-segmentation-1413.ivf", + "vp80-03-segmentation-1414.ivf", "vp80-03-segmentation-1415.ivf", + "vp80-03-segmentation-1425.ivf", "vp80-03-segmentation-1426.ivf", + "vp80-03-segmentation-1427.ivf", "vp80-03-segmentation-1432.ivf", + "vp80-03-segmentation-1435.ivf", "vp80-03-segmentation-1436.ivf", + "vp80-03-segmentation-1437.ivf", "vp80-03-segmentation-1441.ivf", + "vp80-03-segmentation-1442.ivf", "vp80-04-partitions-1404.ivf", + "vp80-04-partitions-1405.ivf", "vp80-04-partitions-1406.ivf", + "vp80-05-sharpness-1428.ivf", "vp80-05-sharpness-1429.ivf", + "vp80-05-sharpness-1430.ivf", "vp80-05-sharpness-1431.ivf", + "vp80-05-sharpness-1433.ivf", "vp80-05-sharpness-1434.ivf", + "vp80-05-sharpness-1438.ivf", "vp80-05-sharpness-1439.ivf", + "vp80-05-sharpness-1440.ivf", "vp80-05-sharpness-1443.ivf" +}; + +class TestVectorTest : public libvpx_test::DecoderTest, + public ::testing::TestWithParam<const char*> { + protected: + TestVectorTest() : md5_file_(NULL) {} + + virtual ~TestVectorTest() { + if (md5_file_) + fclose(md5_file_); + } + + void OpenMD5File(const std::string& md5_file_name_) { + md5_file_ = libvpx_test::OpenTestDataFile(md5_file_name_); + ASSERT_TRUE(md5_file_) << "Md5 file open failed. Filename: " + << md5_file_name_; + } + + virtual void DecompressedFrameHook(const vpx_image_t& img, + const unsigned int frame_number) { + char expected_md5[33]; + char junk[128]; + + // Read correct md5 checksums. + const int res = fscanf(md5_file_, "%s %s", expected_md5, junk); + ASSERT_NE(res, EOF) << "Read md5 data failed"; + expected_md5[32] = '\0'; + + MD5Context md5; + MD5Init(&md5); + + // Compute and update md5 for each raw in decompressed data. + for (int plane = 0; plane < 3; ++plane) { + uint8_t *buf = img.planes[plane]; + + for (unsigned int y = 0; y < (plane ? (img.d_h + 1) >> 1 : img.d_h); + ++y) { + MD5Update(&md5, buf, (plane ? (img.d_w + 1) >> 1 : img.d_w)); + buf += img.stride[plane]; + } + } + + uint8_t md5_sum[16]; + MD5Final(md5_sum, &md5); + + char actual_md5[33]; + // Convert to get the actual md5. + for (int i = 0; i < 16; i++) { + snprintf(&actual_md5[i * 2], sizeof(actual_md5) - i * 2, "%02x", + md5_sum[i]); + } + actual_md5[32] = '\0'; + + // Check md5 match. + ASSERT_STREQ(expected_md5, actual_md5) + << "Md5 checksums don't match: frame number = " << frame_number; + } + + private: + FILE *md5_file_; +}; + +// This test runs through the whole set of test vectors, and decodes them. +// The md5 checksums are computed for each frame in the video file. If md5 +// checksums match the correct md5 data, then the test is passed. Otherwise, +// the test failed. +TEST_P(TestVectorTest, MD5Match) { + const std::string filename = GetParam(); + // Open compressed video file. + libvpx_test::IVFVideoSource video(filename); + + video.Init(); + + // Construct md5 file name. + const std::string md5_filename = filename + ".md5"; + OpenMD5File(md5_filename); + + // Decode frame, and check the md5 matching. + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); +} + +INSTANTIATE_TEST_CASE_P(TestVectorSequence, TestVectorTest, + ::testing::ValuesIn(kTestVectors)); + +} // namespace
diff --git a/test/util.h b/test/util.h new file mode 100644 index 0000000..06a70cc --- /dev/null +++ b/test/util.h
@@ -0,0 +1,18 @@ +/* + * 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 TEST_UTIL_H_ +#define TEST_UTIL_H_ + +// Macros +#define PARAMS(...) ::testing::TestWithParam< std::tr1::tuple< __VA_ARGS__ > > +#define GET_PARAM(k) std::tr1::get< k >(GetParam()) + +#endif // TEST_UTIL_H_
diff --git a/test/video_source.h b/test/video_source.h new file mode 100644 index 0000000..9772657 --- /dev/null +++ b/test/video_source.h
@@ -0,0 +1,175 @@ +/* + * 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 TEST_VIDEO_SOURCE_H_ +#define TEST_VIDEO_SOURCE_H_ + +#include <cstdio> +#include <cstdlib> +#include <string> +#include "test/acm_random.h" +#include "vpx/vpx_encoder.h" + +namespace libvpx_test { + +static FILE *OpenTestDataFile(const std::string& file_name) { + std::string path_to_source = file_name; + const char *kDataPath = getenv("LIBVPX_TEST_DATA_PATH"); + + if (kDataPath) { + path_to_source = kDataPath; + path_to_source += "/"; + path_to_source += file_name; + } + + return fopen(path_to_source.c_str(), "rb"); +} + +// Abstract base class for test video sources, which provide a stream of +// vpx_image_t images with associated timestamps and duration. +class VideoSource { + public: + virtual ~VideoSource() {} + + // Prepare the stream for reading, rewind/open as necessary. + virtual void Begin() = 0; + + // Advance the cursor to the next frame + virtual void Next() = 0; + + // Get the current video frame, or NULL on End-Of-Stream. + virtual vpx_image_t *img() const = 0; + + // Get the presentation timestamp of the current frame. + virtual vpx_codec_pts_t pts() const = 0; + + // Get the current frame's duration + virtual unsigned long duration() const = 0; + + // Get the timebase for the stream + virtual vpx_rational_t timebase() const = 0; + + // Get the current frame counter, starting at 0. + virtual unsigned int frame() const = 0; + + // Get the current file limit. + virtual unsigned int limit() const = 0; +}; + + +class DummyVideoSource : public VideoSource { + public: + DummyVideoSource() : img_(NULL), limit_(100), width_(0), height_(0) { + SetSize(80, 64); + } + + virtual ~DummyVideoSource() { vpx_img_free(img_); } + + virtual void Begin() { + frame_ = 0; + FillFrame(); + } + + virtual void Next() { + ++frame_; + FillFrame(); + } + + virtual vpx_image_t *img() const { + return (frame_ < limit_) ? img_ : NULL; + } + + // Models a stream where Timebase = 1/FPS, so pts == frame. + virtual vpx_codec_pts_t pts() const { return frame_; } + + virtual unsigned long duration() const { return 1; } + + virtual vpx_rational_t timebase() const { + const vpx_rational_t t = {1, 30}; + return t; + } + + virtual unsigned int frame() const { return frame_; } + + virtual unsigned int limit() const { return limit_; } + + void SetSize(unsigned int width, unsigned int height) { + if (width != width_ || height != height_) { + vpx_img_free(img_); + raw_sz_ = ((width + 31)&~31) * height * 3 / 2; + img_ = vpx_img_alloc(NULL, VPX_IMG_FMT_VPXI420, width, height, 32); + width_ = width; + height_ = height; + } + } + + protected: + virtual void FillFrame() { memset(img_->img_data, 0, raw_sz_); } + + vpx_image_t *img_; + size_t raw_sz_; + unsigned int limit_; + unsigned int frame_; + unsigned int width_; + unsigned int height_; +}; + + +class RandomVideoSource : public DummyVideoSource { + public: + RandomVideoSource(int seed = ACMRandom::DeterministicSeed()) + : rnd_(seed), + seed_(seed) { } + + protected: + // Reset the RNG to get a matching stream for the second pass + virtual void Begin() { + frame_ = 0; + rnd_.Reset(seed_); + FillFrame(); + } + + // 15 frames of noise, followed by 15 static frames. Reset to 0 rather + // than holding previous frames to encourage keyframes to be thrown. + virtual void FillFrame() { + if (frame_ % 30 < 15) + for (size_t i = 0; i < raw_sz_; ++i) + img_->img_data[i] = rnd_.Rand8(); + else + memset(img_->img_data, 0, raw_sz_); + } + + ACMRandom rnd_; + int seed_; +}; + +// Abstract base class for test video sources, which provide a stream of +// decompressed images to the decoder. +class CompressedVideoSource { + public: + virtual ~CompressedVideoSource() {} + + virtual void Init() = 0; + + // Prepare the stream for reading, rewind/open as necessary. + virtual void Begin() = 0; + + // Advance the cursor to the next frame + virtual void Next() = 0; + + virtual const uint8_t *cxdata() const = 0; + + virtual const unsigned int frame_size() const = 0; + + virtual const unsigned int frame_number() const = 0; +}; + +} // namespace libvpx_test + +#endif // TEST_VIDEO_SOURCE_H_
diff --git a/test/vp8_fdct4x4_test.cc b/test/vp8_fdct4x4_test.cc new file mode 100644 index 0000000..619b23d --- /dev/null +++ b/test/vp8_fdct4x4_test.cc
@@ -0,0 +1,169 @@ +/* +* 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + + +extern "C" { +#include "vpx_rtcd.h" +} + +#include "test/acm_random.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "vpx/vpx_integer.h" + + +namespace { + +const int cospi8sqrt2minus1 = 20091; +const int sinpi8sqrt2 = 35468; + +void reference_idct4x4(const int16_t *input, int16_t *output) { + const int16_t *ip = input; + int16_t *op = output; + + for (int i = 0; i < 4; ++i) { + const int a1 = ip[0] + ip[8]; + const int b1 = ip[0] - ip[8]; + const int temp1 = (ip[4] * sinpi8sqrt2) >> 16; + const int temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16); + const int c1 = temp1 - temp2; + const int temp3 = ip[4] + ((ip[4] * cospi8sqrt2minus1) >> 16); + const int temp4 = (ip[12] * sinpi8sqrt2) >> 16; + const int d1 = temp3 + temp4; + op[0] = a1 + d1; + op[12] = a1 - d1; + op[4] = b1 + c1; + op[8] = b1 - c1; + ++ip; + ++op; + } + ip = output; + op = output; + for (int i = 0; i < 4; ++i) { + const int a1 = ip[0] + ip[2]; + const int b1 = ip[0] - ip[2]; + const int temp1 = (ip[1] * sinpi8sqrt2) >> 16; + const int temp2 = ip[3] + ((ip[3] * cospi8sqrt2minus1) >> 16); + const int c1 = temp1 - temp2; + const int temp3 = ip[1] + ((ip[1] * cospi8sqrt2minus1) >> 16); + const int temp4 = (ip[3] * sinpi8sqrt2) >> 16; + const int d1 = temp3 + temp4; + op[0] = (a1 + d1 + 4) >> 3; + op[3] = (a1 - d1 + 4) >> 3; + op[1] = (b1 + c1 + 4) >> 3; + op[2] = (b1 - c1 + 4) >> 3; + ip += 4; + op += 4; + } +} + +using libvpx_test::ACMRandom; + +TEST(Vp8FdctTest, SignBiasCheck) { + ACMRandom rnd(ACMRandom::DeterministicSeed()); + int16_t test_input_block[16]; + int16_t test_output_block[16]; + const int pitch = 8; + int count_sign_block[16][2]; + const int count_test_block = 1000000; + + memset(count_sign_block, 0, sizeof(count_sign_block)); + + for (int i = 0; i < count_test_block; ++i) { + // Initialize a test block with input range [-255, 255]. + for (int j = 0; j < 16; ++j) + test_input_block[j] = rnd.Rand8() - rnd.Rand8(); + + vp8_short_fdct4x4_c(test_input_block, test_output_block, pitch); + + for (int j = 0; j < 16; ++j) { + if (test_output_block[j] < 0) + ++count_sign_block[j][0]; + else if (test_output_block[j] > 0) + ++count_sign_block[j][1]; + } + } + + bool bias_acceptable = true; + for (int j = 0; j < 16; ++j) + bias_acceptable = bias_acceptable && + (abs(count_sign_block[j][0] - count_sign_block[j][1]) < 10000); + + EXPECT_EQ(true, bias_acceptable) + << "Error: 4x4 FDCT has a sign bias > 1% for input range [-255, 255]"; + + memset(count_sign_block, 0, sizeof(count_sign_block)); + + for (int i = 0; i < count_test_block; ++i) { + // Initialize a test block with input range [-15, 15]. + for (int j = 0; j < 16; ++j) + test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4); + + vp8_short_fdct4x4_c(test_input_block, test_output_block, pitch); + + for (int j = 0; j < 16; ++j) { + if (test_output_block[j] < 0) + ++count_sign_block[j][0]; + else if (test_output_block[j] > 0) + ++count_sign_block[j][1]; + } + } + + bias_acceptable = true; + for (int j = 0; j < 16; ++j) + bias_acceptable = bias_acceptable && + (abs(count_sign_block[j][0] - count_sign_block[j][1]) < 100000); + + EXPECT_EQ(true, bias_acceptable) + << "Error: 4x4 FDCT has a sign bias > 10% for input range [-15, 15]"; +}; + +TEST(Vp8FdctTest, RoundTripErrorCheck) { + ACMRandom rnd(ACMRandom::DeterministicSeed()); + int max_error = 0; + double total_error = 0; + const int count_test_block = 1000000; + for (int i = 0; i < count_test_block; ++i) { + int16_t test_input_block[16]; + int16_t test_temp_block[16]; + int16_t test_output_block[16]; + + // Initialize a test block with input range [-255, 255]. + for (int j = 0; j < 16; ++j) + test_input_block[j] = rnd.Rand8() - rnd.Rand8(); + + const int pitch = 8; + vp8_short_fdct4x4_c(test_input_block, test_temp_block, pitch); + reference_idct4x4(test_temp_block, test_output_block); + + for (int j = 0; j < 16; ++j) { + const int diff = test_input_block[j] - test_output_block[j]; + const int error = diff * diff; + if (max_error < error) + max_error = error; + total_error += error; + } + } + + EXPECT_GE(1, max_error ) + << "Error: FDCT/IDCT has an individual roundtrip error > 1"; + + EXPECT_GE(count_test_block, total_error) + << "Error: FDCT/IDCT has average roundtrip error > 1 per block"; +}; + +} // namespace