rtp_video_frame_h265_assembler_unittests.cc (5433B)
1 /* 2 * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <cstdint> 12 #include <iterator> 13 #include <memory> 14 #include <optional> 15 #include <utility> 16 #include <vector> 17 18 #include "api/array_view.h" 19 #include "api/video/encoded_frame.h" 20 #include "api/video/rtp_video_frame_assembler.h" 21 #include "api/video/video_codec_type.h" 22 #include "api/video/video_frame_type.h" 23 #include "modules/rtp_rtcp/source/rtp_format.h" 24 #include "modules/rtp_rtcp/source/rtp_packet_received.h" 25 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" 26 #include "modules/rtp_rtcp/source/rtp_video_header.h" 27 #include "rtc_base/checks.h" 28 #include "test/gmock.h" 29 #include "test/gtest.h" 30 31 namespace webrtc { 32 namespace { 33 34 using ::testing::ElementsAreArray; 35 using ::testing::Eq; 36 using ::testing::IsEmpty; 37 using ::testing::SizeIs; 38 using ::testing::UnorderedElementsAre; 39 using PayloadFormat = RtpVideoFrameAssembler::PayloadFormat; 40 41 class PacketBuilder { 42 public: 43 explicit PacketBuilder(PayloadFormat format) 44 : format_(format), packet_to_send_(&extension_manager_) {} 45 46 PacketBuilder& WithSeqNum(uint16_t seq_num) { 47 seq_num_ = seq_num; 48 return *this; 49 } 50 51 PacketBuilder& WithPayload(ArrayView<const uint8_t> payload) { 52 payload_.assign(payload.begin(), payload.end()); 53 return *this; 54 } 55 56 PacketBuilder& WithVideoHeader(const RTPVideoHeader& video_header) { 57 video_header_ = video_header; 58 return *this; 59 } 60 61 template <typename T, typename... Args> 62 PacketBuilder& WithExtension(int id, const Args&... args) { 63 extension_manager_.Register<T>(id); 64 packet_to_send_.IdentifyExtensions(extension_manager_); 65 packet_to_send_.SetExtension<T>(std::forward<const Args>(args)...); 66 return *this; 67 } 68 69 RtpPacketReceived Build() { 70 auto packetizer = 71 RtpPacketizer::Create(GetVideoCodecType(), payload_, {}, video_header_); 72 packetizer->NextPacket(&packet_to_send_); 73 packet_to_send_.SetSequenceNumber(seq_num_); 74 75 RtpPacketReceived received(&extension_manager_); 76 received.Parse(packet_to_send_.Buffer()); 77 return received; 78 } 79 80 private: 81 std::optional<VideoCodecType> GetVideoCodecType() { 82 switch (format_) { 83 case PayloadFormat::kH265: { 84 return kVideoCodecH265; 85 } 86 default: 87 RTC_DCHECK_NOTREACHED(); 88 return std::nullopt; 89 } 90 } 91 92 const RtpVideoFrameAssembler::PayloadFormat format_; 93 uint16_t seq_num_ = 0; 94 std::vector<uint8_t> payload_; 95 RTPVideoHeader video_header_; 96 RtpPacketReceived::ExtensionManager extension_manager_; 97 RtpPacketToSend packet_to_send_; 98 }; 99 100 void AppendFrames(RtpVideoFrameAssembler::FrameVector&& from, 101 RtpVideoFrameAssembler::FrameVector& to) { 102 to.insert(to.end(), std::make_move_iterator(from.begin()), 103 std::make_move_iterator(from.end())); 104 } 105 106 ArrayView<int64_t> References(const std::unique_ptr<EncodedFrame>& frame) { 107 return MakeArrayView(frame->references, frame->num_references); 108 } 109 110 ArrayView<const uint8_t> Payload(const std::unique_ptr<EncodedFrame>& frame) { 111 return *frame->GetEncodedData(); 112 } 113 114 TEST(RtpVideoFrameH265Assembler, H265Packetization) { 115 RtpVideoFrameAssembler assembler(RtpVideoFrameAssembler::kH265); 116 RtpVideoFrameAssembler::FrameVector frames; 117 118 // Key and delta frames generated on linux with ffmpeg command: 119 // `ffmpeg -i /dev/video0 -r 30 -c:v libx265 -s 1280x720 camera.h265`, 120 // truncated for test. 121 // IDR_N_LP(key) frame with start code included. 122 uint8_t kIdrPayload[] = {0x00, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, 123 0x08, 0x4a, 0x31, 0x11, 0x15, 0xe5, 0xc0}; 124 // TRAIL_R(delta) frame with start code included. 125 uint8_t kDeltaPayload[] = {0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0xd0, 126 0x09, 0x7e, 0x10, 0xc6, 0x1c, 0x8c, 0x17}; 127 128 RTPVideoHeader video_header; 129 video_header.frame_type = VideoFrameType::kVideoFrameKey; 130 RtpVideoFrameAssembler::FrameVector idr_frames = 131 assembler.InsertPacket(PacketBuilder(PayloadFormat::kH265) 132 .WithPayload(kIdrPayload) 133 .WithVideoHeader(video_header) 134 .WithSeqNum(10) 135 .Build()); 136 AppendFrames(std::move(idr_frames), frames); 137 138 RtpVideoFrameAssembler::FrameVector delta_frames = 139 assembler.InsertPacket(PacketBuilder(PayloadFormat::kH265) 140 .WithPayload(kDeltaPayload) 141 .WithSeqNum(11) 142 .Build()); 143 AppendFrames(std::move(delta_frames), frames); 144 ASSERT_THAT(frames, SizeIs(2)); 145 146 auto first_frame = frames[0].ExtractFrame(); 147 EXPECT_THAT(first_frame->Id(), Eq(10)); 148 EXPECT_THAT(Payload(first_frame), ElementsAreArray(kIdrPayload)); 149 EXPECT_THAT(References(first_frame), IsEmpty()); 150 151 auto second_frame = frames[1].ExtractFrame(); 152 EXPECT_THAT(second_frame->Id(), Eq(11)); 153 EXPECT_THAT(Payload(second_frame), ElementsAreArray(kDeltaPayload)); 154 EXPECT_THAT(References(second_frame), UnorderedElementsAre(10)); 155 } 156 157 } // namespace 158 } // namespace webrtc