| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stdint.h> | 5 #include <stdint.h> |
| 6 #include <string.h> | 6 #include <string.h> |
| 7 | 7 |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/run_loop.h" |
| 10 #include "base/single_thread_task_runner.h" | 11 #include "base/single_thread_task_runner.h" |
| 11 #include "base/synchronization/waitable_event.h" | 12 #include "base/synchronization/waitable_event.h" |
| 13 #include "base/test/test_simple_task_runner.h" |
| 12 #include "base/threading/thread.h" | 14 #include "base/threading/thread.h" |
| 13 #include "base/threading/thread_task_runner_handle.h" | 15 #include "base/threading/thread_task_runner_handle.h" |
| 14 #include "content/renderer/media/gpu/rtc_video_decoder.h" | 16 #include "content/renderer/media/gpu/rtc_video_decoder.h" |
| 15 #include "media/base/gmock_callback_support.h" | 17 #include "media/base/gmock_callback_support.h" |
| 18 #include "media/base/mock_filters.h" |
| 19 #include "media/filters/fake_video_decoder.h" |
| 16 #include "media/renderers/mock_gpu_video_accelerator_factories.h" | 20 #include "media/renderers/mock_gpu_video_accelerator_factories.h" |
| 17 #include "media/video/mock_video_decode_accelerator.h" | 21 #include "media/video/mock_video_decode_accelerator.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
| 19 | 23 |
| 20 #if defined(OS_WIN) | 24 #if defined(OS_WIN) |
| 21 #include "base/command_line.h" | 25 #include "base/command_line.h" |
| 22 #include "content/public/common/content_switches.h" | 26 #include "content/public/common/content_switches.h" |
| 23 #endif // defined(OS_WIN) | 27 #endif // defined(OS_WIN) |
| 24 | 28 |
| 25 using ::testing::_; | 29 using ::testing::_; |
| 26 using ::testing::Invoke; | 30 using ::testing::Invoke; |
| 27 using ::testing::Return; | 31 using ::testing::Return; |
| 28 using ::testing::SaveArg; | 32 using ::testing::SaveArg; |
| 29 using ::testing::Values; | 33 using ::testing::Values; |
| 30 using ::testing::WithArgs; | 34 using ::testing::WithArgs; |
| 31 | 35 |
| 32 namespace content { | 36 namespace content { |
| 33 | 37 |
| 34 namespace { | 38 namespace { |
| 35 | 39 |
| 36 static const int kMinResolutionWidth = 16; | 40 const int kVideoFrameWidth = 1280; |
| 37 static const int kMinResolutionHeight = 16; | 41 const int kVideoFrameHeight = 720; |
| 38 static const int kMaxResolutionWidth = 1920; | 42 |
| 39 static const int kMaxResolutionHeight = 1088; | 43 const int kMaxParallelVideoDecoderRequests = 4; |
| 44 |
| 45 void OnBytesDecoded(int numbytes) {} |
| 46 |
| 47 // This class creates and manages a thread to which tasks may be posted, and |
| 48 // which exposes BlockUntilTaskRunnerIdle(), allowing callers on other threads |
| 49 // to block until the thread this idle. |
| 50 // This is accomplished via two task runners on the underlying thread. Tasks may |
| 51 // be posted to the one returned by task_runner(), while another, which is |
| 52 // private to the implementation, pumps and controls the first. To the caller, |
| 53 // this interface appears as a Thread on which one can block. |
| 54 class ThreadHelper { |
| 55 public: |
| 56 explicit ThreadHelper(const std::string& thread_name); |
| 57 ~ThreadHelper(); |
| 58 |
| 59 // Start the underlying thread. Return true on success. After this call, |
| 60 // task_runner() will return a valid base::SingleThreadTaskRunner. |
| 61 bool Start(); |
| 62 |
| 63 // Return true if the underlying thread is running, false otherwise. |
| 64 bool IsRunning(); |
| 65 |
| 66 // Block the calling thread until task_runner() is idle. If tasks are posted |
| 67 // to the task_runner() during execution of other tasks, those will run too. |
| 68 // Thus, this call may never return. |
| 69 void BlockUntilTaskRunnerIdle(); |
| 70 |
| 71 // Stop the underlying thread. task_runner() will return nullptr after this |
| 72 // is called. |
| 73 void Stop(); |
| 74 |
| 75 // Tasks may be posted to this task_runner. Will when this is not running, |
| 76 // returns nullptr. |
| 77 scoped_refptr<base::SingleThreadTaskRunner> task_runner() const { |
| 78 return postable_task_runner_; |
| 79 } |
| 80 |
| 81 private: |
| 82 void StartPumping(); |
| 83 void Pump(); |
| 84 void StopPumping(); |
| 85 |
| 86 base::Thread thread_; |
| 87 base::WaitableEvent waiter_; |
| 88 bool pumping_ = false; |
| 89 scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_; |
| 90 scoped_refptr<base::TestSimpleTaskRunner> postable_task_runner_; |
| 91 |
| 92 DISALLOW_COPY_AND_ASSIGN(ThreadHelper); |
| 93 }; |
| 94 |
| 95 ThreadHelper::ThreadHelper(const std::string& thread_name) |
| 96 : thread_(thread_name), |
| 97 waiter_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| 98 base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
| 99 |
| 100 ThreadHelper::~ThreadHelper() { |
| 101 if (thread_.IsRunning()) |
| 102 Stop(); |
| 103 } |
| 104 |
| 105 bool ThreadHelper::Start() { |
| 106 if (!thread_.Start()) |
| 107 return false; |
| 108 control_task_runner_ = thread_.task_runner(); |
| 109 control_task_runner_->PostTask( |
| 110 FROM_HERE, |
| 111 base::Bind(&ThreadHelper::StartPumping, base::Unretained(this))); |
| 112 waiter_.Wait(); |
| 113 return true; |
| 114 } |
| 115 |
| 116 bool ThreadHelper::IsRunning() { |
| 117 return thread_.IsRunning(); |
| 118 } |
| 119 |
| 120 void ThreadHelper::BlockUntilTaskRunnerIdle() { |
| 121 waiter_.Wait(); |
| 122 } |
| 123 |
| 124 void ThreadHelper::Stop() { |
| 125 BlockUntilTaskRunnerIdle(); |
| 126 control_task_runner_->PostTask( |
| 127 FROM_HERE, |
| 128 base::Bind(&ThreadHelper::StopPumping, base::Unretained(this))); |
| 129 waiter_.Wait(); |
| 130 thread_.Stop(); |
| 131 } |
| 132 |
| 133 // Start pumping the task_runner(), so that it executes tasks. Called in |
| 134 // Start(). Must be called on the |control_task_runner_|. |
| 135 void ThreadHelper::StartPumping() { |
| 136 DCHECK(!postable_task_runner_); |
| 137 postable_task_runner_ = new base::TestSimpleTaskRunner(); |
| 138 thread_.message_loop()->SetTaskRunner(postable_task_runner_); |
| 139 |
| 140 // Start pumping. |
| 141 pumping_ = true; |
| 142 Pump(); |
| 143 } |
| 144 |
| 145 // Recurring task which causes tasks posted to task_runner() to run. |
| 146 // BlockUntilTaskRunnerIdle() will block the calling thread while this is |
| 147 // running. Must be called on the |control_task_runner_|. |
| 148 void ThreadHelper::Pump() { |
| 149 postable_task_runner_->RunUntilIdle(); |
| 150 if (pumping_) { |
| 151 control_task_runner_->PostDelayedTask( |
| 152 FROM_HERE, base::Bind(&ThreadHelper::Pump, base::Unretained(this)), |
| 153 base::TimeDelta::FromMilliseconds(10)); |
| 154 } |
| 155 waiter_.Signal(); |
| 156 } |
| 157 |
| 158 // Stop pumping the task_runner(). Called in Stop(). Must be called on the |
| 159 // control_task_runner_|. |
| 160 void ThreadHelper::StopPumping() { |
| 161 thread_.message_loop()->SetTaskRunner(control_task_runner_); |
| 162 pumping_ = false; |
| 163 } |
| 40 | 164 |
| 41 } // namespace | 165 } // namespace |
| 42 | 166 |
| 43 // TODO(wuchengli): add MockSharedMemory so more functions can be tested. | 167 // TODO(wuchengli): add MockSharedMemory so more functions can be tested. |
| 44 class RTCVideoDecoderTest | 168 class RTCVideoDecoderTest |
| 45 : public ::testing::TestWithParam<webrtc::VideoCodecType>, | 169 : public ::testing::TestWithParam<webrtc::VideoCodecType>, |
| 46 webrtc::DecodedImageCallback { | 170 webrtc::DecodedImageCallback { |
| 47 public: | 171 public: |
| 48 RTCVideoDecoderTest() | 172 RTCVideoDecoderTest() |
| 49 : mock_gpu_factories_( | 173 : fake_video_decoder_(nullptr), video_decoder_thread_("decoder_thread") { |
| 50 new media::MockGpuVideoAcceleratorFactories(nullptr)), | |
| 51 vda_thread_("vda_thread"), | |
| 52 idle_waiter_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| 53 base::WaitableEvent::InitialState::NOT_SIGNALED) { | |
| 54 memset(&codec_, 0, sizeof(codec_)); | 174 memset(&codec_, 0, sizeof(codec_)); |
| 55 } | 175 } |
| 56 | 176 |
| 57 void SetUp() override { | 177 void SetUp() override { |
| 58 ASSERT_TRUE(vda_thread_.Start()); | 178 ASSERT_TRUE(video_decoder_thread_.Start()); |
| 59 vda_task_runner_ = vda_thread_.task_runner(); | 179 video_decoder_task_runner_ = video_decoder_thread_.task_runner(); |
| 60 mock_vda_ = new media::MockVideoDecodeAccelerator; | |
| 61 | 180 |
| 62 media::VideoDecodeAccelerator::SupportedProfile supported_profile; | 181 // If any frames are passed in, they should match these values. |
| 63 supported_profile.min_resolution.SetSize(kMinResolutionWidth, | 182 codec_.width = kVideoFrameWidth; |
| 64 kMinResolutionHeight); | 183 codec_.height = kVideoFrameWidth; |
| 65 supported_profile.max_resolution.SetSize(kMaxResolutionWidth, | |
| 66 kMaxResolutionHeight); | |
| 67 supported_profile.profile = media::H264PROFILE_MAIN; | |
| 68 capabilities_.supported_profiles.push_back(supported_profile); | |
| 69 supported_profile.profile = media::VP8PROFILE_ANY; | |
| 70 capabilities_.supported_profiles.push_back(supported_profile); | |
| 71 | |
| 72 EXPECT_CALL(*mock_gpu_factories_.get(), GetTaskRunner()) | |
| 73 .WillRepeatedly(Return(vda_task_runner_)); | |
| 74 EXPECT_CALL(*mock_gpu_factories_.get(), | |
| 75 GetVideoDecodeAcceleratorCapabilities()) | |
| 76 .WillRepeatedly(Return(capabilities_)); | |
| 77 EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoDecodeAccelerator()) | |
| 78 .WillRepeatedly(Return(mock_vda_)); | |
| 79 EXPECT_CALL(*mock_vda_, Initialize(_, _)) | |
| 80 .Times(1) | |
| 81 .WillRepeatedly(Return(true)); | |
| 82 EXPECT_CALL(*mock_vda_, Destroy()).Times(1); | |
| 83 | 184 |
| 84 #if defined(OS_WIN) | 185 #if defined(OS_WIN) |
| 85 base::CommandLine::ForCurrentProcess()->AppendSwitch( | 186 base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| 86 switches::kEnableWin7WebRtcHWH264Decoding); | 187 switches::kEnableWin7WebRtcHWH264Decoding); |
| 87 #endif // defined(OS_WIN) | 188 #endif // defined(OS_WIN) |
| 88 } | 189 } |
| 89 | 190 |
| 90 void TearDown() override { | 191 void TearDown() override { |
| 91 DVLOG(2) << "TearDown"; | 192 DVLOG(2) << "TearDown"; |
| 92 EXPECT_TRUE(vda_thread_.IsRunning()); | 193 EXPECT_TRUE(video_decoder_thread_.IsRunning()); |
| 93 RunUntilIdle(); // Wait until all callbascks complete. | 194 RTCVideoDecoder::Destroy(rtc_decoder_.release(), |
| 94 vda_task_runner_->DeleteSoon(FROM_HERE, rtc_decoder_.release()); | 195 video_decoder_task_runner_); |
| 95 // Make sure the decoder is released before stopping the thread. | 196 // Make sure the decoder is released before stopping the thread. |
| 96 RunUntilIdle(); | 197 RunUntilIdle(); |
| 97 vda_thread_.Stop(); | 198 video_decoder_thread_.Stop(); |
| 98 } | 199 } |
| 99 | 200 |
| 100 int32_t Decoded(webrtc::VideoFrame& decoded_image) override { | 201 int32_t Decoded(webrtc::VideoFrame& decoded_image) override { |
| 101 DVLOG(2) << "Decoded"; | 202 DVLOG(2) << "Decoded"; |
| 102 EXPECT_EQ(vda_task_runner_, base::ThreadTaskRunnerHandle::Get()); | 203 EXPECT_EQ(video_decoder_task_runner_, base::ThreadTaskRunnerHandle::Get()); |
| 103 return WEBRTC_VIDEO_CODEC_OK; | 204 return WEBRTC_VIDEO_CODEC_OK; |
| 104 } | 205 } |
| 105 | 206 |
| 106 void CreateDecoder(webrtc::VideoCodecType codec_type) { | 207 void CreateDecoder(webrtc::VideoCodecType codec_type) { |
| 107 DVLOG(2) << "CreateDecoder"; | 208 DVLOG(2) << "CreateDecoder"; |
| 209 |
| 210 // If a decoder already exists, delete the old one. |
| 211 if (rtc_decoder_) { |
| 212 RTCVideoDecoder::Destroy(rtc_decoder_.release(), |
| 213 video_decoder_task_runner_); |
| 214 } |
| 215 |
| 108 codec_.codecType = codec_type; | 216 codec_.codecType = codec_type; |
| 109 rtc_decoder_ = | 217 rtc_decoder_ = RTCVideoDecoder::Create( |
| 110 RTCVideoDecoder::Create(codec_type, mock_gpu_factories_.get()); | 218 codec_type, base::Bind(&RTCVideoDecoderTest::CreateFakeVideoDecoder, |
| 219 base::Unretained(this)), |
| 220 video_decoder_task_runner_); |
| 221 RunUntilIdle(); |
| 111 } | 222 } |
| 112 | 223 |
| 113 void Initialize() { | 224 void Initialize() { |
| 114 DVLOG(2) << "Initialize"; | 225 DVLOG(2) << "Initialize"; |
| 226 DCHECK(rtc_decoder_); |
| 115 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); | 227 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); |
| 116 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, | 228 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, |
| 117 rtc_decoder_->RegisterDecodeCompleteCallback(this)); | 229 rtc_decoder_->RegisterDecodeCompleteCallback(this)); |
| 118 } | 230 } |
| 119 | 231 |
| 120 void NotifyResetDone() { | 232 // This function blocks the calling thread until |video_decoder_thread_| is |
| 121 DVLOG(2) << "NotifyResetDone"; | 233 // idle. |
| 122 vda_task_runner_->PostTask( | |
| 123 FROM_HERE, | |
| 124 base::Bind(&RTCVideoDecoder::NotifyResetDone, | |
| 125 base::Unretained(rtc_decoder_.get()))); | |
| 126 } | |
| 127 | |
| 128 void NotifyError(media::VideoDecodeAccelerator::Error error) { | |
| 129 DVLOG(2) << "NotifyError"; | |
| 130 vda_task_runner_->PostTask( | |
| 131 FROM_HERE, | |
| 132 base::Bind(&RTCVideoDecoder::NotifyError, | |
| 133 base::Unretained(rtc_decoder_.get()), error)); | |
| 134 } | |
| 135 | |
| 136 void RunUntilIdle() { | 234 void RunUntilIdle() { |
| 137 DVLOG(2) << "RunUntilIdle"; | 235 DVLOG(2) << "RunUntilIdle"; |
| 138 vda_task_runner_->PostTask(FROM_HERE, | 236 video_decoder_thread_.BlockUntilTaskRunnerIdle(); |
| 139 base::Bind(&base::WaitableEvent::Signal, | |
| 140 base::Unretained(&idle_waiter_))); | |
| 141 idle_waiter_.Wait(); | |
| 142 } | |
| 143 | |
| 144 void SetUpResetVDA() { | |
| 145 mock_vda_after_reset_ = new media::MockVideoDecodeAccelerator; | |
| 146 EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoDecodeAccelerator()) | |
| 147 .WillRepeatedly(Return(mock_vda_after_reset_)); | |
| 148 EXPECT_CALL(*mock_vda_after_reset_, Initialize(_, _)) | |
| 149 .Times(1) | |
| 150 .WillRepeatedly(Return(true)); | |
| 151 EXPECT_CALL(*mock_vda_after_reset_, Destroy()).Times(1); | |
| 152 } | 237 } |
| 153 | 238 |
| 154 protected: | 239 protected: |
| 155 std::unique_ptr<media::MockGpuVideoAcceleratorFactories> mock_gpu_factories_; | 240 media::FakeVideoDecoder* fake_video_decoder_; |
| 156 media::MockVideoDecodeAccelerator* mock_vda_; | |
| 157 media::MockVideoDecodeAccelerator* mock_vda_after_reset_; | |
| 158 std::unique_ptr<RTCVideoDecoder> rtc_decoder_; | 241 std::unique_ptr<RTCVideoDecoder> rtc_decoder_; |
| 159 webrtc::VideoCodec codec_; | 242 webrtc::VideoCodec codec_; |
| 160 base::Thread vda_thread_; | 243 scoped_refptr<base::SingleThreadTaskRunner> video_decoder_task_runner_; |
| 161 media::VideoDecodeAccelerator::Capabilities capabilities_; | |
| 162 | 244 |
| 163 private: | 245 private: |
| 164 scoped_refptr<base::SingleThreadTaskRunner> vda_task_runner_; | 246 std::unique_ptr<media::VideoDecoder> CreateFakeVideoDecoder() { |
| 247 std::unique_ptr<media::FakeVideoDecoder> fake_decoder( |
| 248 new media::FakeVideoDecoder(0, /* decode_delay */ |
| 249 kMaxParallelVideoDecoderRequests, |
| 250 base::Bind(&OnBytesDecoded))); |
| 251 fake_video_decoder_ = fake_decoder.get(); |
| 252 return fake_decoder; |
| 253 } |
| 165 | 254 |
| 166 base::Lock lock_; | 255 ThreadHelper video_decoder_thread_; |
| 167 base::WaitableEvent idle_waiter_; | |
| 168 }; | 256 }; |
| 169 | 257 |
| 170 TEST_F(RTCVideoDecoderTest, CreateReturnsNullOnUnsupportedCodec) { | 258 TEST_F(RTCVideoDecoderTest, CreateReturnsNullOnUnsupportedCodec) { |
| 259 // VP8 is supported, so |rtc_decoder_| should be non-null. |
| 171 CreateDecoder(webrtc::kVideoCodecVP8); | 260 CreateDecoder(webrtc::kVideoCodecVP8); |
| 172 std::unique_ptr<RTCVideoDecoder> null_rtc_decoder(RTCVideoDecoder::Create( | 261 EXPECT_NE(nullptr, rtc_decoder_.get()); |
| 173 webrtc::kVideoCodecI420, mock_gpu_factories_.get())); | 262 |
| 174 EXPECT_EQ(NULL, null_rtc_decoder.get()); | 263 // I420 is not supported, so |rtc_decoder_| should be null. |
| 264 CreateDecoder(webrtc::kVideoCodecI420); |
| 265 EXPECT_EQ(nullptr, rtc_decoder_.get()); |
| 175 } | 266 } |
| 176 | 267 |
| 177 TEST_P(RTCVideoDecoderTest, CreateAndInitSucceeds) { | 268 TEST_P(RTCVideoDecoderTest, CreateAndInitSucceedsForSupportedCodec) { |
| 178 const webrtc::VideoCodecType codec_type = GetParam(); | 269 const webrtc::VideoCodecType codec_type = GetParam(); |
| 179 CreateDecoder(codec_type); | 270 CreateDecoder(codec_type); |
| 271 ASSERT_NE(nullptr, rtc_decoder_.get()); |
| 180 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); | 272 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); |
| 181 } | 273 } |
| 182 | 274 |
| 183 TEST_F(RTCVideoDecoderTest, InitDecodeReturnsErrorOnFeedbackMode) { | 275 TEST_F(RTCVideoDecoderTest, InitDecodeReturnsErrorOnFeedbackMode) { |
| 184 CreateDecoder(webrtc::kVideoCodecVP8); | 276 CreateDecoder(webrtc::kVideoCodecVP8); |
| 185 codec_.codecSpecific.VP8.feedbackModeOn = true; | 277 codec_.codecSpecific.VP8.feedbackModeOn = true; |
| 186 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, rtc_decoder_->InitDecode(&codec_, 1)); | 278 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, rtc_decoder_->InitDecode(&codec_, 1)); |
| 187 } | 279 } |
| 188 | 280 |
| 189 TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorWithoutInitDecode) { | 281 TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorWithoutInitDecode) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 209 input_image._completeFrame = true; | 301 input_image._completeFrame = true; |
| 210 bool missingFrames = true; | 302 bool missingFrames = true; |
| 211 EXPECT_EQ( | 303 EXPECT_EQ( |
| 212 WEBRTC_VIDEO_CODEC_ERROR, | 304 WEBRTC_VIDEO_CODEC_ERROR, |
| 213 rtc_decoder_->Decode(input_image, missingFrames, nullptr, nullptr, 0)); | 305 rtc_decoder_->Decode(input_image, missingFrames, nullptr, nullptr, 0)); |
| 214 } | 306 } |
| 215 | 307 |
| 216 TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) { | 308 TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) { |
| 217 CreateDecoder(webrtc::kVideoCodecVP8); | 309 CreateDecoder(webrtc::kVideoCodecVP8); |
| 218 Initialize(); | 310 Initialize(); |
| 219 EXPECT_CALL(*mock_vda_, Reset()) | 311 |
| 220 .WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone)); | 312 // TODO(slan): Mock Reset() call here. |
| 313 |
| 221 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); | 314 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); |
| 222 } | 315 } |
| 223 | 316 |
| 224 TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) { | 317 TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) { |
| 225 CreateDecoder(webrtc::kVideoCodecVP8); | 318 CreateDecoder(webrtc::kVideoCodecVP8); |
| 226 EXPECT_CALL(*mock_vda_, Reset()) | |
| 227 .WillRepeatedly(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone)); | |
| 228 Initialize(); | 319 Initialize(); |
| 320 |
| 229 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); | 321 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); |
| 230 Initialize(); | 322 Initialize(); |
| 323 |
| 231 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); | 324 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release()); |
| 232 } | 325 } |
| 233 | 326 |
| 234 TEST_F(RTCVideoDecoderTest, IsBufferAfterReset) { | |
| 235 CreateDecoder(webrtc::kVideoCodecVP8); | |
| 236 EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_INVALID)); | |
| 237 EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, | |
| 238 RTCVideoDecoder::ID_INVALID)); | |
| 239 EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_HALF - 2, | |
| 240 RTCVideoDecoder::ID_HALF + 2)); | |
| 241 EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_HALF + 2, | |
| 242 RTCVideoDecoder::ID_HALF - 2)); | |
| 243 | |
| 244 EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(0, 0)); | |
| 245 EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_LAST)); | |
| 246 EXPECT_FALSE( | |
| 247 rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_HALF - 2)); | |
| 248 EXPECT_TRUE( | |
| 249 rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_HALF + 2)); | |
| 250 | |
| 251 EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, 0)); | |
| 252 EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, | |
| 253 RTCVideoDecoder::ID_HALF - 2)); | |
| 254 EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, | |
| 255 RTCVideoDecoder::ID_HALF + 2)); | |
| 256 EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, | |
| 257 RTCVideoDecoder::ID_LAST)); | |
| 258 } | |
| 259 | |
| 260 TEST_F(RTCVideoDecoderTest, IsFirstBufferAfterReset) { | |
| 261 CreateDecoder(webrtc::kVideoCodecVP8); | |
| 262 EXPECT_TRUE( | |
| 263 rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_INVALID)); | |
| 264 EXPECT_FALSE( | |
| 265 rtc_decoder_->IsFirstBufferAfterReset(1, RTCVideoDecoder::ID_INVALID)); | |
| 266 EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(0, 0)); | |
| 267 EXPECT_TRUE(rtc_decoder_->IsFirstBufferAfterReset(1, 0)); | |
| 268 EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(2, 0)); | |
| 269 | |
| 270 EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(RTCVideoDecoder::ID_HALF, | |
| 271 RTCVideoDecoder::ID_HALF)); | |
| 272 EXPECT_TRUE(rtc_decoder_->IsFirstBufferAfterReset( | |
| 273 RTCVideoDecoder::ID_HALF + 1, RTCVideoDecoder::ID_HALF)); | |
| 274 EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset( | |
| 275 RTCVideoDecoder::ID_HALF + 2, RTCVideoDecoder::ID_HALF)); | |
| 276 | |
| 277 EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(RTCVideoDecoder::ID_LAST, | |
| 278 RTCVideoDecoder::ID_LAST)); | |
| 279 EXPECT_TRUE( | |
| 280 rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_LAST)); | |
| 281 EXPECT_FALSE( | |
| 282 rtc_decoder_->IsFirstBufferAfterReset(1, RTCVideoDecoder::ID_LAST)); | |
| 283 } | |
| 284 | |
| 285 // Tests/Verifies that |rtc_encoder_| drops incoming frames and its error | 327 // Tests/Verifies that |rtc_encoder_| drops incoming frames and its error |
| 286 // counter is increased when in an error condition. | 328 // counter is increased when in an error condition. |
| 287 TEST_P(RTCVideoDecoderTest, GetVDAErrorCounterForTesting) { | 329 TEST_P(RTCVideoDecoderTest, GetDecoderErrorCounterForTesting) { |
| 288 const webrtc::VideoCodecType codec_type = GetParam(); | 330 const webrtc::VideoCodecType codec_type = GetParam(); |
| 289 CreateDecoder(codec_type); | 331 CreateDecoder(codec_type); |
| 290 Initialize(); | 332 Initialize(); |
| 291 | 333 |
| 292 webrtc::EncodedImage input_image; | 334 // Hold the next decode, so that we can trigger an error. |
| 335 ASSERT_NE(nullptr, fake_video_decoder_); |
| 336 video_decoder_task_runner_->PostTask( |
| 337 FROM_HERE, base::Bind(&media::FakeVideoDecoder::HoldDecode, |
| 338 base::Unretained(fake_video_decoder_))); |
| 339 RunUntilIdle(); |
| 340 |
| 341 // Send a valid key frame to the decoder. |
| 342 uint8_t buffer[kVideoFrameWidth * kVideoFrameHeight]; |
| 343 webrtc::EncodedImage input_image(buffer, sizeof(buffer), sizeof(buffer)); |
| 293 input_image._completeFrame = true; | 344 input_image._completeFrame = true; |
| 294 input_image._encodedWidth = kMinResolutionWidth; | 345 input_image._encodedWidth = kVideoFrameWidth; |
| 295 input_image._encodedHeight = kMaxResolutionHeight; | 346 input_image._encodedHeight = kVideoFrameHeight; |
| 347 input_image._frameType = webrtc::kVideoFrameKey; |
| 348 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, |
| 349 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); |
| 350 |
| 351 // Simulate an error from the decoder. This will run the held decode callback |
| 352 // with DECODE_ERROR. |
| 353 video_decoder_task_runner_->PostTask( |
| 354 FROM_HERE, base::Bind(&media::FakeVideoDecoder::SimulateError, |
| 355 base::Unretained(fake_video_decoder_))); |
| 356 RunUntilIdle(); |
| 357 EXPECT_EQ(1, rtc_decoder_->GetDecoderErrorCounterForTesting()); |
| 358 |
| 359 // We will continue to push this frame, but each one will not be a key frame. |
| 296 input_image._frameType = webrtc::kVideoFrameDelta; | 360 input_image._frameType = webrtc::kVideoFrameDelta; |
| 297 input_image._length = kMinResolutionWidth * kMaxResolutionHeight; | 361 |
| 362 // This should trigger a Reset() on the decoder. |
| 298 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, | 363 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, |
| 299 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); | 364 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); |
| 300 RunUntilIdle(); | 365 EXPECT_EQ(1, rtc_decoder_->GetDecoderErrorCounterForTesting()); |
| 301 | |
| 302 // Notify the decoder about a platform error. | |
| 303 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 304 RunUntilIdle(); | |
| 305 EXPECT_EQ(1, rtc_decoder_->GetVDAErrorCounterForTesting()); | |
| 306 | |
| 307 // Expect decode call to reset decoder, and set up a new VDA to track it. | |
| 308 SetUpResetVDA(); | |
| 309 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, | |
| 310 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); | |
| 311 EXPECT_EQ(1, rtc_decoder_->GetVDAErrorCounterForTesting()); | |
| 312 | 366 |
| 313 // Decoder expects a keyframe after reset, so drops any other frames. However, | 367 // Decoder expects a keyframe after reset, so drops any other frames. However, |
| 314 // we should still increment the error counter. | 368 // we should still increment the error counter. |
| 315 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, | 369 EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, |
| 316 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); | 370 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); |
| 317 EXPECT_EQ(2, rtc_decoder_->GetVDAErrorCounterForTesting()); | 371 EXPECT_EQ(2, rtc_decoder_->GetDecoderErrorCounterForTesting()); |
| 372 } |
| 373 |
| 374 // Verify that there are never more than GetMaxDecodeRequests() issued to the |
| 375 // decoder concurrently. |
| 376 TEST_F(RTCVideoDecoderTest, MaxNumberOfRequestsIsNotExceeded) { |
| 377 CreateDecoder(webrtc::kVideoCodecVP8); |
| 378 Initialize(); |
| 379 |
| 380 // Hold incoming decode requests. |
| 381 ASSERT_NE(nullptr, fake_video_decoder_); |
| 382 video_decoder_task_runner_->PostTask( |
| 383 FROM_HERE, base::Bind(&media::FakeVideoDecoder::HoldDecode, |
| 384 base::Unretained(fake_video_decoder_))); |
| 385 RunUntilIdle(); |
| 386 |
| 387 uint8_t buffer[kVideoFrameWidth * kVideoFrameHeight]; |
| 388 webrtc::EncodedImage input_image(buffer, sizeof(buffer), sizeof(buffer)); |
| 389 input_image._completeFrame = true; |
| 390 input_image._encodedWidth = kVideoFrameWidth; |
| 391 input_image._encodedHeight = kVideoFrameHeight; |
| 392 input_image._frameType = webrtc::kVideoFrameKey; |
| 393 |
| 394 // Determine how many concurrent decode requests the remote decoder can |
| 395 // handle, and send more than that into the RTCVideoDecoder. Frames |
| 396 // should buffer until the remote decoder calls back. |
| 397 const int numRequests = fake_video_decoder_->GetMaxDecodeRequests(); |
| 398 EXPECT_EQ(kMaxParallelVideoDecoderRequests, numRequests); |
| 399 for (int i = 0; i < numRequests + 1; ++i) { |
| 400 if (i > 0) |
| 401 input_image._frameType = webrtc::kVideoFrameDelta; |
| 402 EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, |
| 403 rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0)); |
| 404 } |
| 405 RunUntilIdle(); |
| 406 |
| 407 // Release the pending decodes. |
| 408 video_decoder_task_runner_->PostTask( |
| 409 FROM_HERE, base::Bind(&media::FakeVideoDecoder::SatisfyDecode, |
| 410 base::Unretained(fake_video_decoder_))); |
| 411 RunUntilIdle(); |
| 318 } | 412 } |
| 319 | 413 |
| 320 INSTANTIATE_TEST_CASE_P(CodecProfiles, | 414 INSTANTIATE_TEST_CASE_P(CodecProfiles, |
| 321 RTCVideoDecoderTest, | 415 RTCVideoDecoderTest, |
| 322 Values(webrtc::kVideoCodecVP8, | 416 Values(webrtc::kVideoCodecVP8, |
| 417 webrtc::kVideoCodecVP9, |
| 323 webrtc::kVideoCodecH264)); | 418 webrtc::kVideoCodecH264)); |
| 324 | 419 |
| 325 } // content | 420 } // content |
| OLD | NEW |