| /* |
| * Copyright (c) 2011 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 "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h" |
| #include "modules/video_coding/main/interface/video_coding.h" |
| #include "modules/video_coding/main/interface/mock/mock_vcm_callbacks.h" |
| #include "modules/video_coding/main/source/mock/fake_tick_time.h" |
| |
| namespace webrtc { |
| |
| using ::testing::Return; |
| using ::testing::_; |
| using ::testing::ElementsAre; |
| using ::testing::AllOf; |
| using ::testing::Args; |
| using ::testing::Field; |
| using ::testing::Pointee; |
| using ::testing::NiceMock; |
| using ::testing::Sequence; |
| |
| class VCMRobustnessTest : public ::testing::Test { |
| protected: |
| static const size_t kPayloadLen = 10; |
| |
| virtual void SetUp() { |
| clock_ = new FakeTickTime(0); |
| ASSERT_TRUE(clock_ != NULL); |
| vcm_ = VideoCodingModule::Create(0, clock_); |
| ASSERT_TRUE(vcm_ != NULL); |
| ASSERT_EQ(0, vcm_->InitializeReceiver()); |
| ASSERT_EQ(0, vcm_->RegisterFrameTypeCallback(&frame_type_callback_)); |
| ASSERT_EQ(0, vcm_->RegisterPacketRequestCallback(&request_callback_)); |
| ASSERT_EQ(VCM_OK, vcm_->Codec(kVideoCodecVP8, &video_codec_)); |
| ASSERT_EQ(VCM_OK, vcm_->RegisterReceiveCodec(&video_codec_, 1)); |
| ASSERT_EQ(VCM_OK, vcm_->RegisterExternalDecoder(&decoder_, |
| video_codec_.plType, |
| true)); |
| } |
| |
| virtual void TearDown() { |
| VideoCodingModule::Destroy(vcm_); |
| delete clock_; |
| } |
| |
| void InsertPacket(uint32_t timestamp, |
| uint16_t seq_no, |
| bool first, |
| bool marker_bit, |
| FrameType frame_type) { |
| const uint8_t payload[kPayloadLen] = {0}; |
| WebRtcRTPHeader rtp_info; |
| memset(&rtp_info, 0, sizeof(rtp_info)); |
| rtp_info.frameType = frame_type; |
| rtp_info.header.timestamp = timestamp; |
| rtp_info.header.sequenceNumber = seq_no; |
| rtp_info.header.markerBit = marker_bit; |
| rtp_info.header.payloadType = video_codec_.plType; |
| rtp_info.type.Video.codec = kRTPVideoVP8; |
| rtp_info.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8(); |
| rtp_info.type.Video.isFirstPacket = first; |
| |
| ASSERT_EQ(VCM_OK, vcm_->IncomingPacket(payload, kPayloadLen, rtp_info)); |
| } |
| |
| VideoCodingModule* vcm_; |
| VideoCodec video_codec_; |
| MockVCMFrameTypeCallback frame_type_callback_; |
| MockPacketRequestCallback request_callback_; |
| NiceMock<MockVideoDecoder> decoder_; |
| NiceMock<MockVideoDecoder> decoderCopy_; |
| FakeTickTime* clock_; |
| }; |
| |
| TEST_F(VCMRobustnessTest, TestHardNack) { |
| Sequence s; |
| EXPECT_CALL(request_callback_, ResendPackets(_, 2)) |
| .With(Args<0, 1>(ElementsAre(6, 7))) |
| .Times(1); |
| for (int ts = 0; ts <= 6000; ts += 3000) { |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, ts), |
| Field(&EncodedImage::_length, |
| kPayloadLen * 3), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s); |
| } |
| |
| ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode( |
| VideoCodingModule::kHardNack, |
| VideoCodingModule::kNoDecodeErrors)); |
| |
| InsertPacket(0, 0, true, false, kVideoFrameKey); |
| InsertPacket(0, 1, false, false, kVideoFrameKey); |
| InsertPacket(0, 2, false, true, kVideoFrameKey); |
| |
| InsertPacket(3000, 3, true, false, kVideoFrameDelta); |
| InsertPacket(3000, 4, false, false, kVideoFrameDelta); |
| InsertPacket(3000, 5, false, true, kVideoFrameDelta); |
| |
| ASSERT_EQ(VCM_OK, vcm_->Decode(0)); |
| ASSERT_EQ(VCM_OK, vcm_->Decode(0)); |
| ASSERT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| |
| clock_->IncrementDebugClock(10); |
| |
| ASSERT_EQ(VCM_OK, vcm_->Process()); |
| |
| ASSERT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| |
| InsertPacket(6000, 8, false, true, kVideoFrameDelta); |
| clock_->IncrementDebugClock(10); |
| ASSERT_EQ(VCM_OK, vcm_->Process()); |
| |
| ASSERT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| |
| InsertPacket(6000, 6, true, false, kVideoFrameDelta); |
| InsertPacket(6000, 7, false, false, kVideoFrameDelta); |
| clock_->IncrementDebugClock(10); |
| ASSERT_EQ(VCM_OK, vcm_->Process()); |
| |
| ASSERT_EQ(VCM_OK, vcm_->Decode(0)); |
| } |
| |
| TEST_F(VCMRobustnessTest, TestHardNackNoneDecoded) { |
| EXPECT_CALL(request_callback_, ResendPackets(_, _)) |
| .Times(0); |
| EXPECT_CALL(frame_type_callback_, FrameTypeRequest(kVideoFrameKey)) |
| .Times(1); |
| |
| ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode( |
| VideoCodingModule::kHardNack, |
| VideoCodingModule::kNoDecodeErrors)); |
| |
| InsertPacket(3000, 3, true, false, kVideoFrameDelta); |
| InsertPacket(3000, 4, false, false, kVideoFrameDelta); |
| InsertPacket(3000, 5, false, true, kVideoFrameDelta); |
| |
| EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| ASSERT_EQ(VCM_OK, vcm_->Process()); |
| |
| clock_->IncrementDebugClock(10); |
| |
| EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| ASSERT_EQ(VCM_OK, vcm_->Process()); |
| } |
| |
| TEST_F(VCMRobustnessTest, TestDualDecoder) { |
| Sequence s1, s2; |
| EXPECT_CALL(request_callback_, ResendPackets(_, 1)) |
| .With(Args<0, 1>(ElementsAre(4))) |
| .Times(1); |
| |
| EXPECT_CALL(decoder_, Copy()) |
| .Times(1) |
| .WillOnce(Return(&decoderCopy_)); |
| EXPECT_CALL(decoderCopy_, Copy()) |
| .Times(1) |
| .WillOnce(Return(&decoder_)); |
| |
| // Decode operations |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 0), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 3000), |
| Field(&EncodedImage::_completeFrame, |
| false)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 6000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 9000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| |
| EXPECT_CALL(decoderCopy_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 3000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s2); |
| EXPECT_CALL(decoderCopy_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 6000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s2); |
| |
| |
| ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode( |
| VideoCodingModule::kDualDecoder, |
| VideoCodingModule::kAllowDecodeErrors)); |
| |
| InsertPacket(0, 0, true, false, kVideoFrameKey); |
| InsertPacket(0, 1, false, false, kVideoFrameKey); |
| InsertPacket(0, 2, false, true, kVideoFrameKey); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 0. |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(3000, 3, true, false, kVideoFrameDelta); |
| // Packet 4 missing |
| InsertPacket(3000, 5, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(6000, 6, true, false, kVideoFrameDelta); |
| InsertPacket(6000, 7, false, false, kVideoFrameDelta); |
| InsertPacket(6000, 8, false, true, kVideoFrameDelta); |
| |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 3000 incomplete. |
| // Spawn a decoder copy. |
| EXPECT_EQ(0, vcm_->DecodeDualFrame(0)); // Expect no dual decoder action. |
| |
| clock_->IncrementDebugClock(10); |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Generate NACK list. |
| |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 6000 complete. |
| EXPECT_EQ(0, vcm_->DecodeDualFrame(0)); // Expect no dual decoder action. |
| |
| InsertPacket(3000, 4, false, false, kVideoFrameDelta); |
| EXPECT_EQ(1, vcm_->DecodeDualFrame(0)); // Dual decode of timestamp 3000. |
| EXPECT_EQ(1, vcm_->DecodeDualFrame(0)); // Dual decode of timestamp 6000. |
| EXPECT_EQ(0, vcm_->DecodeDualFrame(0)); // No more frames. |
| |
| InsertPacket(9000, 9, true, false, kVideoFrameDelta); |
| InsertPacket(9000, 10, false, false, kVideoFrameDelta); |
| InsertPacket(9000, 11, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 9000 complete. |
| EXPECT_EQ(0, vcm_->DecodeDualFrame(0)); // Expect no dual decoder action. |
| } |
| |
| TEST_F(VCMRobustnessTest, TestModeNoneWithErrors) { |
| EXPECT_CALL(decoder_, InitDecode(_, _)).Times(1); |
| EXPECT_CALL(decoder_, Release()).Times(1); |
| Sequence s1; |
| EXPECT_CALL(request_callback_, ResendPackets(_, 1)) |
| .With(Args<0, 1>(ElementsAre(4))) |
| .Times(0); |
| |
| EXPECT_CALL(decoder_, Copy()) |
| .Times(0); |
| EXPECT_CALL(decoderCopy_, Copy()) |
| .Times(0); |
| |
| // Decode operations |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 0), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 3000), |
| Field(&EncodedImage::_completeFrame, |
| false)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 6000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 9000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| |
| ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode( |
| VideoCodingModule::kNone, |
| VideoCodingModule::kAllowDecodeErrors)); |
| |
| InsertPacket(0, 0, true, false, kVideoFrameKey); |
| InsertPacket(0, 1, false, false, kVideoFrameKey); |
| InsertPacket(0, 2, false, true, kVideoFrameKey); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 0. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(3000, 3, true, false, kVideoFrameDelta); |
| // Packet 4 missing |
| InsertPacket(3000, 5, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(6000, 6, true, false, kVideoFrameDelta); |
| InsertPacket(6000, 7, false, false, kVideoFrameDelta); |
| InsertPacket(6000, 8, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 3000 incomplete. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(10); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 6000 complete. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(23); |
| InsertPacket(3000, 4, false, false, kVideoFrameDelta); |
| |
| InsertPacket(9000, 9, true, false, kVideoFrameDelta); |
| InsertPacket(9000, 10, false, false, kVideoFrameDelta); |
| InsertPacket(9000, 11, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 9000 complete. |
| } |
| |
| TEST_F(VCMRobustnessTest, TestModeNoneWithoutErrors) { |
| Sequence s1; |
| EXPECT_CALL(decoder_, InitDecode(_, _)).Times(1); |
| EXPECT_CALL(decoder_, Release()).Times(1); |
| EXPECT_CALL(request_callback_, ResendPackets(_, 1)) |
| .With(Args<0, 1>(ElementsAre(4))) |
| .Times(0); |
| |
| EXPECT_CALL(decoder_, Copy()) |
| .Times(0); |
| EXPECT_CALL(decoderCopy_, Copy()) |
| .Times(0); |
| |
| // Decode operations |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 0), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 3000), |
| Field(&EncodedImage::_completeFrame, |
| false)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(decoder_, Decode(AllOf(Field(&EncodedImage::_timeStamp, 6000), |
| Field(&EncodedImage::_completeFrame, |
| true)), |
| false, _, _, _)) |
| .Times(1) |
| .InSequence(s1); |
| EXPECT_CALL(frame_type_callback_, FrameTypeRequest(kVideoFrameKey)) |
| .Times(1); |
| |
| ASSERT_EQ(VCM_OK, vcm_->SetReceiverRobustnessMode( |
| VideoCodingModule::kNone, |
| VideoCodingModule::kNoDecodeErrors)); |
| |
| InsertPacket(0, 0, true, false, kVideoFrameKey); |
| InsertPacket(0, 1, false, false, kVideoFrameKey); |
| InsertPacket(0, 2, false, true, kVideoFrameKey); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 0. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(3000, 3, true, false, kVideoFrameDelta); |
| // Packet 4 missing |
| InsertPacket(3000, 5, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(33); |
| InsertPacket(6000, 6, true, false, kVideoFrameDelta); |
| InsertPacket(6000, 7, false, false, kVideoFrameDelta); |
| InsertPacket(6000, 8, false, true, kVideoFrameDelta); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 3000 incomplete. |
| // Schedule key frame request. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(10); |
| EXPECT_EQ(VCM_OK, vcm_->Decode(0)); // Decode timestamp 6000 complete. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect no NACK list. |
| |
| clock_->IncrementDebugClock(500); // Wait for the key request timer to set. |
| EXPECT_EQ(VCM_OK, vcm_->Process()); // Expect key frame request. |
| } |
| } // namespace webrtc |