diff --git a/video/BUILD.gn b/video/BUILD.gn index 8cfbc94efb..daa3a57764 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -564,11 +564,13 @@ if (rtc_include_tests) { "video_receive_stream_unittest.cc", "video_send_stream_impl_unittest.cc", "video_send_stream_tests.cc", + "video_stream_decoder_impl_unittest.cc", "video_stream_encoder_unittest.cc", ] deps = [ ":video", ":video_mocks", + ":video_stream_decoder_impl", ":video_stream_encoder_impl", "../api:create_frame_generator", "../api:fake_frame_decryptor", diff --git a/video/video_stream_decoder_impl.cc b/video/video_stream_decoder_impl.cc index 0477be0c54..1e11d38050 100644 --- a/video/video_stream_decoder_impl.cc +++ b/video/video_stream_decoder_impl.cc @@ -24,48 +24,41 @@ VideoStreamDecoderImpl::VideoStreamDecoderImpl( VideoDecoderFactory* decoder_factory, TaskQueueFactory* task_queue_factory, std::map> decoder_settings) - : callbacks_(callbacks), + : timing_(Clock::GetRealTimeClock()), + decode_callbacks_(this), + next_frame_timestamps_index_(0), + callbacks_(callbacks), + keyframe_required_(true), decoder_factory_(decoder_factory), decoder_settings_(std::move(decoder_settings)), + shut_down_(false), + frame_buffer_(Clock::GetRealTimeClock(), &timing_, nullptr), bookkeeping_queue_(task_queue_factory->CreateTaskQueue( "video_stream_decoder_bookkeeping_queue", TaskQueueFactory::Priority::NORMAL)), - decode_thread_(&DecodeLoop, - this, - "video_stream_decoder_decode_thread", - rtc::kHighestPriority), - timing_(Clock::GetRealTimeClock()), - frame_buffer_(Clock::GetRealTimeClock(), &timing_, nullptr), - next_frame_timestamps_index_(0) { + decode_queue_(task_queue_factory->CreateTaskQueue( + "video_stream_decoder_decode_queue", + TaskQueueFactory::Priority::NORMAL)) { frame_timestamps_.fill({-1, -1, -1}); - decode_thread_.Start(); + bookkeeping_queue_.PostTask([this]() { + RTC_DCHECK_RUN_ON(&bookkeeping_queue_); + StartNextDecode(); + }); } VideoStreamDecoderImpl::~VideoStreamDecoderImpl() { - frame_buffer_.Stop(); - decode_thread_.Stop(); + rtc::CritScope lock(&shut_down_crit_); + shut_down_ = true; } void VideoStreamDecoderImpl::OnFrame( std::unique_ptr frame) { if (!bookkeeping_queue_.IsCurrent()) { - struct OnFrameTask : QueuedTask { - OnFrameTask(std::unique_ptr frame, - VideoStreamDecoderImpl* video_stream_decoder) - : frame_(std::move(frame)), - video_stream_decoder_(video_stream_decoder) {} + bookkeeping_queue_.PostTask([this, frame = std::move(frame)]() mutable { + OnFrame(std::move(frame)); + return true; + }); - bool Run() override { - video_stream_decoder_->OnFrame(std::move(frame_)); - return true; - } - - std::unique_ptr frame_; - VideoStreamDecoderImpl* video_stream_decoder_; - }; - - bookkeeping_queue_.PostTask( - std::make_unique(std::move(frame), this)); return; } @@ -120,7 +113,8 @@ VideoDecoder* VideoStreamDecoderImpl::GetDecoder(int payload_type) { return nullptr; } - int32_t register_result = decoder->RegisterDecodeCompleteCallback(this); + int32_t register_result = + decoder->RegisterDecodeCompleteCallback(&decode_callbacks_); if (register_result != WEBRTC_VIDEO_CODEC_OK) { RTC_LOG(LS_WARNING) << "Failed to register decode callback."; return nullptr; @@ -131,102 +125,113 @@ VideoDecoder* VideoStreamDecoderImpl::GetDecoder(int payload_type) { return decoder_.get(); } -// static -void VideoStreamDecoderImpl::DecodeLoop(void* ptr) { - // TODO(philipel): Remove this and use rtc::Event::kForever when it's - // supported by the |frame_buffer_|. - static constexpr int kForever = 100000000; +void VideoStreamDecoderImpl::SaveFrameTimestamps( + const video_coding::EncodedFrame& frame) { + FrameTimestamps* frame_timestamps = + &frame_timestamps_[next_frame_timestamps_index_]; + frame_timestamps->timestamp = frame.Timestamp(); + frame_timestamps->decode_start_time_ms = rtc::TimeMillis(); + frame_timestamps->render_time_us = frame.RenderTimeMs() * 1000; - int max_wait_time_ms = kForever; - bool keyframe_required = true; - auto* vs_decoder = static_cast(ptr); - while (true) { - DecodeResult decode_result = - vs_decoder->DecodeNextFrame(max_wait_time_ms, keyframe_required); + next_frame_timestamps_index_ = + Add(next_frame_timestamps_index_, 1); +} - switch (decode_result) { - case kOk: { - max_wait_time_ms = kForever; - keyframe_required = false; - break; - } - case kDecodeFailure: { - max_wait_time_ms = 0; - keyframe_required = true; - break; - } - case kNoFrame: { - max_wait_time_ms = kForever; - // If we end up here it means that we got a decoding error and there is - // no keyframe available in the |frame_buffer_|. - vs_decoder->bookkeeping_queue_.PostTask([vs_decoder]() { - RTC_DCHECK_RUN_ON(&vs_decoder->bookkeeping_queue_); - vs_decoder->callbacks_->OnNonDecodableState(); - }); - break; - } - case kNoDecoder: { - max_wait_time_ms = kForever; - break; - } - case kShutdown: { +void VideoStreamDecoderImpl::StartNextDecode() { + int64_t max_wait_time = keyframe_required_ ? 200 : 3000; + + frame_buffer_.NextFrame( + max_wait_time, keyframe_required_, &bookkeeping_queue_, + [this](std::unique_ptr frame, + video_coding::FrameBuffer::ReturnReason res) mutable { + RTC_DCHECK_RUN_ON(&bookkeeping_queue_); + OnNextFrameCallback(std::move(frame), res); + }); +} + +void VideoStreamDecoderImpl::OnNextFrameCallback( + std::unique_ptr frame, + video_coding::FrameBuffer::ReturnReason result) { + switch (result) { + case video_coding::FrameBuffer::kFrameFound: { + RTC_DCHECK(frame); + SaveFrameTimestamps(*frame); + + rtc::CritScope lock(&shut_down_crit_); + if (shut_down_) { return; } + + decode_queue_.PostTask([this, frame = std::move(frame)]() mutable { + RTC_DCHECK_RUN_ON(&decode_queue_); + DecodeResult decode_result = DecodeFrame(std::move(frame)); + bookkeeping_queue_.PostTask([this, decode_result]() { + RTC_DCHECK_RUN_ON(&bookkeeping_queue_); + switch (decode_result) { + case kOk: { + keyframe_required_ = false; + break; + } + case kOkRequestKeyframe: { + callbacks_->OnNonDecodableState(); + keyframe_required_ = false; + break; + } + case kDecodeFailure: { + callbacks_->OnNonDecodableState(); + keyframe_required_ = true; + break; + } + } + StartNextDecode(); + }); + }); + break; + } + case video_coding::FrameBuffer::kTimeout: { + callbacks_->OnNonDecodableState(); + // The |frame_buffer_| requires the frame callback function to complete + // before NextFrame is called again. For this reason we call + // StartNextDecode in a later task to allow this task to complete first. + bookkeeping_queue_.PostTask([this]() { + RTC_DCHECK_RUN_ON(&bookkeeping_queue_); + StartNextDecode(); + }); + break; + } + case video_coding::FrameBuffer::kStopped: { + // We are shutting down, do nothing. + break; } } } -VideoStreamDecoderImpl::DecodeResult VideoStreamDecoderImpl::DecodeNextFrame( - int max_wait_time_ms, - bool keyframe_required) { - std::unique_ptr frame; - video_coding::FrameBuffer::ReturnReason res = - frame_buffer_.NextFrame(max_wait_time_ms, &frame, keyframe_required); +VideoStreamDecoderImpl::DecodeResult VideoStreamDecoderImpl::DecodeFrame( + std::unique_ptr frame) { + RTC_DCHECK(frame); - if (res == video_coding::FrameBuffer::ReturnReason::kStopped) - return kShutdown; - - if (frame) { - VideoDecoder* decoder = GetDecoder(frame->PayloadType()); - if (!decoder) { - RTC_LOG(LS_WARNING) << "Failed to get decoder, dropping frame (" - << frame->id.picture_id << ":" - << frame->id.spatial_layer << ")."; - return kNoDecoder; - } - - int64_t decode_start_time_ms = rtc::TimeMillis(); - int64_t timestamp = frame->Timestamp(); - int64_t render_time_us = frame->RenderTimeMs() * 1000; - bookkeeping_queue_.PostTask( - [this, decode_start_time_ms, timestamp, render_time_us]() { - RTC_DCHECK_RUN_ON(&bookkeeping_queue_); - // Saving decode start time this way wont work if we decode spatial - // layers sequentially. - FrameTimestamps* frame_timestamps = - &frame_timestamps_[next_frame_timestamps_index_]; - frame_timestamps->timestamp = timestamp; - frame_timestamps->decode_start_time_ms = decode_start_time_ms; - frame_timestamps->render_time_us = render_time_us; - - next_frame_timestamps_index_ = - Add(next_frame_timestamps_index_, 1); - }); - - int32_t decode_result = decoder->Decode(frame->EncodedImage(), - false, // missing_frame - frame->RenderTimeMs()); - - return decode_result == WEBRTC_VIDEO_CODEC_OK ? kOk : kDecodeFailure; + VideoDecoder* decoder = GetDecoder(frame->PayloadType()); + if (!decoder) { + return kDecodeFailure; } - return kNoFrame; + int32_t decode_result = decoder->Decode(frame->EncodedImage(), // + /*missing_frames=*/false, // + frame->RenderTimeMs()); + switch (decode_result) { + case WEBRTC_VIDEO_CODEC_OK: { + return kOk; + } + case WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME: { + return kOkRequestKeyframe; + } + default: + return kDecodeFailure; + } } VideoStreamDecoderImpl::FrameTimestamps* VideoStreamDecoderImpl::GetFrameTimestamps(int64_t timestamp) { - RTC_DCHECK_RUN_ON(&bookkeeping_queue_); - int start_time_index = next_frame_timestamps_index_; for (int i = 0; i < kFrameTimestampsMemory; ++i) { start_time_index = Subtract(start_time_index, 1); @@ -238,23 +243,10 @@ VideoStreamDecoderImpl::GetFrameTimestamps(int64_t timestamp) { return nullptr; } -// VideoDecoder::DecodedImageCallback -int32_t VideoStreamDecoderImpl::Decoded(VideoFrame& decoded_image) { - Decoded(decoded_image, absl::nullopt, absl::nullopt); - return WEBRTC_VIDEO_CODEC_OK; -} - -// VideoDecoder::DecodedImageCallback -int32_t VideoStreamDecoderImpl::Decoded(VideoFrame& decoded_image, - int64_t decode_time_ms) { - Decoded(decoded_image, decode_time_ms, absl::nullopt); - return WEBRTC_VIDEO_CODEC_OK; -} - -// VideoDecoder::DecodedImageCallback -void VideoStreamDecoderImpl::Decoded(VideoFrame& decoded_image, - absl::optional decode_time_ms, - absl::optional qp) { +void VideoStreamDecoderImpl::OnDecodedFrameCallback( + VideoFrame& decoded_image, + absl::optional decode_time_ms, + absl::optional qp) { int64_t decode_stop_time_ms = rtc::TimeMillis(); bookkeeping_queue_.PostTask([this, decode_stop_time_ms, decoded_image, @@ -284,4 +276,28 @@ void VideoStreamDecoderImpl::Decoded(VideoFrame& decoded_image, }); } +VideoStreamDecoderImpl::DecodeCallbacks::DecodeCallbacks( + VideoStreamDecoderImpl* video_stream_decoder_impl) + : video_stream_decoder_impl_(video_stream_decoder_impl) {} + +int32_t VideoStreamDecoderImpl::DecodeCallbacks::Decoded( + VideoFrame& decoded_image) { + Decoded(decoded_image, absl::nullopt, absl::nullopt); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t VideoStreamDecoderImpl::DecodeCallbacks::Decoded( + VideoFrame& decoded_image, + int64_t decode_time_ms) { + Decoded(decoded_image, decode_time_ms, absl::nullopt); + return WEBRTC_VIDEO_CODEC_OK; +} + +void VideoStreamDecoderImpl::DecodeCallbacks::Decoded( + VideoFrame& decoded_image, + absl::optional decode_time_ms, + absl::optional qp) { + video_stream_decoder_impl_->OnDecodedFrameCallback(decoded_image, + decode_time_ms, qp); +} } // namespace webrtc diff --git a/video/video_stream_decoder_impl.h b/video/video_stream_decoder_impl.h index c439be5c16..f3f09e4a79 100644 --- a/video/video_stream_decoder_impl.h +++ b/video/video_stream_decoder_impl.h @@ -26,8 +26,7 @@ namespace webrtc { -class VideoStreamDecoderImpl : public VideoStreamDecoderInterface, - private DecodedImageCallback { +class VideoStreamDecoderImpl : public VideoStreamDecoderInterface { public: VideoStreamDecoderImpl( VideoStreamDecoderInterface::Callbacks* callbacks, @@ -43,12 +42,23 @@ class VideoStreamDecoderImpl : public VideoStreamDecoderInterface, void SetMaxPlayoutDelay(TimeDelta max_delay) override; private: + class DecodeCallbacks : public DecodedImageCallback { + public: + explicit DecodeCallbacks(VideoStreamDecoderImpl* video_stream_decoder_impl); + int32_t Decoded(VideoFrame& decodedImage) override; + int32_t Decoded(VideoFrame& decodedImage, int64_t decode_time_ms) override; + void Decoded(VideoFrame& decodedImage, + absl::optional decode_time_ms, + absl::optional qp) override; + + private: + VideoStreamDecoderImpl* const video_stream_decoder_impl_; + }; + enum DecodeResult { kOk, + kOkRequestKeyframe, kDecodeFailure, - kNoFrame, - kNoDecoder, - kShutdown, }; struct FrameTimestamps { @@ -57,36 +67,25 @@ class VideoStreamDecoderImpl : public VideoStreamDecoderInterface, int64_t render_time_us; }; - VideoDecoder* GetDecoder(int payload_type); - static void DecodeLoop(void* ptr); - DecodeResult DecodeNextFrame(int max_wait_time_ms, bool keyframe_required); + void SaveFrameTimestamps(const video_coding::EncodedFrame& frame) + RTC_RUN_ON(bookkeeping_queue_); + FrameTimestamps* GetFrameTimestamps(int64_t timestamp) + RTC_RUN_ON(bookkeeping_queue_); + void StartNextDecode() RTC_RUN_ON(bookkeeping_queue_); + void OnNextFrameCallback(std::unique_ptr frame, + video_coding::FrameBuffer::ReturnReason res) + RTC_RUN_ON(bookkeeping_queue_); + void OnDecodedFrameCallback(VideoFrame& decodedImage, // NOLINT + absl::optional decode_time_ms, + absl::optional qp); - FrameTimestamps* GetFrameTimestamps(int64_t timestamp); + VideoDecoder* GetDecoder(int payload_type) RTC_RUN_ON(decode_queue_); + VideoStreamDecoderImpl::DecodeResult DecodeFrame( + std::unique_ptr frame) + RTC_RUN_ON(decode_queue_); - // Implements DecodedImageCallback interface - int32_t Decoded(VideoFrame& decodedImage) override; - int32_t Decoded(VideoFrame& decodedImage, int64_t decode_time_ms) override; - void Decoded(VideoFrame& decodedImage, - absl::optional decode_time_ms, - absl::optional qp) override; - - VideoStreamDecoderInterface::Callbacks* const callbacks_ - RTC_PT_GUARDED_BY(bookkeeping_queue_); - VideoDecoderFactory* const decoder_factory_; - std::map> decoder_settings_; - - // The |bookkeeping_queue_| is used to: - // - Make |callbacks_|. - // - Insert/extract frames from the |frame_buffer_| - // - Synchronize with whatever thread that makes the Decoded callback. - rtc::TaskQueue bookkeeping_queue_; - - rtc::PlatformThread decode_thread_; VCMTiming timing_; - video_coding::FrameBuffer frame_buffer_; - video_coding::VideoLayerFrameId last_continuous_id_; - absl::optional current_payload_type_; - std::unique_ptr decoder_; + DecodeCallbacks decode_callbacks_; // Some decoders are pipelined so it is not sufficient to save frame info // for the last frame only. @@ -94,6 +93,31 @@ class VideoStreamDecoderImpl : public VideoStreamDecoderInterface, std::array frame_timestamps_ RTC_GUARDED_BY(bookkeeping_queue_); int next_frame_timestamps_index_ RTC_GUARDED_BY(bookkeeping_queue_); + VideoStreamDecoderInterface::Callbacks* const callbacks_ + RTC_PT_GUARDED_BY(bookkeeping_queue_); + video_coding::VideoLayerFrameId last_continuous_id_ + RTC_GUARDED_BY(bookkeeping_queue_); + bool keyframe_required_ RTC_GUARDED_BY(bookkeeping_queue_); + + absl::optional current_payload_type_ RTC_GUARDED_BY(decode_queue_); + VideoDecoderFactory* const decoder_factory_ RTC_PT_GUARDED_BY(decode_queue_); + std::map> decoder_settings_ + RTC_GUARDED_BY(decode_queue_); + + // The |bookkeeping_queue_| use the |frame_buffer_| and also posts tasks to + // the |decode_queue_|. The |decode_queue_| in turn use the |decoder_| to + // decode frames. When the |decoder_| is done it will post back to the + // |bookkeeping_queue_| with the decoded frame. During shutdown we start by + // isolating the |bookkeeping_queue_| from the |decode_queue_|, so now it's + // safe for the |decode_queue_| to be destructed. After that the |decoder_| + // can be destructed, and then the |bookkeeping_queue_|. Finally the + // |frame_buffer_| can be destructed. + rtc::CriticalSection shut_down_crit_; + bool shut_down_ RTC_GUARDED_BY(shut_down_crit_); + video_coding::FrameBuffer frame_buffer_ RTC_GUARDED_BY(bookkeeping_queue_); + rtc::TaskQueue bookkeeping_queue_; + std::unique_ptr decoder_ RTC_GUARDED_BY(decode_queue_); + rtc::TaskQueue decode_queue_; }; } // namespace webrtc diff --git a/video/video_stream_decoder_impl_unittest.cc b/video/video_stream_decoder_impl_unittest.cc new file mode 100644 index 0000000000..37924d11b3 --- /dev/null +++ b/video/video_stream_decoder_impl_unittest.cc @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "video/video_stream_decoder_impl.h" + +#include + +#include "api/video/i420_buffer.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" + +namespace webrtc { +namespace { +using ::testing::_; +using ::testing::ByMove; +using ::testing::NiceMock; +using ::testing::Return; + +class MockVideoStreamDecoderCallbacks + : public VideoStreamDecoderInterface::Callbacks { + public: + MOCK_METHOD0(OnNonDecodableState, void()); + MOCK_METHOD1(OnContinuousUntil, + void(const video_coding::VideoLayerFrameId& key)); + MOCK_METHOD1(OnEncodedFrame, void(const video_coding::EncodedFrame& frame)); + MOCK_METHOD3(OnDecodedFrame, + void(VideoFrame decodedImage, + absl::optional decode_time_ms, + absl::optional qp)); +}; + +class StubVideoDecoder : public VideoDecoder { + public: + MOCK_METHOD2(InitDecode, + int32_t(const VideoCodec* codec_settings, + int32_t number_of_cores)); + + int32_t Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) override { + int32_t ret_code = DecodeCall(input_image, missing_frames, render_time_ms); + if (ret_code == WEBRTC_VIDEO_CODEC_OK || + ret_code == WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME) { + VideoFrame frame = VideoFrame::Builder() + .set_video_frame_buffer(I420Buffer::Create(1, 1)) + .build(); + callback_->Decoded(frame); + } + return ret_code; + } + + MOCK_METHOD3(DecodeCall, + int32_t(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms)); + + int32_t Release() override { return 0; } + + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override { + callback_ = callback; + return 0; + } + + private: + DecodedImageCallback* callback_; +}; + +class WrappedVideoDecoder : public VideoDecoder { + public: + explicit WrappedVideoDecoder(StubVideoDecoder* decoder) : decoder_(decoder) {} + + int32_t InitDecode(const VideoCodec* codec_settings, + int32_t number_of_cores) override { + return decoder_->InitDecode(codec_settings, number_of_cores); + } + int32_t Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) override { + return decoder_->Decode(input_image, missing_frames, render_time_ms); + } + int32_t Release() override { return decoder_->Release(); } + + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override { + return decoder_->RegisterDecodeCompleteCallback(callback); + } + + private: + StubVideoDecoder* decoder_; +}; + +class FakeVideoDecoderFactory : public VideoDecoderFactory { + public: + std::vector GetSupportedFormats() const override { + return {}; + } + std::unique_ptr CreateVideoDecoder( + const SdpVideoFormat& format) override { + if (format.name == "VP8") { + return std::make_unique(&vp8_decoder_); + } + + if (format.name == "AV1") { + return std::make_unique(&av1_decoder_); + } + + return {}; + } + + StubVideoDecoder& Vp8Decoder() { return vp8_decoder_; } + StubVideoDecoder& Av1Decoder() { return av1_decoder_; } + + private: + NiceMock vp8_decoder_; + NiceMock av1_decoder_; +}; + +class FakeEncodedFrame : public video_coding::EncodedFrame { + public: + int64_t ReceivedTime() const override { return 0; } + int64_t RenderTime() const override { return 0; } + + // Setters for protected variables. + void SetPayloadType(int payload_type) { _payloadType = payload_type; } +}; + +class FrameBuilder { + public: + FrameBuilder() : frame_(std::make_unique()) {} + + FrameBuilder& WithPayloadType(int payload_type) { + frame_->SetPayloadType(payload_type); + return *this; + } + + FrameBuilder& WithPictureId(int picture_id) { + frame_->id.picture_id = picture_id; + return *this; + } + + std::unique_ptr Build() { return std::move(frame_); } + + private: + std::unique_ptr frame_; +}; + +class VideoStreamDecoderImplTest : public ::testing::Test { + public: + VideoStreamDecoderImplTest() + : time_controller_(Timestamp::seconds(0)), + video_stream_decoder_(&callbacks_, + &decoder_factory_, + time_controller_.GetTaskQueueFactory(), + {{1, std::make_pair(SdpVideoFormat("VP8"), 1)}, + {2, std::make_pair(SdpVideoFormat("AV1"), 1)}}) { + } + + NiceMock callbacks_; + FakeVideoDecoderFactory decoder_factory_; + GlobalSimulatedTimeController time_controller_; + VideoStreamDecoderImpl video_stream_decoder_; +}; + +TEST_F(VideoStreamDecoderImplTest, InsertAndDecodeFrame) { + video_stream_decoder_.OnFrame(FrameBuilder().WithPayloadType(1).Build()); + EXPECT_CALL(callbacks_, OnDecodedFrame); + time_controller_.AdvanceTime(TimeDelta::ms(1)); +} + +TEST_F(VideoStreamDecoderImplTest, NonDecodableStateWaitingForKeyframe) { + EXPECT_CALL(callbacks_, OnNonDecodableState); + time_controller_.AdvanceTime(TimeDelta::ms(200)); +} + +TEST_F(VideoStreamDecoderImplTest, NonDecodableStateWaitingForDeltaFrame) { + video_stream_decoder_.OnFrame(FrameBuilder().WithPayloadType(1).Build()); + EXPECT_CALL(callbacks_, OnDecodedFrame); + time_controller_.AdvanceTime(TimeDelta::ms(1)); + EXPECT_CALL(callbacks_, OnNonDecodableState); + time_controller_.AdvanceTime(TimeDelta::ms(3000)); +} + +TEST_F(VideoStreamDecoderImplTest, InsertAndDecodeFrameWithKeyframeRequest) { + video_stream_decoder_.OnFrame(FrameBuilder().WithPayloadType(1).Build()); + EXPECT_CALL(decoder_factory_.Vp8Decoder(), DecodeCall) + .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME)); + EXPECT_CALL(callbacks_, OnDecodedFrame); + EXPECT_CALL(callbacks_, OnNonDecodableState); + time_controller_.AdvanceTime(TimeDelta::ms(1)); +} + +TEST_F(VideoStreamDecoderImplTest, FailToInitDecoder) { + video_stream_decoder_.OnFrame(FrameBuilder().WithPayloadType(1).Build()); + ON_CALL(decoder_factory_.Vp8Decoder(), InitDecode) + .WillByDefault(Return(WEBRTC_VIDEO_CODEC_ERROR)); + EXPECT_CALL(callbacks_, OnNonDecodableState); + time_controller_.AdvanceTime(TimeDelta::ms(1)); +} + +TEST_F(VideoStreamDecoderImplTest, FailToDecodeFrame) { + video_stream_decoder_.OnFrame(FrameBuilder().WithPayloadType(1).Build()); + ON_CALL(decoder_factory_.Vp8Decoder(), DecodeCall) + .WillByDefault(Return(WEBRTC_VIDEO_CODEC_ERROR)); + EXPECT_CALL(callbacks_, OnNonDecodableState); + time_controller_.AdvanceTime(TimeDelta::ms(1)); +} + +TEST_F(VideoStreamDecoderImplTest, ChangeFramePayloadType) { + video_stream_decoder_.OnFrame( + FrameBuilder().WithPayloadType(1).WithPictureId(0).Build()); + EXPECT_CALL(decoder_factory_.Vp8Decoder(), DecodeCall); + EXPECT_CALL(callbacks_, OnDecodedFrame); + time_controller_.AdvanceTime(TimeDelta::ms(1)); + + video_stream_decoder_.OnFrame( + FrameBuilder().WithPayloadType(2).WithPictureId(1).Build()); + EXPECT_CALL(decoder_factory_.Av1Decoder(), DecodeCall); + EXPECT_CALL(callbacks_, OnDecodedFrame); + time_controller_.AdvanceTime(TimeDelta::ms(1)); +} + +} // namespace +} // namespace webrtc