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