|  | /* | 
|  | *  Copyright (c) 2012 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 <vector> | 
|  |  | 
|  | #include "webrtc/modules/video_coding/codecs/interface/mock/mock_video_codec_interface.h" | 
|  | #include "webrtc/modules/video_coding/main/interface/mock/mock_vcm_callbacks.h" | 
|  | #include "webrtc/modules/video_coding/main/interface/video_coding.h" | 
|  | #include "webrtc/system_wrappers/interface/scoped_ptr.h" | 
|  | #include "webrtc/system_wrappers/interface/tick_util.h" | 
|  |  | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::AllOf; | 
|  | using ::testing::ElementsAre; | 
|  | using ::testing::ElementsAreArray; | 
|  | using ::testing::Field; | 
|  | using ::testing::NiceMock; | 
|  | using ::testing::Pointee; | 
|  | using ::testing::Return; | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | class TestVideoCodingModule : public ::testing::Test { | 
|  | protected: | 
|  | static const int kDefaultWidth = 1280; | 
|  | static const int kDefaultHeight = 720; | 
|  | static const int kNumberOfStreams = 3; | 
|  | static const int kNumberOfLayers = 3; | 
|  | static const int kUnusedPayloadType = 10; | 
|  |  | 
|  | virtual void SetUp() { | 
|  | TickTime::UseFakeClock(0); | 
|  | vcm_ = VideoCodingModule::Create(0); | 
|  | EXPECT_EQ(0, vcm_->InitializeReceiver()); | 
|  | EXPECT_EQ(0, vcm_->InitializeSender()); | 
|  | EXPECT_EQ(0, vcm_->RegisterExternalEncoder(&encoder_, kUnusedPayloadType, | 
|  | false)); | 
|  | EXPECT_EQ(0, vcm_->RegisterExternalDecoder(&decoder_, kUnusedPayloadType, | 
|  | true)); | 
|  | memset(&settings_, 0, sizeof(settings_)); | 
|  | EXPECT_EQ(0, vcm_->Codec(kVideoCodecVP8, &settings_)); | 
|  | settings_.numberOfSimulcastStreams = kNumberOfStreams; | 
|  | ConfigureStream(kDefaultWidth / 4, kDefaultHeight / 4, 100, | 
|  | &settings_.simulcastStream[0]); | 
|  | ConfigureStream(kDefaultWidth / 2, kDefaultHeight / 2, 500, | 
|  | &settings_.simulcastStream[1]); | 
|  | ConfigureStream(kDefaultWidth, kDefaultHeight, 1200, | 
|  | &settings_.simulcastStream[2]); | 
|  | settings_.plType = kUnusedPayloadType;  // Use the mocked encoder. | 
|  | EXPECT_EQ(0, vcm_->RegisterSendCodec(&settings_, 1, 1200)); | 
|  | EXPECT_EQ(0, vcm_->RegisterReceiveCodec(&settings_, 1, true)); | 
|  | } | 
|  |  | 
|  | virtual void TearDown() { | 
|  | VideoCodingModule::Destroy(vcm_); | 
|  | } | 
|  |  | 
|  | void ExpectIntraRequest(int stream) { | 
|  | if (stream == -1) { | 
|  | // No intra request expected. | 
|  | EXPECT_CALL(encoder_, Encode( | 
|  | _, _, Pointee(ElementsAre(kDeltaFrame, kDeltaFrame, kDeltaFrame)))) | 
|  | .Times(1) | 
|  | .WillRepeatedly(Return(0)); | 
|  | return; | 
|  | } | 
|  | assert(stream >= 0); | 
|  | assert(stream < kNumberOfStreams); | 
|  | std::vector<VideoFrameType> frame_types(kNumberOfStreams, kDeltaFrame); | 
|  | frame_types[stream] = kKeyFrame; | 
|  | EXPECT_CALL(encoder_, Encode( | 
|  | _, _, Pointee(ElementsAreArray(&frame_types[0], frame_types.size())))) | 
|  | .Times(1) | 
|  | .WillRepeatedly(Return(0)); | 
|  | } | 
|  |  | 
|  | static void ConfigureStream(int width, int height, int max_bitrate, | 
|  | SimulcastStream* stream) { | 
|  | assert(stream); | 
|  | stream->width = width; | 
|  | stream->height = height; | 
|  | stream->maxBitrate = max_bitrate; | 
|  | stream->numberOfTemporalLayers = kNumberOfLayers; | 
|  | stream->qpMax = 45; | 
|  | } | 
|  |  | 
|  | void InsertAndVerifyPaddingFrame(const uint8_t* payload, int length, | 
|  | WebRtcRTPHeader* header) { | 
|  | ASSERT_TRUE(header != NULL); | 
|  | for (int j = 0; j < 5; ++j) { | 
|  | // Padding only packets are passed to the VCM with payload size 0. | 
|  | EXPECT_EQ(0, vcm_->IncomingPacket(payload, 0, *header)); | 
|  | ++header->header.sequenceNumber; | 
|  | } | 
|  | EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)) | 
|  | .Times(0); | 
|  | EXPECT_EQ(0, vcm_->Process()); | 
|  | EXPECT_CALL(decoder_, Decode(_, _, _, _, _)) | 
|  | .Times(0); | 
|  | EXPECT_EQ(VCM_FRAME_NOT_READY, vcm_->Decode(0)); | 
|  | } | 
|  |  | 
|  | void InsertAndVerifyDecodableFrame(const uint8_t* payload, int length, | 
|  | WebRtcRTPHeader* header) { | 
|  | ASSERT_TRUE(header != NULL); | 
|  | EXPECT_EQ(0, vcm_->IncomingPacket(payload, length, *header)); | 
|  | ++header->header.sequenceNumber; | 
|  | EXPECT_CALL(packet_request_callback_, ResendPackets(_, _)) | 
|  | .Times(0); | 
|  | EXPECT_EQ(0, vcm_->Process()); | 
|  | EXPECT_CALL(decoder_, Decode(_, _, _, _, _)) | 
|  | .Times(1); | 
|  | EXPECT_EQ(0, vcm_->Decode(0)); | 
|  | } | 
|  |  | 
|  | VideoCodingModule* vcm_; | 
|  | NiceMock<MockVideoDecoder> decoder_; | 
|  | NiceMock<MockVideoEncoder> encoder_; | 
|  | I420VideoFrame input_frame_; | 
|  | VideoCodec settings_; | 
|  | NiceMock<MockPacketRequestCallback> packet_request_callback_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TestVideoCodingModule, TestIntraRequests) { | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(0)); | 
|  | ExpectIntraRequest(0); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  | ExpectIntraRequest(-1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  |  | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(1)); | 
|  | ExpectIntraRequest(1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  | ExpectIntraRequest(-1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  |  | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(2)); | 
|  | ExpectIntraRequest(2); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  | ExpectIntraRequest(-1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  |  | 
|  | EXPECT_EQ(-1, vcm_->IntraFrameRequest(3)); | 
|  | ExpectIntraRequest(-1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  |  | 
|  | EXPECT_EQ(-1, vcm_->IntraFrameRequest(-1)); | 
|  | ExpectIntraRequest(-1); | 
|  | EXPECT_EQ(0, vcm_->AddVideoFrame(input_frame_, NULL, NULL)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVideoCodingModule, TestIntraRequestsInternalCapture) { | 
|  | // De-register current external encoder. | 
|  | EXPECT_EQ(0, vcm_->RegisterExternalEncoder(NULL, kUnusedPayloadType, false)); | 
|  | // Register encoder with internal capture. | 
|  | EXPECT_EQ(0, vcm_->RegisterExternalEncoder(&encoder_, kUnusedPayloadType, | 
|  | true)); | 
|  | EXPECT_EQ(0, vcm_->RegisterSendCodec(&settings_, 1, 1200)); | 
|  | ExpectIntraRequest(0); | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(0)); | 
|  | ExpectIntraRequest(1); | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(1)); | 
|  | ExpectIntraRequest(2); | 
|  | EXPECT_EQ(0, vcm_->IntraFrameRequest(2)); | 
|  | // No requests expected since these indices are out of bounds. | 
|  | EXPECT_EQ(-1, vcm_->IntraFrameRequest(3)); | 
|  | EXPECT_EQ(-1, vcm_->IntraFrameRequest(-1)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVideoCodingModule, PaddingOnlyFrames) { | 
|  | EXPECT_EQ(0, vcm_->SetVideoProtection(kProtectionNack, true)); | 
|  | EXPECT_EQ(0, vcm_->RegisterPacketRequestCallback(&packet_request_callback_)); | 
|  | const unsigned int kPaddingSize = 220; | 
|  | const uint8_t payload[kPaddingSize] = {0}; | 
|  | WebRtcRTPHeader header; | 
|  | memset(&header, 0, sizeof(header)); | 
|  | header.frameType = kFrameEmpty; | 
|  | header.header.markerBit = false; | 
|  | header.header.paddingLength = kPaddingSize; | 
|  | header.header.payloadType = kUnusedPayloadType; | 
|  | header.header.ssrc = 1; | 
|  | header.header.headerLength = 12; | 
|  | header.type.Video.codec = kRTPVideoVP8; | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | InsertAndVerifyPaddingFrame(payload, 0, &header); | 
|  | TickTime::AdvanceFakeClock(33); | 
|  | header.header.timestamp += 3000; | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(TestVideoCodingModule, PaddingOnlyFramesWithLosses) { | 
|  | EXPECT_EQ(0, vcm_->SetVideoProtection(kProtectionNack, true)); | 
|  | EXPECT_EQ(0, vcm_->RegisterPacketRequestCallback(&packet_request_callback_)); | 
|  | const unsigned int kFrameSize = 1200; | 
|  | const unsigned int kPaddingSize = 220; | 
|  | const uint8_t payload[kFrameSize] = {0}; | 
|  | WebRtcRTPHeader header; | 
|  | memset(&header, 0, sizeof(header)); | 
|  | header.frameType = kFrameEmpty; | 
|  | header.header.markerBit = false; | 
|  | header.header.paddingLength = kPaddingSize; | 
|  | header.header.payloadType = kUnusedPayloadType; | 
|  | header.header.ssrc = 1; | 
|  | header.header.headerLength = 12; | 
|  | header.type.Video.codec = kRTPVideoVP8; | 
|  | // Insert one video frame to get one frame decoded. | 
|  | header.frameType = kVideoFrameKey; | 
|  | header.type.Video.isFirstPacket = true; | 
|  | header.header.markerBit = true; | 
|  | InsertAndVerifyDecodableFrame(payload, kFrameSize, &header); | 
|  | TickTime::AdvanceFakeClock(33); | 
|  | header.header.timestamp += 3000; | 
|  |  | 
|  | header.frameType = kFrameEmpty; | 
|  | header.type.Video.isFirstPacket = false; | 
|  | header.header.markerBit = false; | 
|  | // Insert padding frames. | 
|  | for (int i = 0; i < 10; ++i) { | 
|  | // Lose the 4th frame. | 
|  | if (i == 3) { | 
|  | header.header.sequenceNumber += 5; | 
|  | ++i; | 
|  | } | 
|  | // Lose one packet from the 6th frame. | 
|  | if (i == 5) { | 
|  | ++header.header.sequenceNumber; | 
|  | } | 
|  | InsertAndVerifyPaddingFrame(payload, 0, &header); | 
|  | TickTime::AdvanceFakeClock(33); | 
|  | header.header.timestamp += 3000; | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(TestVideoCodingModule, PaddingOnlyAndVideo) { | 
|  | EXPECT_EQ(0, vcm_->SetVideoProtection(kProtectionNack, true)); | 
|  | EXPECT_EQ(0, vcm_->RegisterPacketRequestCallback(&packet_request_callback_)); | 
|  | const unsigned int kFrameSize = 1200; | 
|  | const unsigned int kPaddingSize = 220; | 
|  | const uint8_t payload[kFrameSize] = {0}; | 
|  | WebRtcRTPHeader header; | 
|  | memset(&header, 0, sizeof(header)); | 
|  | header.frameType = kFrameEmpty; | 
|  | header.type.Video.isFirstPacket = false; | 
|  | header.header.markerBit = false; | 
|  | header.header.paddingLength = kPaddingSize; | 
|  | header.header.payloadType = kUnusedPayloadType; | 
|  | header.header.ssrc = 1; | 
|  | header.header.headerLength = 12; | 
|  | header.type.Video.codec = kRTPVideoVP8; | 
|  | header.type.Video.codecHeader.VP8.pictureId = -1; | 
|  | header.type.Video.codecHeader.VP8.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 = kVideoFrameKey; | 
|  | else | 
|  | header.frameType = kVideoFrameDelta; | 
|  | header.type.Video.isFirstPacket = true; | 
|  | header.header.markerBit = true; | 
|  | InsertAndVerifyDecodableFrame(payload, kFrameSize, &header); | 
|  | TickTime::AdvanceFakeClock(33); | 
|  | header.header.timestamp += 3000; | 
|  | } | 
|  |  | 
|  | // Insert 2 padding only frames. | 
|  | header.frameType = kFrameEmpty; | 
|  | header.type.Video.isFirstPacket = false; | 
|  | header.header.markerBit = false; | 
|  | for (int j = 0; j < 2; ++j) { | 
|  | InsertAndVerifyPaddingFrame(payload, 0, &header); | 
|  | TickTime::AdvanceFakeClock(33); | 
|  | header.header.timestamp += 3000; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |