
instead of creating each time an object with RtpDepacketizer interface this moves packet payload memcpy from RtpVideoStreamReceiver into the depacketizers with possibility to remove it from there in follow ups. Bug: webrtc:11152 Change-Id: If474207eb84d7e9d0207075bd395e60895f0d842 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/162185 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30095}
992 lines
39 KiB
C++
992 lines
39 KiB
C++
/*
|
|
* 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 <utility>
|
|
|
|
#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/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::ElementsAre;
|
|
using ::testing::Invoke;
|
|
using ::testing::SizeIs;
|
|
using ::testing::Values;
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
const uint8_t kH264StartCode[] = {0x00, 0x00, 0x00, 0x01};
|
|
|
|
std::vector<uint64_t> GetAbsoluteCaptureTimestamps(
|
|
const video_coding::EncodedFrame* frame) {
|
|
std::vector<uint64_t> result;
|
|
for (const auto& packet_info : frame->PacketInfos()) {
|
|
if (packet_info.absolute_capture_time()) {
|
|
result.push_back(
|
|
packet_info.absolute_capture_time()->absolute_capture_timestamp);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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:
|
|
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,
|
|
rtc::CopyOnWriteBuffer* data) {
|
|
NaluInfo info;
|
|
info.type = H264::NaluType::kSps;
|
|
info.sps_id = sps_id;
|
|
info.pps_id = -1;
|
|
data->AppendData({H264::NaluType::kSps, 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,
|
|
rtc::CopyOnWriteBuffer* data) {
|
|
NaluInfo info;
|
|
info.type = H264::NaluType::kPps;
|
|
info.sps_id = sps_id;
|
|
info.pps_id = pps_id;
|
|
data->AppendData({H264::NaluType::kPps, 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;
|
|
rtc::CopyOnWriteBuffer 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, PacketInfoIsPropagatedIntoVideoFrames) {
|
|
constexpr uint64_t kAbsoluteCaptureTimestamp = 12;
|
|
constexpr int kId0 = 1;
|
|
|
|
RtpHeaderExtensionMap extension_map;
|
|
extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
|
|
RtpPacketReceived rtp_packet(&extension_map);
|
|
RTPVideoHeader video_header;
|
|
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
|
|
rtp_packet.SetSequenceNumber(1);
|
|
rtp_packet.SetTimestamp(1);
|
|
rtp_packet.SetSsrc(kSsrc);
|
|
rtp_packet.SetExtension<AbsoluteCaptureTimeExtension>(
|
|
AbsoluteCaptureTime{kAbsoluteCaptureTimestamp,
|
|
/*estimated_capture_clock_offset=*/absl::nullopt});
|
|
|
|
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(_))
|
|
.WillOnce(Invoke(
|
|
[kAbsoluteCaptureTimestamp](video_coding::EncodedFrame* frame) {
|
|
EXPECT_THAT(GetAbsoluteCaptureTimestamps(frame),
|
|
ElementsAre(kAbsoluteCaptureTimestamp));
|
|
}));
|
|
rtp_video_stream_receiver_->OnReceivedPayloadData(data, rtp_packet,
|
|
video_header);
|
|
}
|
|
|
|
TEST_F(RtpVideoStreamReceiverTest,
|
|
MissingAbsoluteCaptureTimeIsFilledWithExtrapolatedValue) {
|
|
constexpr uint64_t kAbsoluteCaptureTimestamp = 12;
|
|
constexpr int kId0 = 1;
|
|
|
|
RtpHeaderExtensionMap extension_map;
|
|
extension_map.Register<AbsoluteCaptureTimeExtension>(kId0);
|
|
RtpPacketReceived rtp_packet(&extension_map);
|
|
|
|
RTPVideoHeader video_header;
|
|
rtc::CopyOnWriteBuffer data({1, 2, 3, 4});
|
|
uint16_t sequence_number = 1;
|
|
uint32_t rtp_timestamp = 1;
|
|
rtp_packet.SetSequenceNumber(sequence_number);
|
|
rtp_packet.SetTimestamp(rtp_timestamp);
|
|
rtp_packet.SetSsrc(kSsrc);
|
|
rtp_packet.SetExtension<AbsoluteCaptureTimeExtension>(
|
|
AbsoluteCaptureTime{kAbsoluteCaptureTimestamp,
|
|
/*estimated_capture_clock_offset=*/absl::nullopt});
|
|
|
|
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 without absolute capture time.
|
|
rtp_packet = RtpPacketReceived(&extension_map);
|
|
rtp_packet.SetSequenceNumber(++sequence_number);
|
|
rtp_packet.SetTimestamp(++rtp_timestamp);
|
|
rtp_packet.SetSsrc(kSsrc);
|
|
|
|
// There is no absolute capture time in the second packet.
|
|
// Expect rtp video stream receiver to extrapolate it for the resulting video
|
|
// frame using absolute capture time from the previous packet.
|
|
EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame(_))
|
|
.WillOnce(Invoke([](video_coding::EncodedFrame* frame) {
|
|
EXPECT_THAT(GetAbsoluteCaptureTimestamps(frame), SizeIs(1));
|
|
}));
|
|
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;
|
|
rtc::CopyOnWriteBuffer 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) {
|
|
rtc::CopyOnWriteBuffer 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);
|
|
|
|
rtc::CopyOnWriteBuffer 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);
|
|
|
|
rtc::CopyOnWriteBuffer 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;
|
|
const uint8_t idr[] = {0x65, 1, 2, 3};
|
|
idr_data.AppendData(idr);
|
|
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));
|
|
|
|
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;
|
|
rtc::CopyOnWriteBuffer data({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();
|
|
rtc::CopyOnWriteBuffer data({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;
|
|
rtc::CopyOnWriteBuffer 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;
|
|
rtc::CopyOnWriteBuffer 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
|