--- /dev/null
+/*
+ * 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/video_source.h"
+#include "third_party/googletest/src/include/gtest/gtest.h"
+
+namespace libvpx_test {
+
+void Encoder::EncodeFrame(VideoSource *video, unsigned long flags) {
+ if (video->img())
+ EncodeFrameInternal(*video, 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,
+ unsigned long 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_, 0);
+ 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(),
+ 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;
+}
+
+void EncoderTest::RunLoop(VideoSource *video) {
+ for (unsigned int pass = 0; pass < passes_; pass++) {
+ 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_, &stats_);
+
+ bool again;
+
+ for (video->Begin(), again = true; again; video->Next()) {
+ again = video->img() != NULL;
+
+ PreEncodeFrameHook(video);
+ encoder.EncodeFrame(video, flags_);
+
+ CxDataIterator iter = encoder.GetCxData();
+
+ while (const vpx_codec_cx_pkt_t *pkt = iter.Next()) {
+ again = true;
+
+ if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
+ continue;
+
+ FramePktHook(pkt);
+ }
+
+ if (!Continue())
+ break;
+ }
+
+ EndPassHook();
+
+ if (!Continue())
+ break;
+ }
+}
+
+} // namespace libvpx_test
--- /dev/null
+/*
+ * 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)
+
+
+// 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;
+ }
+
+ 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,
+ TwopassStatsStore *stats)
+ : cfg_(cfg), deadline_(deadline), stats_(stats) {
+ memset(&encoder_, 0, sizeof(encoder_));
+ }
+
+ ~Encoder() {
+ vpx_codec_destroy(&encoder_);
+ }
+
+ CxDataIterator GetCxData() {
+ return CxDataIterator(&encoder_);
+ }
+
+ // This is a thin wrapper around vpx_codec_encode(), so refer to
+ // vpx_encoder.h for its semantics.
+ void EncodeFrame(VideoSource *video, unsigned long flags);
+
+ // Convenience wrapper for EncodeFrame()
+ void EncodeFrame(VideoSource *video) {
+ EncodeFrame(video, 0);
+ }
+
+ 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, unsigned long flags);
+
+ // Flush the encoder on EOS
+ void Flush();
+
+ vpx_codec_ctx_t encoder_;
+ vpx_codec_enc_cfg_t cfg_;
+ unsigned long deadline_;
+ 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), flags_(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) {}
+
+ // Hook to be called on every compressed data packet.
+ virtual void FramePktHook(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 flags_;
+};
+
+} // namespace libvpx_test
+
+// Macros to be used with ::testing::Combine
+#define PARAMS(...) ::testing::TestWithParam< std::tr1::tuple< __VA_ARGS__ > >
+#define GET_PARAM(k) std::tr1::get< k >(GetParam())
+
+#endif // TEST_ENCODE_TEST_DRIVER_H_
--- /dev/null
+/*
+ * 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 {
+
+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;
+ }
+
+ virtual bool Continue() {
+ return !HasFatalFailure() && !abort_;
+ }
+
+ virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video) {
+ if (kf_do_force_kf_)
+ flags_ = (video->frame() % 3) ? 0 : VPX_EFLAG_FORCE_KF;
+ }
+
+ 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_;
+};
+
+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));
+
+ 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>::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>::iterator iter = kf_pts_list_.begin();
+ iter != kf_pts_list_.end();
+ iter++) {
+ ASSERT_EQ(0, *iter % 25) << "Unexpected keyframe at frame " << *iter;
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(AllModes, KeyframeTest, ALL_TEST_MODES);
+} // namespace
LIBVPX_TEST_SRCS-yes += test.mk
LIBVPX_TEST_SRCS-yes += boolcoder_test.cc
+LIBVPX_TEST_SRCS-yes += encode_test_driver.cc
+LIBVPX_TEST_SRCS-yes += encode_test_driver.h
LIBVPX_TEST_SRCS-yes += idctllm_test.cc
+LIBVPX_TEST_SRCS-yes += keyframe_test.cc
LIBVPX_TEST_SRCS-yes += test_libvpx.cc
+LIBVPX_TEST_SRCS-yes += video_source.h
--- /dev/null
+/*
+ * 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 "vpx/vpx_encoder.h"
+
+namespace libvpx_test {
+
+// 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;
+};
+
+
+class DummyVideoSource : public VideoSource {
+ public:
+ DummyVideoSource()
+ : img_(NULL), limit_(100) { 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_; }
+
+ void SetSize(unsigned int width, unsigned int 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);
+ }
+
+ 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_;
+};
+
+
+class RandomVideoSource : public DummyVideoSource {
+ protected:
+ // 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] = rand();
+ else
+ memset(img_->img_data, 0, raw_sz_);
+ }
+};
+
+} // namespace libvpx_test
+
+#endif // TEST_VIDEO_SOURCE_H_