diff options
Diffstat (limited to 'third_party/libwebrtc/api/video/rtp_video_frame_assembler_unittests.cc')
-rw-r--r-- | third_party/libwebrtc/api/video/rtp_video_frame_assembler_unittests.cc | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/third_party/libwebrtc/api/video/rtp_video_frame_assembler_unittests.cc b/third_party/libwebrtc/api/video/rtp_video_frame_assembler_unittests.cc new file mode 100644 index 0000000000..82defb8399 --- /dev/null +++ b/third_party/libwebrtc/api/video/rtp_video_frame_assembler_unittests.cc @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2021 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 "api/array_view.h" +#include "api/video/rtp_video_frame_assembler.h" +#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_format.h" +#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "modules/rtp_rtcp/source/rtp_packetizer_av1_test_helper.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Matches; +using ::testing::SizeIs; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; +using PayloadFormat = RtpVideoFrameAssembler::PayloadFormat; + +class PacketBuilder { + public: + explicit PacketBuilder(PayloadFormat format) + : format_(format), packet_to_send_(&extension_manager_) {} + + PacketBuilder& WithSeqNum(uint16_t seq_num) { + seq_num_ = seq_num; + return *this; + } + + PacketBuilder& WithPayload(rtc::ArrayView<const uint8_t> payload) { + payload_.assign(payload.begin(), payload.end()); + return *this; + } + + PacketBuilder& WithVideoHeader(const RTPVideoHeader& video_header) { + video_header_ = video_header; + return *this; + } + + template <typename T, typename... Args> + PacketBuilder& WithExtension(int id, const Args&... args) { + extension_manager_.Register<T>(id); + packet_to_send_.IdentifyExtensions(extension_manager_); + packet_to_send_.SetExtension<T>(std::forward<const Args>(args)...); + return *this; + } + + RtpPacketReceived Build() { + auto packetizer = + RtpPacketizer::Create(GetVideoCodecType(), payload_, {}, video_header_); + packetizer->NextPacket(&packet_to_send_); + packet_to_send_.SetSequenceNumber(seq_num_); + + RtpPacketReceived received(&extension_manager_); + received.Parse(packet_to_send_.Buffer()); + return received; + } + + private: + absl::optional<VideoCodecType> GetVideoCodecType() { + switch (format_) { + case PayloadFormat::kRaw: { + return absl::nullopt; + } + case PayloadFormat::kH264: { + return kVideoCodecH264; + } + case PayloadFormat::kVp8: { + return kVideoCodecVP8; + } + case PayloadFormat::kVp9: { + return kVideoCodecVP9; + } + case PayloadFormat::kAv1: { + return kVideoCodecAV1; + } + case PayloadFormat::kGeneric: { + return kVideoCodecGeneric; + } + } + RTC_DCHECK_NOTREACHED(); + return absl::nullopt; + } + + const RtpVideoFrameAssembler::PayloadFormat format_; + uint16_t seq_num_ = 0; + std::vector<uint8_t> payload_; + RTPVideoHeader video_header_; + RtpPacketReceived::ExtensionManager extension_manager_; + RtpPacketToSend packet_to_send_; +}; + +RtpPacketReceived PaddingPacket(uint16_t seq_num) { + RtpPacketReceived padding_packet; + padding_packet.SetSequenceNumber(seq_num); + padding_packet.SetPadding(224); + return padding_packet; +} + +void AppendFrames(RtpVideoFrameAssembler::FrameVector from, + RtpVideoFrameAssembler::FrameVector& to) { + to.insert(to.end(), std::make_move_iterator(from.begin()), + std::make_move_iterator(from.end())); +} + +rtc::ArrayView<int64_t> References(const std::unique_ptr<EncodedFrame>& frame) { + return rtc::MakeArrayView(frame->references, frame->num_references); +} + +rtc::ArrayView<uint8_t> Payload(const std::unique_ptr<EncodedFrame>& frame) { + return rtc::ArrayView<uint8_t>(*frame->GetEncodedData()); +} + +TEST(RtpVideoFrameAssembler, Vp8Packetization) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kVp8); + + // When sending VP8 over RTP parts of the payload is actually inspected at the + // RTP level. It just so happen that the initial 'V' sets the keyframe bit + // (0x01) to the correct value. + uint8_t kKeyframePayload[] = "Vp8Keyframe"; + ASSERT_EQ(kKeyframePayload[0] & 0x01, 0); + + uint8_t kDeltaframePayload[] = "SomeFrame"; + ASSERT_EQ(kDeltaframePayload[0] & 0x01, 1); + + RtpVideoFrameAssembler::FrameVector frames; + + RTPVideoHeader video_header; + auto& vp8_header = + video_header.video_type_header.emplace<RTPVideoHeaderVP8>(); + + vp8_header.pictureId = 10; + vp8_header.tl0PicIdx = 0; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kVp8) + .WithPayload(kKeyframePayload) + .WithVideoHeader(video_header) + .Build()), + frames); + + vp8_header.pictureId = 11; + vp8_header.tl0PicIdx = 1; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kVp8) + .WithPayload(kDeltaframePayload) + .WithVideoHeader(video_header) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(References(first_frame), IsEmpty()); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kKeyframePayload)); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(11)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaframePayload)); +} + +TEST(RtpVideoFrameAssembler, Vp9Packetization) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kVp9); + RtpVideoFrameAssembler::FrameVector frames; + + uint8_t kPayload[] = "SomePayload"; + + RTPVideoHeader video_header; + auto& vp9_header = + video_header.video_type_header.emplace<RTPVideoHeaderVP9>(); + vp9_header.InitRTPVideoHeaderVP9(); + + vp9_header.picture_id = 10; + vp9_header.tl0_pic_idx = 0; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kVp9) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .Build()), + frames); + + vp9_header.picture_id = 11; + vp9_header.tl0_pic_idx = 1; + vp9_header.inter_pic_predicted = true; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kVp9) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(11)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); +} + +TEST(RtpVideoFrameAssembler, Av1Packetization) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kAv1); + RtpVideoFrameAssembler::FrameVector frames; + + auto kKeyframePayload = + BuildAv1Frame({Av1Obu(kAv1ObuTypeSequenceHeader).WithPayload({1, 2, 3}), + Av1Obu(kAv1ObuTypeFrame).WithPayload({4, 5, 6})}); + + auto kDeltaframePayload = + BuildAv1Frame({Av1Obu(kAv1ObuTypeFrame).WithPayload({7, 8, 9})}); + + RTPVideoHeader video_header; + + video_header.frame_type = VideoFrameType::kVideoFrameKey; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kAv1) + .WithPayload(kKeyframePayload) + .WithVideoHeader(video_header) + .WithSeqNum(20) + .Build()), + frames); + + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kAv1) + .WithPayload(kDeltaframePayload) + .WithSeqNum(21) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(20)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kKeyframePayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(21)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaframePayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(20)); +} + +TEST(RtpVideoFrameAssembler, RawPacketizationDependencyDescriptorExtension) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kRaw); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = "SomePayload"; + + FrameDependencyStructure dependency_structure; + dependency_structure.num_decode_targets = 1; + dependency_structure.num_chains = 1; + dependency_structure.decode_target_protected_by_chain.push_back(0); + dependency_structure.templates.push_back( + FrameDependencyTemplate().S(0).T(0).Dtis("S").ChainDiffs({0})); + dependency_structure.templates.push_back( + FrameDependencyTemplate().S(0).T(0).Dtis("S").ChainDiffs({10}).FrameDiffs( + {10})); + + DependencyDescriptor dependency_descriptor; + + dependency_descriptor.frame_number = 10; + dependency_descriptor.frame_dependencies = dependency_structure.templates[0]; + dependency_descriptor.attached_structure = + std::make_unique<FrameDependencyStructure>(dependency_structure); + AppendFrames(assembler.InsertPacket( + PacketBuilder(PayloadFormat::kRaw) + .WithPayload(kPayload) + .WithExtension<RtpDependencyDescriptorExtension>( + 1, dependency_structure, dependency_descriptor) + .Build()), + frames); + + dependency_descriptor.frame_number = 20; + dependency_descriptor.frame_dependencies = dependency_structure.templates[1]; + dependency_descriptor.attached_structure.reset(); + AppendFrames(assembler.InsertPacket( + PacketBuilder(PayloadFormat::kRaw) + .WithPayload(kPayload) + .WithExtension<RtpDependencyDescriptorExtension>( + 1, dependency_structure, dependency_descriptor) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(10)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(20)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); +} + +TEST(RtpVideoFrameAssembler, RawPacketizationGenericDescriptor00Extension) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kRaw); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = "SomePayload"; + + RtpGenericFrameDescriptor generic; + + generic.SetFirstPacketInSubFrame(true); + generic.SetLastPacketInSubFrame(true); + generic.SetFrameId(100); + AppendFrames( + assembler.InsertPacket( + PacketBuilder(PayloadFormat::kRaw) + .WithPayload(kPayload) + .WithExtension<RtpGenericFrameDescriptorExtension00>(1, generic) + .Build()), + frames); + + generic.SetFrameId(102); + generic.AddFrameDependencyDiff(2); + AppendFrames( + assembler.InsertPacket( + PacketBuilder(PayloadFormat::kRaw) + .WithPayload(kPayload) + .WithExtension<RtpGenericFrameDescriptorExtension00>(1, generic) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(100)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(102)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(100)); +} + +TEST(RtpVideoFrameAssembler, RawPacketizationGenericPayloadDescriptor) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = "SomePayload"; + + RTPVideoHeader video_header; + + video_header.frame_type = VideoFrameType::kVideoFrameKey; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(123) + .Build()), + frames); + + video_header.frame_type = VideoFrameType::kVideoFrameDelta; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(124) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(2)); + + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(123)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(124)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(123)); +} + +TEST(RtpVideoFrameAssembler, Padding) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = "SomePayload"; + + RTPVideoHeader video_header; + + video_header.frame_type = VideoFrameType::kVideoFrameKey; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(123) + .Build()), + frames); + + video_header.frame_type = VideoFrameType::kVideoFrameDelta; + AppendFrames(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(125) + .Build()), + frames); + + ASSERT_THAT(frames, SizeIs(1)); + auto first_frame = frames[0].ExtractFrame(); + EXPECT_THAT(first_frame->Id(), Eq(123)); + EXPECT_THAT(Payload(first_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(first_frame), IsEmpty()); + + AppendFrames(assembler.InsertPacket(PaddingPacket(/*seq_num=*/124)), frames); + + ASSERT_THAT(frames, SizeIs(2)); + auto second_frame = frames[1].ExtractFrame(); + EXPECT_THAT(second_frame->Id(), Eq(125)); + EXPECT_THAT(Payload(second_frame), ElementsAreArray(kPayload)); + EXPECT_THAT(References(second_frame), UnorderedElementsAre(123)); +} + +TEST(RtpVideoFrameAssembler, ClearOldPackets) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + + // If we don't have a payload the packet will be counted as a padding packet. + uint8_t kPayload[] = "DontCare"; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(0) + .Build()), + SizeIs(1)); + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(2000) + .Build()), + SizeIs(1)); + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(0) + .Build()), + SizeIs(0)); + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(1) + .Build()), + SizeIs(1)); +} + +TEST(RtpVideoFrameAssembler, ClearOldPacketsWithPadding) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + uint8_t kPayload[] = "DontCare"; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(0) + .Build()), + SizeIs(1)); + + EXPECT_THAT(assembler.InsertPacket(PaddingPacket(/*seq_num=*/2000)), + SizeIs(0)); + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(0) + .Build()), + SizeIs(0)); + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(1) + .Build()), + SizeIs(1)); +} + +TEST(RtpVideoFrameAssembler, SeqNumStartAndSeqNumEndSet) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = + "Some payload that will get split into two when packetized."; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = sizeof(kPayload) - 1; + + auto packetizer = + RtpPacketizer::Create(kVideoCodecGeneric, kPayload, limits, video_header); + ASSERT_THAT(packetizer->NumPackets(), Eq(2U)); + + RtpPacketReceived::ExtensionManager extension_manager; + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(123); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(124); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + AppendFrames(assembler.InsertPacket(received_packet), frames); + } + + ASSERT_THAT(frames, SizeIs(1)); + EXPECT_THAT(frames[0].RtpSeqNumStart(), Eq(123)); + EXPECT_THAT(frames[0].RtpSeqNumEnd(), Eq(124)); +} + +TEST(RtpVideoFrameAssembler, SeqNumStartAndSeqNumEndSetWhenPaddingReceived) { + RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kGeneric); + RtpVideoFrameAssembler::FrameVector frames; + uint8_t kPayload[] = + "Some payload that will get split into two when packetized."; + + RTPVideoHeader video_header; + video_header.frame_type = VideoFrameType::kVideoFrameKey; + + EXPECT_THAT(assembler.InsertPacket(PacketBuilder(PayloadFormat::kGeneric) + .WithPayload(kPayload) + .WithVideoHeader(video_header) + .WithSeqNum(121) + .Build()), + SizeIs(1)); + + video_header.frame_type = VideoFrameType::kVideoFrameDelta; + RtpPacketReceived::ExtensionManager extension_manager; + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = sizeof(kPayload) - 1; + + auto packetizer = + RtpPacketizer::Create(kVideoCodecGeneric, kPayload, limits, video_header); + ASSERT_THAT(packetizer->NumPackets(), Eq(2U)); + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(123); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + { + RtpPacketToSend send_packet(&extension_manager); + packetizer->NextPacket(&send_packet); + send_packet.SetSequenceNumber(124); + RtpPacketReceived received_packet(&extension_manager); + received_packet.Parse(send_packet.Buffer()); + assembler.InsertPacket(received_packet); + } + + AppendFrames(assembler.InsertPacket(PaddingPacket(/*seq_num=*/122)), frames); + + ASSERT_THAT(frames, SizeIs(1)); + EXPECT_THAT(frames[0].RtpSeqNumStart(), Eq(123)); + EXPECT_THAT(frames[0].RtpSeqNumEnd(), Eq(124)); +} + +} // namespace +} // namespace webrtc |