/* * Copyright (c) 2013 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 "api/test/mock_video_decoder.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/include/video_coding.h" #include "modules/video_coding/timing/timing.h" #include "modules/video_coding/video_coding_impl.h" #include "system_wrappers/include/clock.h" #include "test/gtest.h" #include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::AnyNumber; using ::testing::NiceMock; namespace webrtc { namespace vcm { namespace { class MockPacketRequestCallback : public VCMPacketRequestCallback { public: MOCK_METHOD(int32_t, ResendPackets, (const uint16_t* sequenceNumbers, uint16_t length), (override)); }; class MockVCMReceiveCallback : public VCMReceiveCallback { public: MockVCMReceiveCallback() {} virtual ~MockVCMReceiveCallback() {} MOCK_METHOD( int32_t, FrameToRender, (VideoFrame&, absl::optional, TimeDelta, VideoContentType), (override)); MOCK_METHOD(void, OnIncomingPayloadType, (int), (override)); MOCK_METHOD(void, OnDecoderInfoChanged, (const VideoDecoder::DecoderInfo&), (override)); }; class TestVideoReceiver : public ::testing::Test { protected: static const int kUnusedPayloadType = 10; static const uint16_t kMaxWaitTimeMs = 100; TestVideoReceiver() : clock_(0), timing_(&clock_, field_trials_), receiver_(&clock_, &timing_, field_trials_) {} virtual void SetUp() { // Register decoder. receiver_.RegisterExternalDecoder(&decoder_, kUnusedPayloadType); VideoDecoder::Settings settings; settings.set_codec_type(kVideoCodecVP8); receiver_.RegisterReceiveCodec(kUnusedPayloadType, settings); // Set protection mode. const size_t kMaxNackListSize = 250; const int kMaxPacketAgeToNack = 450; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, 0); EXPECT_EQ( 0, receiver_.RegisterPacketRequestCallback(&packet_request_callback_)); // Since we call Decode, we need to provide a valid receive callback. // However, for the purposes of these tests, we ignore the callbacks. EXPECT_CALL(receive_callback_, OnIncomingPayloadType(_)).Times(AnyNumber()); EXPECT_CALL(receive_callback_, OnDecoderInfoChanged).Times(AnyNumber()); receiver_.RegisterReceiveCallback(&receive_callback_); } RTPHeader GetDefaultRTPHeader() const { RTPHeader header; header.markerBit = false; header.payloadType = kUnusedPayloadType; header.ssrc = 1; header.headerLength = 12; return header; } RTPVideoHeader GetDefaultVp8Header() const { RTPVideoHeader video_header = {}; video_header.frame_type = VideoFrameType::kEmptyFrame; video_header.codec = kVideoCodecVP8; return video_header; } void InsertAndVerifyPaddingFrame(const uint8_t* payload, RTPHeader* header, const RTPVideoHeader& video_header) { for (int j = 0; j < 5; ++j) { // Padding only packets are passed to the VCM with payload size 0. EXPECT_EQ(0, receiver_.IncomingPacket(payload, 0, *header, video_header)); ++header->sequenceNumber; } receiver_.Process(); EXPECT_CALL(decoder_, Decode(_, _, _)).Times(0); EXPECT_EQ(VCM_FRAME_NOT_READY, receiver_.Decode(kMaxWaitTimeMs)); } void InsertAndVerifyDecodableFrame(const uint8_t* payload, size_t length, RTPHeader* header, const RTPVideoHeader& video_header) { EXPECT_EQ(0, receiver_.IncomingPacket(payload, length, *header, video_header)); ++header->sequenceNumber; EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); receiver_.Process(); EXPECT_CALL(decoder_, Decode(_, _, _)).Times(1); EXPECT_EQ(0, receiver_.Decode(kMaxWaitTimeMs)); } test::ScopedKeyValueConfig field_trials_; SimulatedClock clock_; NiceMock decoder_; NiceMock packet_request_callback_; VCMTiming timing_; MockVCMReceiveCallback receive_callback_; VideoReceiver receiver_; }; TEST_F(TestVideoReceiver, PaddingOnlyFrames) { const size_t kPaddingSize = 220; const uint8_t kPayload[kPaddingSize] = {0}; RTPHeader header = GetDefaultRTPHeader(); RTPVideoHeader video_header = GetDefaultVp8Header(); header.paddingLength = kPaddingSize; for (int i = 0; i < 10; ++i) { EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); InsertAndVerifyPaddingFrame(kPayload, &header, video_header); clock_.AdvanceTimeMilliseconds(33); header.timestamp += 3000; } } TEST_F(TestVideoReceiver, PaddingOnlyFramesWithLosses) { const size_t kFrameSize = 1200; const size_t kPaddingSize = 220; const uint8_t kPayload[kFrameSize] = {0}; RTPHeader header = GetDefaultRTPHeader(); RTPVideoHeader video_header = GetDefaultVp8Header(); header.paddingLength = kPaddingSize; video_header.video_type_header.emplace(); // Insert one video frame to get one frame decoded. video_header.frame_type = VideoFrameType::kVideoFrameKey; video_header.is_first_packet_in_frame = true; header.markerBit = true; InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header, video_header); clock_.AdvanceTimeMilliseconds(33); header.timestamp += 3000; video_header.frame_type = VideoFrameType::kEmptyFrame; video_header.is_first_packet_in_frame = false; header.markerBit = false; // Insert padding frames. for (int i = 0; i < 10; ++i) { // Lose one packet from the 6th frame. if (i == 5) { ++header.sequenceNumber; } // Lose the 4th frame. if (i == 3) { header.sequenceNumber += 5; } else { if (i > 3 && i < 5) { EXPECT_CALL(packet_request_callback_, ResendPackets(_, 5)).Times(1); } else if (i >= 5) { EXPECT_CALL(packet_request_callback_, ResendPackets(_, 6)).Times(1); } else { EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); } InsertAndVerifyPaddingFrame(kPayload, &header, video_header); } clock_.AdvanceTimeMilliseconds(33); header.timestamp += 3000; } } TEST_F(TestVideoReceiver, PaddingOnlyAndVideo) { const size_t kFrameSize = 1200; const size_t kPaddingSize = 220; const uint8_t kPayload[kFrameSize] = {0}; RTPHeader header = GetDefaultRTPHeader(); RTPVideoHeader video_header = GetDefaultVp8Header(); video_header.is_first_packet_in_frame = false; header.paddingLength = kPaddingSize; auto& vp8_header = video_header.video_type_header.emplace(); vp8_header.pictureId = -1; vp8_header.tl0PicIdx = -1; for (int i = 0; i < 3; ++i) { // Insert 2 video frames. for (int j = 0; j < 2; ++j) { if (i == 0 && j == 0) // First frame should be a key frame. video_header.frame_type = VideoFrameType::kVideoFrameKey; else video_header.frame_type = VideoFrameType::kVideoFrameDelta; video_header.is_first_packet_in_frame = true; header.markerBit = true; InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header, video_header); clock_.AdvanceTimeMilliseconds(33); header.timestamp += 3000; } // Insert 2 padding only frames. video_header.frame_type = VideoFrameType::kEmptyFrame; video_header.is_first_packet_in_frame = false; header.markerBit = false; for (int j = 0; j < 2; ++j) { // InsertAndVerifyPaddingFrame(kPayload, &header); clock_.AdvanceTimeMilliseconds(33); header.timestamp += 3000; } } } } // namespace } // namespace vcm } // namespace webrtc