1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <cstdlib> 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/synchronization/condition_variable.h" 10 #include "base/synchronization/lock.h" 11 #include "base/time/time.h" 12 #include "media/cast/cast_config.h" 13 #include "media/cast/receiver/video_decoder.h" 14 #include "media/cast/sender/vp8_encoder.h" 15 #include "media/cast/test/utility/default_config.h" 16 #include "media/cast/test/utility/standalone_cast_environment.h" 17 #include "media/cast/test/utility/video_utility.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace media { 21 namespace cast { 22 23 namespace { 24 25 const int kWidth = 360; 26 const int kHeight = 240; 27 const int kFrameRate = 10; 28 29 VideoSenderConfig GetVideoSenderConfigForTest() { 30 VideoSenderConfig config = GetDefaultVideoSenderConfig(); 31 config.width = kWidth; 32 config.height = kHeight; 33 config.max_frame_rate = kFrameRate; 34 return config; 35 } 36 37 } // namespace 38 39 class VideoDecoderTest : public ::testing::TestWithParam<Codec> { 40 public: 41 VideoDecoderTest() 42 : cast_environment_(new StandaloneCastEnvironment()), 43 vp8_encoder_(GetVideoSenderConfigForTest(), 0), 44 cond_(&lock_) { 45 vp8_encoder_.Initialize(); 46 } 47 48 virtual ~VideoDecoderTest() { 49 // Make sure all threads have stopped before the environment goes away. 50 cast_environment_->Shutdown(); 51 } 52 53 protected: 54 virtual void SetUp() OVERRIDE { 55 video_decoder_.reset(new VideoDecoder(cast_environment_, GetParam())); 56 CHECK_EQ(STATUS_VIDEO_INITIALIZED, video_decoder_->InitializationResult()); 57 58 next_frame_timestamp_ = base::TimeDelta(); 59 last_frame_id_ = 0; 60 seen_a_decoded_frame_ = false; 61 62 total_video_frames_feed_in_ = 0; 63 total_video_frames_decoded_ = 0; 64 } 65 66 // Called from the unit test thread to create another EncodedFrame and push it 67 // into the decoding pipeline. 68 void FeedMoreVideo(int num_dropped_frames) { 69 // Prepare a simulated EncodedFrame to feed into the VideoDecoder. 70 71 const gfx::Size frame_size(kWidth, kHeight); 72 const scoped_refptr<VideoFrame> video_frame = 73 VideoFrame::CreateFrame(VideoFrame::YV12, 74 frame_size, 75 gfx::Rect(frame_size), 76 frame_size, 77 next_frame_timestamp_); 78 next_frame_timestamp_ += base::TimeDelta::FromSeconds(1) / kFrameRate; 79 PopulateVideoFrame(video_frame.get(), 0); 80 81 // Encode |frame| into |encoded_frame->data|. 82 scoped_ptr<EncodedFrame> encoded_frame( 83 new EncodedFrame()); 84 // Test only supports VP8, currently. 85 CHECK_EQ(CODEC_VIDEO_VP8, GetParam()); 86 vp8_encoder_.Encode(video_frame, encoded_frame.get()); 87 // Rewrite frame IDs for testing purposes. 88 encoded_frame->frame_id = last_frame_id_ + 1 + num_dropped_frames; 89 if (last_frame_id_ == 0) 90 encoded_frame->referenced_frame_id = encoded_frame->frame_id; 91 else 92 encoded_frame->referenced_frame_id = encoded_frame->frame_id - 1; 93 last_frame_id_ = encoded_frame->frame_id; 94 95 { 96 base::AutoLock auto_lock(lock_); 97 ++total_video_frames_feed_in_; 98 } 99 100 cast_environment_->PostTask( 101 CastEnvironment::MAIN, 102 FROM_HERE, 103 base::Bind(&VideoDecoder::DecodeFrame, 104 base::Unretained(video_decoder_.get()), 105 base::Passed(&encoded_frame), 106 base::Bind(&VideoDecoderTest::OnDecodedFrame, 107 base::Unretained(this), 108 video_frame, 109 num_dropped_frames == 0))); 110 } 111 112 // Blocks the caller until all video that has been feed in has been decoded. 113 void WaitForAllVideoToBeDecoded() { 114 DCHECK(!cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); 115 base::AutoLock auto_lock(lock_); 116 while (total_video_frames_decoded_ < total_video_frames_feed_in_) 117 cond_.Wait(); 118 EXPECT_EQ(total_video_frames_feed_in_, total_video_frames_decoded_); 119 } 120 121 private: 122 // Called by |vp8_decoder_| to deliver each frame of decoded video. 123 void OnDecodedFrame(const scoped_refptr<VideoFrame>& expected_video_frame, 124 bool should_be_continuous, 125 const scoped_refptr<VideoFrame>& video_frame, 126 bool is_continuous) { 127 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); 128 129 // A NULL |video_frame| indicates a decode error, which we don't expect. 130 ASSERT_FALSE(!video_frame.get()); 131 132 // Did the decoder detect whether frames were dropped? 133 EXPECT_EQ(should_be_continuous, is_continuous); 134 135 // Does the video data seem to be intact? 136 EXPECT_EQ(expected_video_frame->coded_size().width(), 137 video_frame->coded_size().width()); 138 EXPECT_EQ(expected_video_frame->coded_size().height(), 139 video_frame->coded_size().height()); 140 EXPECT_LT(40.0, I420PSNR(expected_video_frame, video_frame)); 141 // TODO(miu): Once we start using VideoFrame::timestamp_, check that here. 142 143 // Signal the main test thread that more video was decoded. 144 base::AutoLock auto_lock(lock_); 145 ++total_video_frames_decoded_; 146 cond_.Signal(); 147 } 148 149 const scoped_refptr<StandaloneCastEnvironment> cast_environment_; 150 scoped_ptr<VideoDecoder> video_decoder_; 151 base::TimeDelta next_frame_timestamp_; 152 uint32 last_frame_id_; 153 bool seen_a_decoded_frame_; 154 155 Vp8Encoder vp8_encoder_; 156 157 base::Lock lock_; 158 base::ConditionVariable cond_; 159 int total_video_frames_feed_in_; 160 int total_video_frames_decoded_; 161 162 DISALLOW_COPY_AND_ASSIGN(VideoDecoderTest); 163 }; 164 165 TEST_P(VideoDecoderTest, DecodesFrames) { 166 const int kNumFrames = 10; 167 for (int i = 0; i < kNumFrames; ++i) 168 FeedMoreVideo(0); 169 WaitForAllVideoToBeDecoded(); 170 } 171 172 TEST_P(VideoDecoderTest, RecoversFromDroppedFrames) { 173 const int kNumFrames = 100; 174 int next_drop_at = 3; 175 int next_num_dropped = 1; 176 for (int i = 0; i < kNumFrames; ++i) { 177 if (i == next_drop_at) { 178 const int num_dropped = next_num_dropped++; 179 next_drop_at *= 2; 180 i += num_dropped; 181 FeedMoreVideo(num_dropped); 182 } else { 183 FeedMoreVideo(0); 184 } 185 } 186 WaitForAllVideoToBeDecoded(); 187 } 188 189 INSTANTIATE_TEST_CASE_P(VideoDecoderTestScenarios, 190 VideoDecoderTest, 191 ::testing::Values(CODEC_VIDEO_VP8)); 192 193 } // namespace cast 194 } // namespace media 195