|  | /* | 
|  | *  Copyright 2017 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/rtp_video_stream_receiver.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "api/video/video_codec_type.h" | 
|  | #include "api/video/video_frame_type.h" | 
|  | #include "common_video/h264/h264_common.h" | 
|  | #include "media/base/media_constants.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_format.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_format_vp9.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_header_extensions.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_packet_received.h" | 
|  | #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" | 
|  | #include "modules/utility/include/process_thread.h" | 
|  | #include "modules/video_coding/frame_object.h" | 
|  | #include "modules/video_coding/include/video_coding_defines.h" | 
|  | #include "modules/video_coding/packet.h" | 
|  | #include "modules/video_coding/rtp_frame_reference_finder.h" | 
|  | #include "rtc_base/byte_buffer.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "system_wrappers/include/clock.h" | 
|  | #include "system_wrappers/include/field_trial.h" | 
|  | #include "test/field_trial.h" | 
|  | #include "test/gmock.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::Invoke; | 
|  | using ::testing::SizeIs; | 
|  | using ::testing::Values; | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01}; | 
|  |  | 
|  | class MockTransport : public Transport { | 
|  | public: | 
|  | MOCK_METHOD3(SendRtp, | 
|  | bool(const uint8_t* packet, | 
|  | size_t length, | 
|  | const PacketOptions& options)); | 
|  | MOCK_METHOD2(SendRtcp, bool(const uint8_t* packet, size_t length)); | 
|  | }; | 
|  |  | 
|  | class MockNackSender : public NackSender { | 
|  | public: | 
|  | MOCK_METHOD1(SendNack, void(const std::vector<uint16_t>& sequence_numbers)); | 
|  | MOCK_METHOD2(SendNack, | 
|  | void(const std::vector<uint16_t>& sequence_numbers, | 
|  | bool buffering_allowed)); | 
|  | }; | 
|  |  | 
|  | class MockKeyFrameRequestSender : public KeyFrameRequestSender { | 
|  | public: | 
|  | MOCK_METHOD0(RequestKeyFrame, void()); | 
|  | }; | 
|  |  | 
|  | class MockOnCompleteFrameCallback | 
|  | : public video_coding::OnCompleteFrameCallback { | 
|  | public: | 
|  | MockOnCompleteFrameCallback() : buffer_(rtc::ByteBuffer::ORDER_NETWORK) {} | 
|  |  | 
|  | MOCK_METHOD1(DoOnCompleteFrame, void(video_coding::EncodedFrame* frame)); | 
|  | MOCK_METHOD1(DoOnCompleteFrameFailNullptr, | 
|  | void(video_coding::EncodedFrame* frame)); | 
|  | MOCK_METHOD1(DoOnCompleteFrameFailLength, | 
|  | void(video_coding::EncodedFrame* frame)); | 
|  | MOCK_METHOD1(DoOnCompleteFrameFailBitstream, | 
|  | void(video_coding::EncodedFrame* frame)); | 
|  | void OnCompleteFrame(std::unique_ptr<video_coding::EncodedFrame> frame) { | 
|  | if (!frame) { | 
|  | DoOnCompleteFrameFailNullptr(nullptr); | 
|  | return; | 
|  | } | 
|  | EXPECT_EQ(buffer_.Length(), frame->size()); | 
|  | if (buffer_.Length() != frame->size()) { | 
|  | DoOnCompleteFrameFailLength(frame.get()); | 
|  | return; | 
|  | } | 
|  | if (frame->size() != buffer_.Length() || | 
|  | memcmp(buffer_.Data(), frame->data(), buffer_.Length()) != 0) { | 
|  | DoOnCompleteFrameFailBitstream(frame.get()); | 
|  | return; | 
|  | } | 
|  | DoOnCompleteFrame(frame.get()); | 
|  | } | 
|  |  | 
|  | void ClearExpectedBitstream() { buffer_.Clear(); } | 
|  |  | 
|  | void AppendExpectedBitstream(const uint8_t data[], size_t size_in_bytes) { | 
|  | // TODO(Johan): Let rtc::ByteBuffer handle uint8_t* instead of char*. | 
|  | buffer_.WriteBytes(reinterpret_cast<const char*>(data), size_in_bytes); | 
|  | } | 
|  | rtc::ByteBufferWriter buffer_; | 
|  | }; | 
|  |  | 
|  | class MockRtpPacketSink : public RtpPacketSinkInterface { | 
|  | public: | 
|  | MOCK_METHOD1(OnRtpPacket, void(const RtpPacketReceived&)); | 
|  | }; | 
|  |  | 
|  | constexpr uint32_t kSsrc = 111; | 
|  | constexpr uint16_t kSequenceNumber = 222; | 
|  | std::unique_ptr<RtpPacketReceived> CreateRtpPacketReceived( | 
|  | uint32_t ssrc = kSsrc, | 
|  | uint16_t sequence_number = kSequenceNumber) { | 
|  | auto packet = std::make_unique<RtpPacketReceived>(); | 
|  | packet->SetSsrc(ssrc); | 
|  | packet->SetSequenceNumber(sequence_number); | 
|  | return packet; | 
|  | } | 
|  |  | 
|  | MATCHER_P(SamePacketAs, other, "") { | 
|  | return arg.Ssrc() == other.Ssrc() && | 
|  | arg.SequenceNumber() == other.SequenceNumber(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class RtpVideoStreamReceiverTest : public ::testing::Test { | 
|  | public: | 
|  | RtpVideoStreamReceiverTest() : RtpVideoStreamReceiverTest("") {} | 
|  | explicit RtpVideoStreamReceiverTest(std::string field_trials) | 
|  | : override_field_trials_(field_trials), | 
|  | config_(CreateConfig()), | 
|  | process_thread_(ProcessThread::Create("TestThread")) {} | 
|  |  | 
|  | void SetUp() { | 
|  | rtp_receive_statistics_ = | 
|  | ReceiveStatistics::Create(Clock::GetRealTimeClock()); | 
|  | rtp_video_stream_receiver_ = std::make_unique<RtpVideoStreamReceiver>( | 
|  | Clock::GetRealTimeClock(), &mock_transport_, nullptr, nullptr, &config_, | 
|  | rtp_receive_statistics_.get(), nullptr, process_thread_.get(), | 
|  | &mock_nack_sender_, &mock_key_frame_request_sender_, | 
|  | &mock_on_complete_frame_callback_, nullptr); | 
|  | } | 
|  |  | 
|  | RTPVideoHeader GetDefaultH264VideoHeader() { | 
|  | RTPVideoHeader video_header; | 
|  | video_header.codec = kVideoCodecH264; | 
|  | video_header.video_type_header.emplace<RTPVideoHeaderH264>(); | 
|  | return video_header; | 
|  | } | 
|  |  | 
|  | // TODO(Johan): refactor h264_sps_pps_tracker_unittests.cc to avoid duplicate | 
|  | // code. | 
|  | void AddSps(RTPVideoHeader* video_header, | 
|  | uint8_t sps_id, | 
|  | std::vector<uint8_t>* data) { | 
|  | NaluInfo info; | 
|  | info.type = H264::NaluType::kSps; | 
|  | info.sps_id = sps_id; | 
|  | info.pps_id = -1; | 
|  | data->push_back(H264::NaluType::kSps); | 
|  | data->push_back(sps_id); | 
|  | auto& h264 = absl::get<RTPVideoHeaderH264>(video_header->video_type_header); | 
|  | h264.nalus[h264.nalus_length++] = info; | 
|  | } | 
|  |  | 
|  | void AddPps(RTPVideoHeader* video_header, | 
|  | uint8_t sps_id, | 
|  | uint8_t pps_id, | 
|  | std::vector<uint8_t>* data) { | 
|  | NaluInfo info; | 
|  | info.type = H264::NaluType::kPps; | 
|  | info.sps_id = sps_id; | 
|  | info.pps_id = pps_id; | 
|  | data->push_back(H264::NaluType::kPps); | 
|  | data->push_back(pps_id); | 
|  | auto& h264 = absl::get<RTPVideoHeaderH264>(video_header->video_type_header); | 
|  | h264.nalus[h264.nalus_length++] = info; | 
|  | } | 
|  |  | 
|  | void AddIdr(RTPVideoHeader* video_header, int pps_id) { | 
|  | NaluInfo info; | 
|  | info.type = H264::NaluType::kIdr; | 
|  | info.sps_id = -1; | 
|  | info.pps_id = pps_id; | 
|  | auto& h264 = absl::get<RTPVideoHeaderH264>(video_header->video_type_header); | 
|  | h264.nalus[h264.nalus_length++] = info; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | static VideoReceiveStream::Config CreateConfig() { | 
|  | VideoReceiveStream::Config config(nullptr); | 
|  | config.rtp.remote_ssrc = 1111; | 
|  | config.rtp.local_ssrc = 2222; | 
|  | return config; | 
|  | } | 
|  |  | 
|  | const webrtc::test::ScopedFieldTrials override_field_trials_; | 
|  | VideoReceiveStream::Config config_; | 
|  | MockNackSender mock_nack_sender_; | 
|  | MockKeyFrameRequestSender mock_key_frame_request_sender_; | 
|  | MockTransport mock_transport_; | 
|  | MockOnCompleteFrameCallback mock_on_complete_frame_callback_; | 
|  | std::unique_ptr<ProcessThread> process_thread_; | 
|  | std::unique_ptr<ReceiveStatistics> rtp_receive_statistics_; | 
|  | std::unique_ptr<RtpVideoStreamReceiver> rtp_video_stream_receiver_; | 
|  | }; | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, CacheColorSpaceFromLastPacketOfKeyframe) { | 
|  | // Test that color space is cached from the last packet of a key frame and | 
|  | // that it's not reset by padding packets without color space. | 
|  | constexpr int kPayloadType = 99; | 
|  | const ColorSpace kColorSpace( | 
|  | ColorSpace::PrimaryID::kFILM, ColorSpace::TransferID::kBT2020_12, | 
|  | ColorSpace::MatrixID::kBT2020_NCL, ColorSpace::RangeID::kFull); | 
|  | const std::vector<uint8_t> kKeyFramePayload = {0, 1, 2, 3, 4, 5, | 
|  | 6, 7, 8, 9, 10}; | 
|  | const std::vector<uint8_t> kDeltaFramePayload = {0, 1, 2, 3, 4}; | 
|  |  | 
|  | // Anonymous helper class that generates received packets. | 
|  | class { | 
|  | public: | 
|  | void SetPayload(const std::vector<uint8_t>& payload, | 
|  | VideoFrameType video_frame_type) { | 
|  | video_frame_type_ = video_frame_type; | 
|  | RtpPacketizer::PayloadSizeLimits pay_load_size_limits; | 
|  | // Reduce max payload length to make sure the key frame generates two | 
|  | // packets. | 
|  | pay_load_size_limits.max_payload_len = 8; | 
|  | RTPVideoHeaderVP9 rtp_video_header_vp9; | 
|  | rtp_video_header_vp9.InitRTPVideoHeaderVP9(); | 
|  | rtp_video_header_vp9.inter_pic_predicted = | 
|  | (video_frame_type == VideoFrameType::kVideoFrameDelta); | 
|  | rtp_packetizer_ = std::make_unique<RtpPacketizerVp9>( | 
|  | payload, pay_load_size_limits, rtp_video_header_vp9); | 
|  | } | 
|  |  | 
|  | size_t NumPackets() { return rtp_packetizer_->NumPackets(); } | 
|  | void SetColorSpace(const ColorSpace& color_space) { | 
|  | color_space_ = color_space; | 
|  | } | 
|  |  | 
|  | RtpPacketReceived NextPacket() { | 
|  | RtpHeaderExtensionMap extension_map; | 
|  | extension_map.Register<ColorSpaceExtension>(1); | 
|  | RtpPacketToSend packet_to_send(&extension_map); | 
|  | packet_to_send.SetSequenceNumber(sequence_number_++); | 
|  | packet_to_send.SetSsrc(kSsrc); | 
|  | packet_to_send.SetPayloadType(kPayloadType); | 
|  | bool include_color_space = | 
|  | (rtp_packetizer_->NumPackets() == 1u && | 
|  | video_frame_type_ == VideoFrameType::kVideoFrameKey); | 
|  | if (include_color_space) { | 
|  | EXPECT_TRUE( | 
|  | packet_to_send.SetExtension<ColorSpaceExtension>(color_space_)); | 
|  | } | 
|  | rtp_packetizer_->NextPacket(&packet_to_send); | 
|  |  | 
|  | RtpPacketReceived received_packet(&extension_map); | 
|  | received_packet.Parse(packet_to_send.data(), packet_to_send.size()); | 
|  | return received_packet; | 
|  | } | 
|  |  | 
|  | private: | 
|  | uint16_t sequence_number_ = 0; | 
|  | VideoFrameType video_frame_type_; | 
|  | ColorSpace color_space_; | 
|  | std::unique_ptr<RtpPacketizer> rtp_packetizer_; | 
|  | } received_packet_generator; | 
|  | received_packet_generator.SetColorSpace(kColorSpace); | 
|  |  | 
|  | // Prepare the receiver for VP9. | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | codec.codecType = kVideoCodecVP9; | 
|  | std::map<std::string, std::string> codec_params; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, codec_params, | 
|  | /*raw_payload=*/false); | 
|  |  | 
|  | // Generate key frame packets. | 
|  | received_packet_generator.SetPayload(kKeyFramePayload, | 
|  | VideoFrameType::kVideoFrameKey); | 
|  | EXPECT_EQ(received_packet_generator.NumPackets(), 2u); | 
|  | RtpPacketReceived key_frame_packet1 = received_packet_generator.NextPacket(); | 
|  | RtpPacketReceived key_frame_packet2 = received_packet_generator.NextPacket(); | 
|  |  | 
|  | // Generate delta frame packet. | 
|  | received_packet_generator.SetPayload(kDeltaFramePayload, | 
|  | VideoFrameType::kVideoFrameDelta); | 
|  | EXPECT_EQ(received_packet_generator.NumPackets(), 1u); | 
|  | RtpPacketReceived delta_frame_packet = received_packet_generator.NextPacket(); | 
|  |  | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kKeyFramePayload.data(), kKeyFramePayload.size()); | 
|  |  | 
|  | // Send the key frame and expect a callback with color space information. | 
|  | EXPECT_FALSE(key_frame_packet1.GetExtension<ColorSpaceExtension>()); | 
|  | EXPECT_TRUE(key_frame_packet2.GetExtension<ColorSpaceExtension>()); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet1); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)) | 
|  | .WillOnce(Invoke([kColorSpace](video_coding::EncodedFrame* frame) { | 
|  | ASSERT_TRUE(frame->EncodedImage().ColorSpace()); | 
|  | EXPECT_EQ(*frame->EncodedImage().ColorSpace(), kColorSpace); | 
|  | })); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet2); | 
|  | // Resend the first key frame packet to simulate padding for example. | 
|  | rtp_video_stream_receiver_->OnRtpPacket(key_frame_packet1); | 
|  |  | 
|  | mock_on_complete_frame_callback_.ClearExpectedBitstream(); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kDeltaFramePayload.data(), kDeltaFramePayload.size()); | 
|  |  | 
|  | // Expect delta frame to have color space set even though color space not | 
|  | // included in the RTP packet. | 
|  | EXPECT_FALSE(delta_frame_packet.GetExtension<ColorSpaceExtension>()); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)) | 
|  | .WillOnce(Invoke([kColorSpace](video_coding::EncodedFrame* frame) { | 
|  | ASSERT_TRUE(frame->EncodedImage().ColorSpace()); | 
|  | EXPECT_EQ(*frame->EncodedImage().ColorSpace(), kColorSpace); | 
|  | })); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(delta_frame_packet); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, GenericKeyFrame) { | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header; | 
|  | const std::vector<uint8_t> data({1, 2, 3, 4}); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | video_header.is_last_packet_in_frame = true; | 
|  | video_header.codec = kVideoCodecGeneric; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameKey; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), | 
|  | data.size()); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, NoInfiniteRecursionOnEncapsulatedRedPacket) { | 
|  | const uint8_t kRedPayloadType = 125; | 
|  | VideoCodec codec; | 
|  | codec.plType = kRedPayloadType; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, {}, /*raw_payload=*/false); | 
|  | const std::vector<uint8_t> data({ | 
|  | 0x80,              // RTP version. | 
|  | kRedPayloadType,   // Payload type. | 
|  | 0, 0, 0, 0, 0, 0,  // Don't care. | 
|  | 0, 0, 0x4, 0x57,   // SSRC | 
|  | kRedPayloadType,   // RED header. | 
|  | 0, 0, 0, 0, 0      // Don't care. | 
|  | }); | 
|  | RtpPacketReceived packet; | 
|  | EXPECT_TRUE(packet.Parse(data.data(), data.size())); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(packet); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, | 
|  | DropsPacketWithRedPayloadTypeAndEmptyPayload) { | 
|  | const uint8_t kRedPayloadType = 125; | 
|  | config_.rtp.red_payload_type = kRedPayloadType; | 
|  | SetUp();  // re-create rtp_video_stream_receiver with red payload type. | 
|  | // clang-format off | 
|  | const uint8_t data[] = { | 
|  | 0x80,              // RTP version. | 
|  | kRedPayloadType,   // Payload type. | 
|  | 0, 0, 0, 0, 0, 0,  // Don't care. | 
|  | 0, 0, 0x4, 0x57,   // SSRC | 
|  | // Empty rtp payload. | 
|  | }; | 
|  | // clang-format on | 
|  | RtpPacketReceived packet; | 
|  | // Manually convert to CopyOnWriteBuffer to be sure capacity == size | 
|  | // and asan bot can catch read buffer overflow. | 
|  | EXPECT_TRUE(packet.Parse(rtc::CopyOnWriteBuffer(data))); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(packet); | 
|  | // Expect asan doesn't find anything. | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, GenericKeyFrameBitstreamError) { | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header; | 
|  | const std::vector<uint8_t> data({1, 2, 3, 4}); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | video_header.is_last_packet_in_frame = true; | 
|  | video_header.codec = kVideoCodecGeneric; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameKey; | 
|  | constexpr uint8_t expected_bitsteam[] = {1, 2, 3, 0xff}; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | expected_bitsteam, sizeof(expected_bitsteam)); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, | 
|  | DoOnCompleteFrameFailBitstream(_)); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | class RtpVideoStreamReceiverTestH264 | 
|  | : public RtpVideoStreamReceiverTest, | 
|  | public ::testing::WithParamInterface<std::string> { | 
|  | protected: | 
|  | RtpVideoStreamReceiverTestH264() : RtpVideoStreamReceiverTest(GetParam()) {} | 
|  | }; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe, | 
|  | RtpVideoStreamReceiverTestH264, | 
|  | Values("", "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/")); | 
|  |  | 
|  | TEST_P(RtpVideoStreamReceiverTestH264, InBandSpsPps) { | 
|  | std::vector<uint8_t> sps_data; | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader sps_video_header = GetDefaultH264VideoHeader(); | 
|  | AddSps(&sps_video_header, 0, &sps_data); | 
|  | rtp_packet.SetSequenceNumber(0); | 
|  | sps_video_header.is_first_packet_in_frame = true; | 
|  | sps_video_header.frame_type = VideoFrameType::kEmptyFrame; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(sps_data.data(), | 
|  | sps_data.size()); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(sps_data, rtp_packet, | 
|  | sps_video_header); | 
|  |  | 
|  | std::vector<uint8_t> pps_data; | 
|  | RTPVideoHeader pps_video_header = GetDefaultH264VideoHeader(); | 
|  | AddPps(&pps_video_header, 0, 1, &pps_data); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  | pps_video_header.is_first_packet_in_frame = true; | 
|  | pps_video_header.frame_type = VideoFrameType::kEmptyFrame; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(pps_data.data(), | 
|  | pps_data.size()); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(pps_data, rtp_packet, | 
|  | pps_video_header); | 
|  |  | 
|  | std::vector<uint8_t> idr_data; | 
|  | RTPVideoHeader idr_video_header = GetDefaultH264VideoHeader(); | 
|  | AddIdr(&idr_video_header, 1); | 
|  | rtp_packet.SetSequenceNumber(2); | 
|  | idr_video_header.is_first_packet_in_frame = true; | 
|  | idr_video_header.is_last_packet_in_frame = true; | 
|  | idr_video_header.frame_type = VideoFrameType::kVideoFrameKey; | 
|  | idr_data.insert(idr_data.end(), {0x65, 1, 2, 3}); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(idr_data.data(), | 
|  | idr_data.size()); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(idr_data, rtp_packet, | 
|  | idr_video_header); | 
|  | } | 
|  |  | 
|  | TEST_P(RtpVideoStreamReceiverTestH264, OutOfBandFmtpSpsPps) { | 
|  | constexpr int kPayloadType = 99; | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | std::map<std::string, std::string> codec_params; | 
|  | // Example parameter sets from https://tools.ietf.org/html/rfc3984#section-8.2 | 
|  | // . | 
|  | codec_params.insert( | 
|  | {cricket::kH264FmtpSpropParameterSets, "Z0IACpZTBYmI,aMljiA=="}); | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, codec_params, | 
|  | /*raw_payload=*/false); | 
|  | const uint8_t binary_sps[] = {0x67, 0x42, 0x00, 0x0a, 0x96, | 
|  | 0x53, 0x05, 0x89, 0x88}; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_sps, | 
|  | sizeof(binary_sps)); | 
|  | const uint8_t binary_pps[] = {0x68, 0xc9, 0x63, 0x88}; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(binary_pps, | 
|  | sizeof(binary_pps)); | 
|  |  | 
|  | std::vector<uint8_t> data; | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header = GetDefaultH264VideoHeader(); | 
|  | AddIdr(&video_header, 0); | 
|  | rtp_packet.SetPayloadType(kPayloadType); | 
|  | rtp_packet.SetSequenceNumber(2); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | video_header.is_last_packet_in_frame = true; | 
|  | video_header.codec = kVideoCodecH264; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameKey; | 
|  | data.insert(data.end(), {1, 2, 3}); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream( | 
|  | kH264StartCode, sizeof(kH264StartCode)); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), | 
|  | data.size()); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, PaddingInMediaStream) { | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header = GetDefaultH264VideoHeader(); | 
|  | std::vector<uint8_t> data; | 
|  | data.insert(data.end(), {1, 2, 3}); | 
|  | rtp_packet.SetPayloadType(99); | 
|  | rtp_packet.SetSequenceNumber(2); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | video_header.is_last_packet_in_frame = true; | 
|  | video_header.codec = kVideoCodecGeneric; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameKey; | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), | 
|  | data.size()); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  |  | 
|  | rtp_packet.SetSequenceNumber(3); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData({}, rtp_packet, | 
|  | video_header); | 
|  |  | 
|  | rtp_packet.SetSequenceNumber(4); | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameDelta; | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  |  | 
|  | rtp_packet.SetSequenceNumber(6); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_)); | 
|  | rtp_packet.SetSequenceNumber(5); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData({}, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, RequestKeyframeIfFirstFrameIsDelta) { | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header; | 
|  | const std::vector<uint8_t> data({1, 2, 3, 4}); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | video_header.is_last_packet_in_frame = true; | 
|  | video_header.codec = kVideoCodecGeneric; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameDelta; | 
|  | EXPECT_CALL(mock_key_frame_request_sender_, RequestKeyFrame()); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, RequestKeyframeWhenPacketBufferGetsFull) { | 
|  | constexpr int kPacketBufferMaxSize = 2048; | 
|  |  | 
|  | RtpPacketReceived rtp_packet; | 
|  | RTPVideoHeader video_header; | 
|  | const std::vector<uint8_t> data({1, 2, 3, 4}); | 
|  | video_header.is_first_packet_in_frame = true; | 
|  | // Incomplete frames so that the packet buffer is filling up. | 
|  | video_header.is_last_packet_in_frame = false; | 
|  | video_header.codec = kVideoCodecGeneric; | 
|  | video_header.frame_type = VideoFrameType::kVideoFrameDelta; | 
|  | uint16_t start_sequence_number = 1234; | 
|  | rtp_packet.SetSequenceNumber(start_sequence_number); | 
|  | while (rtp_packet.SequenceNumber() - start_sequence_number < | 
|  | kPacketBufferMaxSize) { | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | rtp_packet.SetSequenceNumber(rtp_packet.SequenceNumber() + 2); | 
|  | } | 
|  |  | 
|  | EXPECT_CALL(mock_key_frame_request_sender_, RequestKeyFrame()); | 
|  | rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet, | 
|  | video_header); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, SecondarySinksGetRtpNotifications) { | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | MockRtpPacketSink secondary_sink_1; | 
|  | MockRtpPacketSink secondary_sink_2; | 
|  |  | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink_1); | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink_2); | 
|  |  | 
|  | auto rtp_packet = CreateRtpPacketReceived(); | 
|  | EXPECT_CALL(secondary_sink_1, OnRtpPacket(SamePacketAs(*rtp_packet))); | 
|  | EXPECT_CALL(secondary_sink_2, OnRtpPacket(SamePacketAs(*rtp_packet))); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet); | 
|  |  | 
|  | // Test tear-down. | 
|  | rtp_video_stream_receiver_->StopReceive(); | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink_1); | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink_2); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, RemovedSecondarySinksGetNoRtpNotifications) { | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | MockRtpPacketSink secondary_sink; | 
|  |  | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink); | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink); | 
|  |  | 
|  | auto rtp_packet = CreateRtpPacketReceived(); | 
|  |  | 
|  | EXPECT_CALL(secondary_sink, OnRtpPacket(_)).Times(0); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet); | 
|  |  | 
|  | // Test tear-down. | 
|  | rtp_video_stream_receiver_->StopReceive(); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, | 
|  | OnlyRemovedSecondarySinksExcludedFromNotifications) { | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | MockRtpPacketSink kept_secondary_sink; | 
|  | MockRtpPacketSink removed_secondary_sink; | 
|  |  | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&kept_secondary_sink); | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&removed_secondary_sink); | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&removed_secondary_sink); | 
|  |  | 
|  | auto rtp_packet = CreateRtpPacketReceived(); | 
|  | EXPECT_CALL(kept_secondary_sink, OnRtpPacket(SamePacketAs(*rtp_packet))); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet); | 
|  |  | 
|  | // Test tear-down. | 
|  | rtp_video_stream_receiver_->StopReceive(); | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&kept_secondary_sink); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverTest, | 
|  | SecondariesOfNonStartedStreamGetNoNotifications) { | 
|  | // Explicitly showing that the stream is not in the |started| state, | 
|  | // regardless of whether streams start out |started| or |stopped|. | 
|  | rtp_video_stream_receiver_->StopReceive(); | 
|  |  | 
|  | MockRtpPacketSink secondary_sink; | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink); | 
|  |  | 
|  | auto rtp_packet = CreateRtpPacketReceived(); | 
|  | EXPECT_CALL(secondary_sink, OnRtpPacket(_)).Times(0); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(*rtp_packet); | 
|  |  | 
|  | // Test tear-down. | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink); | 
|  | } | 
|  |  | 
|  | class RtpVideoStreamReceiverGenericDescriptorTest | 
|  | : public RtpVideoStreamReceiverTest, | 
|  | public ::testing::WithParamInterface<int> { | 
|  | public: | 
|  | void RegisterRtpGenericFrameDescriptorExtension( | 
|  | RtpHeaderExtensionMap* extension_map, | 
|  | int version) { | 
|  | constexpr int kId00 = 5; | 
|  | constexpr int kId01 = 6; | 
|  | switch (version) { | 
|  | case 0: | 
|  | extension_map->Register<RtpGenericFrameDescriptorExtension00>(kId00); | 
|  | return; | 
|  | case 1: | 
|  | extension_map->Register<RtpGenericFrameDescriptorExtension01>(kId01); | 
|  | return; | 
|  | } | 
|  | RTC_NOTREACHED(); | 
|  | } | 
|  |  | 
|  | bool SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | const RtpGenericFrameDescriptor& generic_descriptor, | 
|  | RtpPacketReceived* rtp_packet, | 
|  | int version) { | 
|  | switch (version) { | 
|  | case 0: | 
|  | return rtp_packet->SetExtension<RtpGenericFrameDescriptorExtension00>( | 
|  | generic_descriptor); | 
|  | case 1: | 
|  | return rtp_packet->SetExtension<RtpGenericFrameDescriptorExtension01>( | 
|  | generic_descriptor); | 
|  | } | 
|  | RTC_NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  | }; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(All, | 
|  | RtpVideoStreamReceiverGenericDescriptorTest, | 
|  | Values(0, 1)); | 
|  |  | 
|  | TEST_P(RtpVideoStreamReceiverGenericDescriptorTest, | 
|  | ParseGenericDescriptorOnePacket) { | 
|  | const int version = GetParam(); | 
|  |  | 
|  | const std::vector<uint8_t> data = {0, 1, 2, 3, 4}; | 
|  | const int kPayloadType = 123; | 
|  | const int kSpatialIndex = 1; | 
|  |  | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, {}, /*raw_payload=*/false); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | RtpHeaderExtensionMap extension_map; | 
|  | RegisterRtpGenericFrameDescriptorExtension(&extension_map, version); | 
|  | RtpPacketReceived rtp_packet(&extension_map); | 
|  |  | 
|  | RtpGenericFrameDescriptor generic_descriptor; | 
|  | generic_descriptor.SetFirstPacketInSubFrame(true); | 
|  | generic_descriptor.SetLastPacketInSubFrame(true); | 
|  | generic_descriptor.SetFrameId(100); | 
|  | generic_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex); | 
|  | generic_descriptor.AddFrameDependencyDiff(90); | 
|  | generic_descriptor.AddFrameDependencyDiff(80); | 
|  | ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | generic_descriptor, &rtp_packet, version)); | 
|  |  | 
|  | uint8_t* payload = rtp_packet.SetPayloadSize(data.size()); | 
|  | memcpy(payload, data.data(), data.size()); | 
|  | // The first byte is the header, so we ignore the first byte of |data|. | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data() + 1, | 
|  | data.size() - 1); | 
|  |  | 
|  | rtp_packet.SetMarker(true); | 
|  | rtp_packet.SetPayloadType(kPayloadType); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame) | 
|  | .WillOnce(Invoke([kSpatialIndex](video_coding::EncodedFrame* frame) { | 
|  | EXPECT_EQ(frame->num_references, 2U); | 
|  | EXPECT_EQ(frame->references[0], frame->id.picture_id - 90); | 
|  | EXPECT_EQ(frame->references[1], frame->id.picture_id - 80); | 
|  | EXPECT_EQ(frame->id.spatial_layer, kSpatialIndex); | 
|  | EXPECT_THAT(frame->PacketInfos(), SizeIs(1)); | 
|  | })); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(rtp_packet); | 
|  | } | 
|  |  | 
|  | TEST_P(RtpVideoStreamReceiverGenericDescriptorTest, | 
|  | ParseGenericDescriptorTwoPackets) { | 
|  | const int version = GetParam(); | 
|  |  | 
|  | const std::vector<uint8_t> data = {0, 1, 2, 3, 4}; | 
|  | const int kPayloadType = 123; | 
|  | const int kSpatialIndex = 1; | 
|  |  | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, {}, /*raw_payload=*/false); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | RtpHeaderExtensionMap extension_map; | 
|  | RegisterRtpGenericFrameDescriptorExtension(&extension_map, version); | 
|  | RtpPacketReceived first_packet(&extension_map); | 
|  |  | 
|  | RtpGenericFrameDescriptor first_packet_descriptor; | 
|  | first_packet_descriptor.SetFirstPacketInSubFrame(true); | 
|  | first_packet_descriptor.SetLastPacketInSubFrame(false); | 
|  | first_packet_descriptor.SetFrameId(100); | 
|  | first_packet_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex); | 
|  | first_packet_descriptor.SetResolution(480, 360); | 
|  | ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | first_packet_descriptor, &first_packet, version)); | 
|  |  | 
|  | uint8_t* first_packet_payload = first_packet.SetPayloadSize(data.size()); | 
|  | memcpy(first_packet_payload, data.data(), data.size()); | 
|  | // The first byte is the header, so we ignore the first byte of |data|. | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data() + 1, | 
|  | data.size() - 1); | 
|  |  | 
|  | first_packet.SetPayloadType(kPayloadType); | 
|  | first_packet.SetSequenceNumber(1); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(first_packet); | 
|  |  | 
|  | RtpPacketReceived second_packet(&extension_map); | 
|  | RtpGenericFrameDescriptor second_packet_descriptor; | 
|  | second_packet_descriptor.SetFirstPacketInSubFrame(false); | 
|  | second_packet_descriptor.SetLastPacketInSubFrame(true); | 
|  | ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | second_packet_descriptor, &second_packet, version)); | 
|  |  | 
|  | second_packet.SetMarker(true); | 
|  | second_packet.SetPayloadType(kPayloadType); | 
|  | second_packet.SetSequenceNumber(2); | 
|  |  | 
|  | uint8_t* second_packet_payload = second_packet.SetPayloadSize(data.size()); | 
|  | memcpy(second_packet_payload, data.data(), data.size()); | 
|  | // The first byte is the header, so we ignore the first byte of |data|. | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data() + 1, | 
|  | data.size() - 1); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame) | 
|  | .WillOnce(Invoke([kSpatialIndex](video_coding::EncodedFrame* frame) { | 
|  | EXPECT_EQ(frame->num_references, 0U); | 
|  | EXPECT_EQ(frame->id.spatial_layer, kSpatialIndex); | 
|  | EXPECT_EQ(frame->EncodedImage()._encodedWidth, 480u); | 
|  | EXPECT_EQ(frame->EncodedImage()._encodedHeight, 360u); | 
|  | EXPECT_THAT(frame->PacketInfos(), SizeIs(2)); | 
|  | })); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(second_packet); | 
|  | } | 
|  |  | 
|  | TEST_F(RtpVideoStreamReceiverGenericDescriptorTest, | 
|  | DropPacketsWithMultipleVersionsOfExtension) { | 
|  | const std::vector<uint8_t> data = {0, 1, 2, 3, 4}; | 
|  | const int kPayloadType = 123; | 
|  |  | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, {}, /*raw_payload=*/false); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | RtpHeaderExtensionMap extension_map; | 
|  | RegisterRtpGenericFrameDescriptorExtension(&extension_map, 0); | 
|  | RegisterRtpGenericFrameDescriptorExtension(&extension_map, 1); | 
|  | RtpPacketReceived rtp_packet(&extension_map); | 
|  |  | 
|  | RtpGenericFrameDescriptor generic_descriptors[2]; | 
|  | for (size_t i = 0; i < 2; ++i) { | 
|  | generic_descriptors[i].SetFirstPacketInSubFrame(true); | 
|  | generic_descriptors[i].SetLastPacketInSubFrame(true); | 
|  | generic_descriptors[i].SetFrameId(100); | 
|  | ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | generic_descriptors[i], &rtp_packet, i)); | 
|  | } | 
|  |  | 
|  | uint8_t* payload = rtp_packet.SetPayloadSize(data.size()); | 
|  | memcpy(payload, data.data(), data.size()); | 
|  | // The first byte is the header, so we ignore the first byte of |data|. | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data() + 1, | 
|  | data.size() - 1); | 
|  |  | 
|  | rtp_packet.SetMarker(true); | 
|  | rtp_packet.SetPayloadType(kPayloadType); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0); | 
|  |  | 
|  | rtp_video_stream_receiver_->OnRtpPacket(rtp_packet); | 
|  | } | 
|  |  | 
|  | TEST_P(RtpVideoStreamReceiverGenericDescriptorTest, | 
|  | ParseGenericDescriptorRawPayload) { | 
|  | const int version = GetParam(); | 
|  |  | 
|  | const std::vector<uint8_t> data = {0, 1, 2, 3, 4}; | 
|  | const int kPayloadType = 123; | 
|  |  | 
|  | VideoCodec codec; | 
|  | codec.plType = kPayloadType; | 
|  | rtp_video_stream_receiver_->AddReceiveCodec(codec, {}, /*raw_payload=*/true); | 
|  | rtp_video_stream_receiver_->StartReceive(); | 
|  |  | 
|  | RtpHeaderExtensionMap extension_map; | 
|  | RegisterRtpGenericFrameDescriptorExtension(&extension_map, version); | 
|  | RtpPacketReceived rtp_packet(&extension_map); | 
|  |  | 
|  | RtpGenericFrameDescriptor generic_descriptor; | 
|  | generic_descriptor.SetFirstPacketInSubFrame(true); | 
|  | generic_descriptor.SetLastPacketInSubFrame(true); | 
|  | ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension( | 
|  | generic_descriptor, &rtp_packet, version)); | 
|  |  | 
|  | uint8_t* payload = rtp_packet.SetPayloadSize(data.size()); | 
|  | memcpy(payload, data.data(), data.size()); | 
|  | mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data(), | 
|  | data.size()); | 
|  |  | 
|  | rtp_packet.SetMarker(true); | 
|  | rtp_packet.SetPayloadType(kPayloadType); | 
|  | rtp_packet.SetSequenceNumber(1); | 
|  |  | 
|  | EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame); | 
|  | rtp_video_stream_receiver_->OnRtpPacket(rtp_packet); | 
|  | } | 
|  |  | 
|  | #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
|  | TEST_F(RtpVideoStreamReceiverTest, RepeatedSecondarySinkDisallowed) { | 
|  | MockRtpPacketSink secondary_sink; | 
|  |  | 
|  | rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink); | 
|  | EXPECT_DEATH(rtp_video_stream_receiver_->AddSecondarySink(&secondary_sink), | 
|  | ""); | 
|  |  | 
|  | // Test tear-down. | 
|  | rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | }  // namespace webrtc |