| Index: media/cast/video_receiver/video_decoder_unittest.cc
|
| diff --git a/media/cast/video_receiver/video_decoder_unittest.cc b/media/cast/video_receiver/video_decoder_unittest.cc
|
| index 44de8809f88d10e5d1d499990e3866f62a52eb68..aa6b7ac058c9df3605d49685df77f676e43f1795 100644
|
| --- a/media/cast/video_receiver/video_decoder_unittest.cc
|
| +++ b/media/cast/video_receiver/video_decoder_unittest.cc
|
| @@ -2,92 +2,186 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include <stdint.h>
|
| +#include <cstdlib>
|
|
|
| #include "base/bind.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/test/simple_test_tick_clock.h"
|
| -#include "base/time/tick_clock.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/synchronization/condition_variable.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/time/time.h"
|
| #include "media/cast/cast_config.h"
|
| -#include "media/cast/cast_defines.h"
|
| -#include "media/cast/cast_environment.h"
|
| -#include "media/cast/cast_receiver.h"
|
| -#include "media/cast/test/fake_single_thread_task_runner.h"
|
| +#include "media/cast/test/utility/standalone_cast_environment.h"
|
| +#include "media/cast/test/utility/video_utility.h"
|
| #include "media/cast/video_receiver/video_decoder.h"
|
| -#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace media {
|
| namespace cast {
|
|
|
| -using testing::_;
|
| +namespace {
|
|
|
| -// Random frame size for testing.
|
| -static const int64 kStartMillisecond = INT64_C(1245);
|
| +const int kWidth = 360;
|
| +const int kHeight = 240;
|
| +const int kFrameRate = 10;
|
|
|
| -namespace {
|
| -class DecodeTestFrameCallback
|
| - : public base::RefCountedThreadSafe<DecodeTestFrameCallback> {
|
| - public:
|
| - DecodeTestFrameCallback() {}
|
| +VideoSenderConfig GetVideoSenderConfigForTest() {
|
| + VideoSenderConfig config;
|
| + config.width = kWidth;
|
| + config.height = kHeight;
|
| + config.max_frame_rate = kFrameRate;
|
| + return config;
|
| +}
|
|
|
| - void DecodeComplete(const scoped_refptr<media::VideoFrame>& decoded_frame,
|
| - const base::TimeTicks& render_time) {}
|
| +} // namespace
|
| +
|
| +class VideoDecoderTest
|
| + : public ::testing::TestWithParam<transport::VideoCodec> {
|
| + public:
|
| + VideoDecoderTest()
|
| + : cast_environment_(new StandaloneCastEnvironment()),
|
| + vp8_encoder_(GetVideoSenderConfigForTest(), 0),
|
| + cond_(&lock_) {
|
| + vp8_encoder_.Initialize();
|
| + }
|
|
|
| protected:
|
| - virtual ~DecodeTestFrameCallback() {}
|
| + virtual void SetUp() OVERRIDE {
|
| + VideoReceiverConfig decoder_config;
|
| + decoder_config.use_external_decoder = false;
|
| + decoder_config.codec = GetParam();
|
| + video_decoder_.reset(new VideoDecoder(cast_environment_, decoder_config));
|
| + CHECK_EQ(STATUS_VIDEO_INITIALIZED, video_decoder_->InitializationResult());
|
| +
|
| + next_frame_timestamp_ = base::TimeDelta();
|
| + last_frame_id_ = 0;
|
| + seen_a_decoded_frame_ = false;
|
| +
|
| + total_video_frames_feed_in_ = 0;
|
| + total_video_frames_decoded_ = 0;
|
| + }
|
|
|
| - private:
|
| - friend class base::RefCountedThreadSafe<DecodeTestFrameCallback>;
|
| + // Called from the unit test thread to create another EncodedVideoFrame and
|
| + // push it into the decoding pipeline.
|
| + void FeedMoreVideo(int num_dropped_frames) {
|
| + // Prepare a simulated EncodedVideoFrame to feed into the VideoDecoder.
|
| +
|
| + const gfx::Size frame_size(kWidth, kHeight);
|
| + const scoped_refptr<VideoFrame> video_frame =
|
| + VideoFrame::CreateFrame(VideoFrame::YV12,
|
| + frame_size,
|
| + gfx::Rect(frame_size),
|
| + frame_size,
|
| + next_frame_timestamp_);
|
| + next_frame_timestamp_ += base::TimeDelta::FromSeconds(1) / kFrameRate;
|
| + PopulateVideoFrame(video_frame, 0);
|
| +
|
| + // Encode |frame| into |encoded_frame->data|.
|
| + scoped_ptr<transport::EncodedVideoFrame> encoded_frame(
|
| + new transport::EncodedVideoFrame());
|
| + CHECK_EQ(transport::kVp8, GetParam()); // Only support VP8 test currently.
|
| + vp8_encoder_.Encode(video_frame, encoded_frame.get());
|
| + encoded_frame->codec = GetParam();
|
| + encoded_frame->frame_id = last_frame_id_ + 1 + num_dropped_frames;
|
| + last_frame_id_ = encoded_frame->frame_id;
|
| +
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + ++total_video_frames_feed_in_;
|
| + }
|
| +
|
| + cast_environment_->PostTask(
|
| + CastEnvironment::MAIN,
|
| + FROM_HERE,
|
| + base::Bind(&VideoDecoder::DecodeFrame,
|
| + base::Unretained(video_decoder_.get()),
|
| + base::Passed(&encoded_frame),
|
| + base::Bind(&VideoDecoderTest::OnDecodedFrame,
|
| + base::Unretained(this),
|
| + video_frame,
|
| + num_dropped_frames == 0)));
|
| + }
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(DecodeTestFrameCallback);
|
| -};
|
| -} // namespace
|
| + // Blocks the caller until all video that has been feed in has been decoded.
|
| + void WaitForAllVideoToBeDecoded() {
|
| + DCHECK(!cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| + base::AutoLock auto_lock(lock_);
|
| + while (total_video_frames_decoded_ < total_video_frames_feed_in_)
|
| + cond_.Wait();
|
| + EXPECT_EQ(total_video_frames_feed_in_, total_video_frames_decoded_);
|
| + }
|
|
|
| -class VideoDecoderTest : public ::testing::Test {
|
| - protected:
|
| - VideoDecoderTest()
|
| - : testing_clock_(new base::SimpleTestTickClock()),
|
| - task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)),
|
| - cast_environment_(
|
| - new CastEnvironment(scoped_ptr<base::TickClock>(testing_clock_),
|
| - task_runner_,
|
| - task_runner_,
|
| - task_runner_)),
|
| - test_callback_(new DecodeTestFrameCallback()) {
|
| - // Configure to vp8.
|
| - config_.codec = transport::kVp8;
|
| - config_.use_external_decoder = false;
|
| - decoder_.reset(new VideoDecoder(config_, cast_environment_));
|
| - testing_clock_->Advance(
|
| - base::TimeDelta::FromMilliseconds(kStartMillisecond));
|
| + private:
|
| + // Called by |vp8_decoder_| to deliver each frame of decoded video.
|
| + void OnDecodedFrame(const scoped_refptr<VideoFrame>& expected_video_frame,
|
| + bool should_be_continuous,
|
| + const scoped_refptr<VideoFrame>& video_frame,
|
| + bool is_continuous) {
|
| + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| +
|
| + // A NULL |video_frame| indicates a decode error, which we don't expect.
|
| + ASSERT_FALSE(!video_frame);
|
| +
|
| + // Did the decoder detect whether frames were dropped?
|
| + EXPECT_EQ(should_be_continuous, is_continuous);
|
| +
|
| + // Does the video data seem to be intact?
|
| + EXPECT_EQ(expected_video_frame->coded_size().width(),
|
| + video_frame->coded_size().width());
|
| + EXPECT_EQ(expected_video_frame->coded_size().height(),
|
| + video_frame->coded_size().height());
|
| + EXPECT_LT(40.0, I420PSNR(expected_video_frame, video_frame));
|
| + // TODO(miu): Once we start using VideoFrame::timestamp_, check that here.
|
| +
|
| + // Signal the main test thread that more video was decoded.
|
| + base::AutoLock auto_lock(lock_);
|
| + ++total_video_frames_decoded_;
|
| + cond_.Signal();
|
| }
|
|
|
| - virtual ~VideoDecoderTest() {}
|
| + const scoped_refptr<StandaloneCastEnvironment> cast_environment_;
|
| + scoped_ptr<VideoDecoder> video_decoder_;
|
| + base::TimeDelta next_frame_timestamp_;
|
| + uint32 last_frame_id_;
|
| + bool seen_a_decoded_frame_;
|
| +
|
| + Vp8Encoder vp8_encoder_;
|
|
|
| - scoped_ptr<VideoDecoder> decoder_;
|
| - VideoReceiverConfig config_;
|
| - base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
|
| - scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
|
| - scoped_refptr<CastEnvironment> cast_environment_;
|
| - scoped_refptr<DecodeTestFrameCallback> test_callback_;
|
| + base::Lock lock_;
|
| + base::ConditionVariable cond_;
|
| + int total_video_frames_feed_in_;
|
| + int total_video_frames_decoded_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(VideoDecoderTest);
|
| };
|
|
|
| -// TODO(pwestin): EXPECT_DEATH tests can not pass valgrind.
|
| -TEST_F(VideoDecoderTest, DISABLED_SizeZero) {
|
| - transport::EncodedVideoFrame encoded_frame;
|
| - base::TimeTicks render_time;
|
| - encoded_frame.codec = transport::kVp8;
|
| - EXPECT_DEATH(
|
| - decoder_->DecodeVideoFrame(
|
| - &encoded_frame,
|
| - render_time,
|
| - base::Bind(&DecodeTestFrameCallback::DecodeComplete, test_callback_)),
|
| - "Empty frame");
|
| +TEST_P(VideoDecoderTest, DecodesFrames) {
|
| + const int kNumFrames = 10;
|
| + for (int i = 0; i < kNumFrames; ++i)
|
| + FeedMoreVideo(0);
|
| + WaitForAllVideoToBeDecoded();
|
| +}
|
| +
|
| +TEST_P(VideoDecoderTest, RecoversFromDroppedFrames) {
|
| + const int kNumFrames = 100;
|
| + int next_drop_at = 3;
|
| + int next_num_dropped = 1;
|
| + for (int i = 0; i < kNumFrames; ++i) {
|
| + if (i == next_drop_at) {
|
| + const int num_dropped = next_num_dropped++;
|
| + next_drop_at *= 2;
|
| + i += num_dropped;
|
| + FeedMoreVideo(num_dropped);
|
| + } else {
|
| + FeedMoreVideo(0);
|
| + }
|
| + }
|
| + WaitForAllVideoToBeDecoded();
|
| }
|
|
|
| -// TODO(pwestin): Test decoding a real frame.
|
| +INSTANTIATE_TEST_CASE_P(VideoDecoderTestScenarios,
|
| + VideoDecoderTest,
|
| + ::testing::Values(transport::kVp8));
|
|
|
| } // namespace cast
|
| } // namespace media
|
|
|