| Index: content/renderer/media/gpu/rtc_video_decoder_unittest.cc
|
| diff --git a/content/renderer/media/gpu/rtc_video_decoder_unittest.cc b/content/renderer/media/gpu/rtc_video_decoder_unittest.cc
|
| index 151e8fcb285357374854cc834e384f08a1c02c9f..2310cb858a7c052bc4799e43db68342925ef06ae 100644
|
| --- a/content/renderer/media/gpu/rtc_video_decoder_unittest.cc
|
| +++ b/content/renderer/media/gpu/rtc_video_decoder_unittest.cc
|
| @@ -7,12 +7,16 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/location.h"
|
| +#include "base/run_loop.h"
|
| #include "base/single_thread_task_runner.h"
|
| #include "base/synchronization/waitable_event.h"
|
| +#include "base/test/test_simple_task_runner.h"
|
| #include "base/threading/thread.h"
|
| #include "base/threading/thread_task_runner_handle.h"
|
| #include "content/renderer/media/gpu/rtc_video_decoder.h"
|
| #include "media/base/gmock_callback_support.h"
|
| +#include "media/base/mock_filters.h"
|
| +#include "media/filters/fake_video_decoder.h"
|
| #include "media/renderers/mock_gpu_video_accelerator_factories.h"
|
| #include "media/video/mock_video_decode_accelerator.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
| @@ -33,10 +37,130 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -static const int kMinResolutionWidth = 16;
|
| -static const int kMinResolutionHeight = 16;
|
| -static const int kMaxResolutionWidth = 1920;
|
| -static const int kMaxResolutionHeight = 1088;
|
| +const int kVideoFrameWidth = 1280;
|
| +const int kVideoFrameHeight = 720;
|
| +
|
| +const int kMaxParallelVideoDecoderRequests = 4;
|
| +
|
| +void OnBytesDecoded(int numbytes) {}
|
| +
|
| +// This class creates and manages a thread to which tasks may be posted, and
|
| +// which exposes BlockUntilTaskRunnerIdle(), allowing callers on other threads
|
| +// to block until the thread this idle.
|
| +// This is accomplished via two task runners on the underlying thread. Tasks may
|
| +// be posted to the one returned by task_runner(), while another, which is
|
| +// private to the implementation, pumps and controls the first. To the caller,
|
| +// this interface appears as a Thread on which one can block.
|
| +class ThreadHelper {
|
| + public:
|
| + explicit ThreadHelper(const std::string& thread_name);
|
| + ~ThreadHelper();
|
| +
|
| + // Start the underlying thread. Return true on success. After this call,
|
| + // task_runner() will return a valid base::SingleThreadTaskRunner.
|
| + bool Start();
|
| +
|
| + // Return true if the underlying thread is running, false otherwise.
|
| + bool IsRunning();
|
| +
|
| + // Block the calling thread until task_runner() is idle. If tasks are posted
|
| + // to the task_runner() during execution of other tasks, those will run too.
|
| + // Thus, this call may never return.
|
| + void BlockUntilTaskRunnerIdle();
|
| +
|
| + // Stop the underlying thread. task_runner() will return nullptr after this
|
| + // is called.
|
| + void Stop();
|
| +
|
| + // Tasks may be posted to this task_runner. Will when this is not running,
|
| + // returns nullptr.
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
|
| + return postable_task_runner_;
|
| + }
|
| +
|
| + private:
|
| + void StartPumping();
|
| + void Pump();
|
| + void StopPumping();
|
| +
|
| + base::Thread thread_;
|
| + base::WaitableEvent waiter_;
|
| + bool pumping_ = false;
|
| + scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_;
|
| + scoped_refptr<base::TestSimpleTaskRunner> postable_task_runner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadHelper);
|
| +};
|
| +
|
| +ThreadHelper::ThreadHelper(const std::string& thread_name)
|
| + : thread_(thread_name),
|
| + waiter_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED) {}
|
| +
|
| +ThreadHelper::~ThreadHelper() {
|
| + if (thread_.IsRunning())
|
| + Stop();
|
| +}
|
| +
|
| +bool ThreadHelper::Start() {
|
| + if (!thread_.Start())
|
| + return false;
|
| + control_task_runner_ = thread_.task_runner();
|
| + control_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ThreadHelper::StartPumping, base::Unretained(this)));
|
| + waiter_.Wait();
|
| + return true;
|
| +}
|
| +
|
| +bool ThreadHelper::IsRunning() {
|
| + return thread_.IsRunning();
|
| +}
|
| +
|
| +void ThreadHelper::BlockUntilTaskRunnerIdle() {
|
| + waiter_.Wait();
|
| +}
|
| +
|
| +void ThreadHelper::Stop() {
|
| + BlockUntilTaskRunnerIdle();
|
| + control_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ThreadHelper::StopPumping, base::Unretained(this)));
|
| + waiter_.Wait();
|
| + thread_.Stop();
|
| +}
|
| +
|
| +// Start pumping the task_runner(), so that it executes tasks. Called in
|
| +// Start(). Must be called on the |control_task_runner_|.
|
| +void ThreadHelper::StartPumping() {
|
| + DCHECK(!postable_task_runner_);
|
| + postable_task_runner_ = new base::TestSimpleTaskRunner();
|
| + thread_.message_loop()->SetTaskRunner(postable_task_runner_);
|
| +
|
| + // Start pumping.
|
| + pumping_ = true;
|
| + Pump();
|
| +}
|
| +
|
| +// Recurring task which causes tasks posted to task_runner() to run.
|
| +// BlockUntilTaskRunnerIdle() will block the calling thread while this is
|
| +// running. Must be called on the |control_task_runner_|.
|
| +void ThreadHelper::Pump() {
|
| + postable_task_runner_->RunUntilIdle();
|
| + if (pumping_) {
|
| + control_task_runner_->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&ThreadHelper::Pump, base::Unretained(this)),
|
| + base::TimeDelta::FromMilliseconds(10));
|
| + }
|
| + waiter_.Signal();
|
| +}
|
| +
|
| +// Stop pumping the task_runner(). Called in Stop(). Must be called on the
|
| +// control_task_runner_|.
|
| +void ThreadHelper::StopPumping() {
|
| + thread_.message_loop()->SetTaskRunner(control_task_runner_);
|
| + pumping_ = false;
|
| +}
|
|
|
| } // namespace
|
|
|
| @@ -46,40 +170,17 @@ class RTCVideoDecoderTest
|
| webrtc::DecodedImageCallback {
|
| public:
|
| RTCVideoDecoderTest()
|
| - : mock_gpu_factories_(
|
| - new media::MockGpuVideoAcceleratorFactories(nullptr)),
|
| - vda_thread_("vda_thread"),
|
| - idle_waiter_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
|
| - base::WaitableEvent::InitialState::NOT_SIGNALED) {
|
| + : fake_video_decoder_(nullptr), video_decoder_thread_("decoder_thread") {
|
| memset(&codec_, 0, sizeof(codec_));
|
| }
|
|
|
| void SetUp() override {
|
| - ASSERT_TRUE(vda_thread_.Start());
|
| - vda_task_runner_ = vda_thread_.task_runner();
|
| - mock_vda_ = new media::MockVideoDecodeAccelerator;
|
| -
|
| - media::VideoDecodeAccelerator::SupportedProfile supported_profile;
|
| - supported_profile.min_resolution.SetSize(kMinResolutionWidth,
|
| - kMinResolutionHeight);
|
| - supported_profile.max_resolution.SetSize(kMaxResolutionWidth,
|
| - kMaxResolutionHeight);
|
| - supported_profile.profile = media::H264PROFILE_MAIN;
|
| - capabilities_.supported_profiles.push_back(supported_profile);
|
| - supported_profile.profile = media::VP8PROFILE_ANY;
|
| - capabilities_.supported_profiles.push_back(supported_profile);
|
| -
|
| - EXPECT_CALL(*mock_gpu_factories_.get(), GetTaskRunner())
|
| - .WillRepeatedly(Return(vda_task_runner_));
|
| - EXPECT_CALL(*mock_gpu_factories_.get(),
|
| - GetVideoDecodeAcceleratorCapabilities())
|
| - .WillRepeatedly(Return(capabilities_));
|
| - EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoDecodeAccelerator())
|
| - .WillRepeatedly(Return(mock_vda_));
|
| - EXPECT_CALL(*mock_vda_, Initialize(_, _))
|
| - .Times(1)
|
| - .WillRepeatedly(Return(true));
|
| - EXPECT_CALL(*mock_vda_, Destroy()).Times(1);
|
| + ASSERT_TRUE(video_decoder_thread_.Start());
|
| + video_decoder_task_runner_ = video_decoder_thread_.task_runner();
|
| +
|
| + // If any frames are passed in, they should match these values.
|
| + codec_.width = kVideoFrameWidth;
|
| + codec_.height = kVideoFrameWidth;
|
|
|
| #if defined(OS_WIN)
|
| base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
| @@ -89,94 +190,85 @@ class RTCVideoDecoderTest
|
|
|
| void TearDown() override {
|
| DVLOG(2) << "TearDown";
|
| - EXPECT_TRUE(vda_thread_.IsRunning());
|
| - RunUntilIdle(); // Wait until all callbascks complete.
|
| - vda_task_runner_->DeleteSoon(FROM_HERE, rtc_decoder_.release());
|
| + EXPECT_TRUE(video_decoder_thread_.IsRunning());
|
| + RTCVideoDecoder::Destroy(rtc_decoder_.release(),
|
| + video_decoder_task_runner_);
|
| // Make sure the decoder is released before stopping the thread.
|
| RunUntilIdle();
|
| - vda_thread_.Stop();
|
| + video_decoder_thread_.Stop();
|
| }
|
|
|
| int32_t Decoded(webrtc::VideoFrame& decoded_image) override {
|
| DVLOG(2) << "Decoded";
|
| - EXPECT_EQ(vda_task_runner_, base::ThreadTaskRunnerHandle::Get());
|
| + EXPECT_EQ(video_decoder_task_runner_, base::ThreadTaskRunnerHandle::Get());
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| void CreateDecoder(webrtc::VideoCodecType codec_type) {
|
| DVLOG(2) << "CreateDecoder";
|
| +
|
| + // If a decoder already exists, delete the old one.
|
| + if (rtc_decoder_) {
|
| + RTCVideoDecoder::Destroy(rtc_decoder_.release(),
|
| + video_decoder_task_runner_);
|
| + }
|
| +
|
| codec_.codecType = codec_type;
|
| - rtc_decoder_ =
|
| - RTCVideoDecoder::Create(codec_type, mock_gpu_factories_.get());
|
| + rtc_decoder_ = RTCVideoDecoder::Create(
|
| + codec_type, base::Bind(&RTCVideoDecoderTest::CreateFakeVideoDecoder,
|
| + base::Unretained(this)),
|
| + video_decoder_task_runner_);
|
| + RunUntilIdle();
|
| }
|
|
|
| void Initialize() {
|
| DVLOG(2) << "Initialize";
|
| + DCHECK(rtc_decoder_);
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1));
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
| rtc_decoder_->RegisterDecodeCompleteCallback(this));
|
| }
|
|
|
| - void NotifyResetDone() {
|
| - DVLOG(2) << "NotifyResetDone";
|
| - vda_task_runner_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&RTCVideoDecoder::NotifyResetDone,
|
| - base::Unretained(rtc_decoder_.get())));
|
| - }
|
| -
|
| - void NotifyError(media::VideoDecodeAccelerator::Error error) {
|
| - DVLOG(2) << "NotifyError";
|
| - vda_task_runner_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&RTCVideoDecoder::NotifyError,
|
| - base::Unretained(rtc_decoder_.get()), error));
|
| - }
|
| -
|
| + // This function blocks the calling thread until |video_decoder_thread_| is
|
| + // idle.
|
| void RunUntilIdle() {
|
| DVLOG(2) << "RunUntilIdle";
|
| - vda_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&base::WaitableEvent::Signal,
|
| - base::Unretained(&idle_waiter_)));
|
| - idle_waiter_.Wait();
|
| - }
|
| -
|
| - void SetUpResetVDA() {
|
| - mock_vda_after_reset_ = new media::MockVideoDecodeAccelerator;
|
| - EXPECT_CALL(*mock_gpu_factories_.get(), DoCreateVideoDecodeAccelerator())
|
| - .WillRepeatedly(Return(mock_vda_after_reset_));
|
| - EXPECT_CALL(*mock_vda_after_reset_, Initialize(_, _))
|
| - .Times(1)
|
| - .WillRepeatedly(Return(true));
|
| - EXPECT_CALL(*mock_vda_after_reset_, Destroy()).Times(1);
|
| + video_decoder_thread_.BlockUntilTaskRunnerIdle();
|
| }
|
|
|
| protected:
|
| - std::unique_ptr<media::MockGpuVideoAcceleratorFactories> mock_gpu_factories_;
|
| - media::MockVideoDecodeAccelerator* mock_vda_;
|
| - media::MockVideoDecodeAccelerator* mock_vda_after_reset_;
|
| + media::FakeVideoDecoder* fake_video_decoder_;
|
| std::unique_ptr<RTCVideoDecoder> rtc_decoder_;
|
| webrtc::VideoCodec codec_;
|
| - base::Thread vda_thread_;
|
| - media::VideoDecodeAccelerator::Capabilities capabilities_;
|
| + scoped_refptr<base::SingleThreadTaskRunner> video_decoder_task_runner_;
|
|
|
| private:
|
| - scoped_refptr<base::SingleThreadTaskRunner> vda_task_runner_;
|
| + std::unique_ptr<media::VideoDecoder> CreateFakeVideoDecoder() {
|
| + std::unique_ptr<media::FakeVideoDecoder> fake_decoder(
|
| + new media::FakeVideoDecoder(0, /* decode_delay */
|
| + kMaxParallelVideoDecoderRequests,
|
| + base::Bind(&OnBytesDecoded)));
|
| + fake_video_decoder_ = fake_decoder.get();
|
| + return fake_decoder;
|
| + }
|
|
|
| - base::Lock lock_;
|
| - base::WaitableEvent idle_waiter_;
|
| + ThreadHelper video_decoder_thread_;
|
| };
|
|
|
| TEST_F(RTCVideoDecoderTest, CreateReturnsNullOnUnsupportedCodec) {
|
| + // VP8 is supported, so |rtc_decoder_| should be non-null.
|
| CreateDecoder(webrtc::kVideoCodecVP8);
|
| - std::unique_ptr<RTCVideoDecoder> null_rtc_decoder(RTCVideoDecoder::Create(
|
| - webrtc::kVideoCodecI420, mock_gpu_factories_.get()));
|
| - EXPECT_EQ(NULL, null_rtc_decoder.get());
|
| + EXPECT_NE(nullptr, rtc_decoder_.get());
|
| +
|
| + // I420 is not supported, so |rtc_decoder_| should be null.
|
| + CreateDecoder(webrtc::kVideoCodecI420);
|
| + EXPECT_EQ(nullptr, rtc_decoder_.get());
|
| }
|
|
|
| -TEST_P(RTCVideoDecoderTest, CreateAndInitSucceeds) {
|
| +TEST_P(RTCVideoDecoderTest, CreateAndInitSucceedsForSupportedCodec) {
|
| const webrtc::VideoCodecType codec_type = GetParam();
|
| CreateDecoder(codec_type);
|
| + ASSERT_NE(nullptr, rtc_decoder_.get());
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1));
|
| }
|
|
|
| @@ -216,110 +308,113 @@ TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnMissingFrames) {
|
| TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) {
|
| CreateDecoder(webrtc::kVideoCodecVP8);
|
| Initialize();
|
| - EXPECT_CALL(*mock_vda_, Reset())
|
| - .WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone));
|
| +
|
| + // TODO(slan): Mock Reset() call here.
|
| +
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release());
|
| }
|
|
|
| TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) {
|
| CreateDecoder(webrtc::kVideoCodecVP8);
|
| - EXPECT_CALL(*mock_vda_, Reset())
|
| - .WillRepeatedly(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone));
|
| Initialize();
|
| +
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release());
|
| Initialize();
|
| - EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release());
|
| -}
|
| -
|
| -TEST_F(RTCVideoDecoderTest, IsBufferAfterReset) {
|
| - CreateDecoder(webrtc::kVideoCodecVP8);
|
| - EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_INVALID));
|
| - EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST,
|
| - RTCVideoDecoder::ID_INVALID));
|
| - EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_HALF - 2,
|
| - RTCVideoDecoder::ID_HALF + 2));
|
| - EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_HALF + 2,
|
| - RTCVideoDecoder::ID_HALF - 2));
|
| -
|
| - EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(0, 0));
|
| - EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_LAST));
|
| - EXPECT_FALSE(
|
| - rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_HALF - 2));
|
| - EXPECT_TRUE(
|
| - rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_HALF + 2));
|
| -
|
| - EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, 0));
|
| - EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST,
|
| - RTCVideoDecoder::ID_HALF - 2));
|
| - EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST,
|
| - RTCVideoDecoder::ID_HALF + 2));
|
| - EXPECT_FALSE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST,
|
| - RTCVideoDecoder::ID_LAST));
|
| -}
|
|
|
| -TEST_F(RTCVideoDecoderTest, IsFirstBufferAfterReset) {
|
| - CreateDecoder(webrtc::kVideoCodecVP8);
|
| - EXPECT_TRUE(
|
| - rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_INVALID));
|
| - EXPECT_FALSE(
|
| - rtc_decoder_->IsFirstBufferAfterReset(1, RTCVideoDecoder::ID_INVALID));
|
| - EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(0, 0));
|
| - EXPECT_TRUE(rtc_decoder_->IsFirstBufferAfterReset(1, 0));
|
| - EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(2, 0));
|
| -
|
| - EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(RTCVideoDecoder::ID_HALF,
|
| - RTCVideoDecoder::ID_HALF));
|
| - EXPECT_TRUE(rtc_decoder_->IsFirstBufferAfterReset(
|
| - RTCVideoDecoder::ID_HALF + 1, RTCVideoDecoder::ID_HALF));
|
| - EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(
|
| - RTCVideoDecoder::ID_HALF + 2, RTCVideoDecoder::ID_HALF));
|
| -
|
| - EXPECT_FALSE(rtc_decoder_->IsFirstBufferAfterReset(RTCVideoDecoder::ID_LAST,
|
| - RTCVideoDecoder::ID_LAST));
|
| - EXPECT_TRUE(
|
| - rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_LAST));
|
| - EXPECT_FALSE(
|
| - rtc_decoder_->IsFirstBufferAfterReset(1, RTCVideoDecoder::ID_LAST));
|
| + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->Release());
|
| }
|
|
|
| // Tests/Verifies that |rtc_encoder_| drops incoming frames and its error
|
| // counter is increased when in an error condition.
|
| -TEST_P(RTCVideoDecoderTest, GetVDAErrorCounterForTesting) {
|
| +TEST_P(RTCVideoDecoderTest, GetDecoderErrorCounterForTesting) {
|
| const webrtc::VideoCodecType codec_type = GetParam();
|
| CreateDecoder(codec_type);
|
| Initialize();
|
|
|
| - webrtc::EncodedImage input_image;
|
| + // Hold the next decode, so that we can trigger an error.
|
| + ASSERT_NE(nullptr, fake_video_decoder_);
|
| + video_decoder_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&media::FakeVideoDecoder::HoldDecode,
|
| + base::Unretained(fake_video_decoder_)));
|
| + RunUntilIdle();
|
| +
|
| + // Send a valid key frame to the decoder.
|
| + uint8_t buffer[kVideoFrameWidth * kVideoFrameHeight];
|
| + webrtc::EncodedImage input_image(buffer, sizeof(buffer), sizeof(buffer));
|
| input_image._completeFrame = true;
|
| - input_image._encodedWidth = kMinResolutionWidth;
|
| - input_image._encodedHeight = kMaxResolutionHeight;
|
| - input_image._frameType = webrtc::kVideoFrameDelta;
|
| - input_image._length = kMinResolutionWidth * kMaxResolutionHeight;
|
| - EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
|
| + input_image._encodedWidth = kVideoFrameWidth;
|
| + input_image._encodedHeight = kVideoFrameHeight;
|
| + input_image._frameType = webrtc::kVideoFrameKey;
|
| + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
| rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
|
| - RunUntilIdle();
|
|
|
| - // Notify the decoder about a platform error.
|
| - NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
|
| + // Simulate an error from the decoder. This will run the held decode callback
|
| + // with DECODE_ERROR.
|
| + video_decoder_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&media::FakeVideoDecoder::SimulateError,
|
| + base::Unretained(fake_video_decoder_)));
|
| RunUntilIdle();
|
| - EXPECT_EQ(1, rtc_decoder_->GetVDAErrorCounterForTesting());
|
| + EXPECT_EQ(1, rtc_decoder_->GetDecoderErrorCounterForTesting());
|
| +
|
| + // We will continue to push this frame, but each one will not be a key frame.
|
| + input_image._frameType = webrtc::kVideoFrameDelta;
|
|
|
| - // Expect decode call to reset decoder, and set up a new VDA to track it.
|
| - SetUpResetVDA();
|
| + // This should trigger a Reset() on the decoder.
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
|
| rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
|
| - EXPECT_EQ(1, rtc_decoder_->GetVDAErrorCounterForTesting());
|
| + EXPECT_EQ(1, rtc_decoder_->GetDecoderErrorCounterForTesting());
|
|
|
| // Decoder expects a keyframe after reset, so drops any other frames. However,
|
| // we should still increment the error counter.
|
| EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR,
|
| rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
|
| - EXPECT_EQ(2, rtc_decoder_->GetVDAErrorCounterForTesting());
|
| + EXPECT_EQ(2, rtc_decoder_->GetDecoderErrorCounterForTesting());
|
| +}
|
| +
|
| +// Verify that there are never more than GetMaxDecodeRequests() issued to the
|
| +// decoder concurrently.
|
| +TEST_F(RTCVideoDecoderTest, MaxNumberOfRequestsIsNotExceeded) {
|
| + CreateDecoder(webrtc::kVideoCodecVP8);
|
| + Initialize();
|
| +
|
| + // Hold incoming decode requests.
|
| + ASSERT_NE(nullptr, fake_video_decoder_);
|
| + video_decoder_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&media::FakeVideoDecoder::HoldDecode,
|
| + base::Unretained(fake_video_decoder_)));
|
| + RunUntilIdle();
|
| +
|
| + uint8_t buffer[kVideoFrameWidth * kVideoFrameHeight];
|
| + webrtc::EncodedImage input_image(buffer, sizeof(buffer), sizeof(buffer));
|
| + input_image._completeFrame = true;
|
| + input_image._encodedWidth = kVideoFrameWidth;
|
| + input_image._encodedHeight = kVideoFrameHeight;
|
| + input_image._frameType = webrtc::kVideoFrameKey;
|
| +
|
| + // Determine how many concurrent decode requests the remote decoder can
|
| + // handle, and send more than that into the RTCVideoDecoder. Frames
|
| + // should buffer until the remote decoder calls back.
|
| + const int numRequests = fake_video_decoder_->GetMaxDecodeRequests();
|
| + EXPECT_EQ(kMaxParallelVideoDecoderRequests, numRequests);
|
| + for (int i = 0; i < numRequests + 1; ++i) {
|
| + if (i > 0)
|
| + input_image._frameType = webrtc::kVideoFrameDelta;
|
| + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
| + rtc_decoder_->Decode(input_image, false, nullptr, nullptr, 0));
|
| + }
|
| + RunUntilIdle();
|
| +
|
| + // Release the pending decodes.
|
| + video_decoder_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&media::FakeVideoDecoder::SatisfyDecode,
|
| + base::Unretained(fake_video_decoder_)));
|
| + RunUntilIdle();
|
| }
|
|
|
| INSTANTIATE_TEST_CASE_P(CodecProfiles,
|
| RTCVideoDecoderTest,
|
| Values(webrtc::kVideoCodecVP8,
|
| + webrtc::kVideoCodecVP9,
|
| webrtc::kVideoCodecH264));
|
|
|
| } // content
|
|
|