| /* | 
 |  *  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 "modules/video_coding/include/mock/mock_vcm_callbacks.h" | 
 | #include "modules/video_coding/include/video_coding.h" | 
 | #include "modules/video_coding/timing.h" | 
 | #include "modules/video_coding/video_coding_impl.h" | 
 | #include "system_wrappers/include/clock.h" | 
 | #include "test/gtest.h" | 
 | #include "test/video_codec_settings.h" | 
 |  | 
 | using ::testing::_; | 
 | using ::testing::AnyNumber; | 
 | using ::testing::NiceMock; | 
 |  | 
 | namespace webrtc { | 
 | namespace vcm { | 
 | namespace { | 
 |  | 
 | class TestVideoReceiver : public ::testing::Test { | 
 |  protected: | 
 |   static const int kUnusedPayloadType = 10; | 
 |   static const uint16_t kMaxWaitTimeMs = 100; | 
 |  | 
 |   TestVideoReceiver() | 
 |       : clock_(0), timing_(&clock_), receiver_(&clock_, &timing_) {} | 
 |  | 
 |   virtual void SetUp() { | 
 |     // Register decoder. | 
 |     receiver_.RegisterExternalDecoder(&decoder_, kUnusedPayloadType); | 
 |     webrtc::test::CodecSettings(kVideoCodecVP8, &settings_); | 
 |     settings_.plType = kUnusedPayloadType; | 
 |     EXPECT_EQ(0, receiver_.RegisterReceiveCodec(&settings_, 1, true)); | 
 |  | 
 |     // Set protection mode. | 
 |     const size_t kMaxNackListSize = 250; | 
 |     const int kMaxPacketAgeToNack = 450; | 
 |     receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, 0); | 
 |     EXPECT_EQ(0, receiver_.SetVideoProtection(kProtectionNack, true)); | 
 |     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_, OnDecoderImplementationName(_)) | 
 |         .Times(AnyNumber()); | 
 |     receiver_.RegisterReceiveCallback(&receive_callback_); | 
 |   } | 
 |  | 
 |   WebRtcRTPHeader GetDefaultVp8Header() const { | 
 |     WebRtcRTPHeader header = {}; | 
 |     header.frameType = VideoFrameType::kEmptyFrame; | 
 |     header.header.markerBit = false; | 
 |     header.header.payloadType = kUnusedPayloadType; | 
 |     header.header.ssrc = 1; | 
 |     header.header.headerLength = 12; | 
 |     header.video_header().codec = kVideoCodecVP8; | 
 |     return header; | 
 |   } | 
 |  | 
 |   void InsertAndVerifyPaddingFrame(const uint8_t* payload, | 
 |                                    WebRtcRTPHeader* 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)); | 
 |       ++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, | 
 |                                      WebRtcRTPHeader* header) { | 
 |     EXPECT_EQ(0, receiver_.IncomingPacket(payload, length, *header)); | 
 |     ++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)); | 
 |   } | 
 |  | 
 |   SimulatedClock clock_; | 
 |   VideoCodec settings_; | 
 |   NiceMock<MockVideoDecoder> decoder_; | 
 |   NiceMock<MockPacketRequestCallback> 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}; | 
 |   WebRtcRTPHeader header = GetDefaultVp8Header(); | 
 |   header.header.paddingLength = kPaddingSize; | 
 |   for (int i = 0; i < 10; ++i) { | 
 |     EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)).Times(0); | 
 |     InsertAndVerifyPaddingFrame(kPayload, &header); | 
 |     clock_.AdvanceTimeMilliseconds(33); | 
 |     header.header.timestamp += 3000; | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(TestVideoReceiver, PaddingOnlyFramesWithLosses) { | 
 |   const size_t kFrameSize = 1200; | 
 |   const size_t kPaddingSize = 220; | 
 |   const uint8_t kPayload[kFrameSize] = {0}; | 
 |   WebRtcRTPHeader header = GetDefaultVp8Header(); | 
 |   header.header.paddingLength = kPaddingSize; | 
 |   header.video_header().video_type_header.emplace<RTPVideoHeaderVP8>(); | 
 |  | 
 |   // Insert one video frame to get one frame decoded. | 
 |   header.frameType = VideoFrameType::kVideoFrameKey; | 
 |   header.video_header().is_first_packet_in_frame = true; | 
 |   header.header.markerBit = true; | 
 |   InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header); | 
 |  | 
 |   clock_.AdvanceTimeMilliseconds(33); | 
 |   header.header.timestamp += 3000; | 
 |   header.frameType = VideoFrameType::kEmptyFrame; | 
 |   header.video_header().is_first_packet_in_frame = false; | 
 |   header.header.markerBit = false; | 
 |   // Insert padding frames. | 
 |   for (int i = 0; i < 10; ++i) { | 
 |     // Lose one packet from the 6th frame. | 
 |     if (i == 5) { | 
 |       ++header.header.sequenceNumber; | 
 |     } | 
 |     // Lose the 4th frame. | 
 |     if (i == 3) { | 
 |       header.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); | 
 |     } | 
 |     clock_.AdvanceTimeMilliseconds(33); | 
 |     header.header.timestamp += 3000; | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(TestVideoReceiver, PaddingOnlyAndVideo) { | 
 |   const size_t kFrameSize = 1200; | 
 |   const size_t kPaddingSize = 220; | 
 |   const uint8_t kPayload[kFrameSize] = {0}; | 
 |   WebRtcRTPHeader header = GetDefaultVp8Header(); | 
 |   header.video_header().is_first_packet_in_frame = false; | 
 |   header.header.paddingLength = kPaddingSize; | 
 |   auto& vp8_header = | 
 |       header.video.video_type_header.emplace<RTPVideoHeaderVP8>(); | 
 |   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. | 
 |         header.frameType = VideoFrameType::kVideoFrameKey; | 
 |       else | 
 |         header.frameType = VideoFrameType::kVideoFrameDelta; | 
 |       header.video_header().is_first_packet_in_frame = true; | 
 |       header.header.markerBit = true; | 
 |       InsertAndVerifyDecodableFrame(kPayload, kFrameSize, &header); | 
 |       clock_.AdvanceTimeMilliseconds(33); | 
 |       header.header.timestamp += 3000; | 
 |     } | 
 |  | 
 |     // Insert 2 padding only frames. | 
 |     header.frameType = VideoFrameType::kEmptyFrame; | 
 |     header.video_header().is_first_packet_in_frame = false; | 
 |     header.header.markerBit = false; | 
 |     for (int j = 0; j < 2; ++j) { | 
 |       // InsertAndVerifyPaddingFrame(kPayload, &header); | 
 |       clock_.AdvanceTimeMilliseconds(33); | 
 |       header.header.timestamp += 3000; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace vcm | 
 | }  // namespace webrtc |