| /* |
| * Copyright (c) 2019 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 "modules/rtp_rtcp/source/video_rtp_depacketizer_av1.h" |
| |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| using ::testing::ElementsAre; |
| |
| // Signals number of the OBU (fragments) in the packet. |
| constexpr uint8_t kObuCountOne = 0b00'01'0000; |
| |
| constexpr uint8_t kObuHeaderSequenceHeader = 0b0'0001'000; |
| constexpr uint8_t kObuHeaderFrame = 0b0'0110'000; |
| |
| constexpr uint8_t kObuHeaderHasSize = 0b0'0000'010; |
| |
| TEST(VideoRtpDepacketizerAv1Test, ParsePassFullRtpPayloadAsCodecPayload) { |
| const uint8_t packet[] = {(uint8_t{1} << 7) | kObuCountOne, 1, 2, 3, 4}; |
| rtc::CopyOnWriteBuffer rtp_payload(packet); |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtp_payload); |
| ASSERT_TRUE(parsed); |
| EXPECT_EQ(parsed->video_payload.size(), sizeof(packet)); |
| EXPECT_TRUE(parsed->video_payload.cdata() == rtp_payload.cdata()); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| ParseTreatsContinuationFlagAsNotBeginningOfFrame) { |
| const uint8_t packet[] = { |
| (uint8_t{1} << 7) | kObuCountOne, |
| kObuHeaderFrame}; // Value doesn't matter since it is a |
| // continuation of the OBU from previous packet. |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtc::CopyOnWriteBuffer(packet)); |
| ASSERT_TRUE(parsed); |
| EXPECT_FALSE(parsed->video_header.is_first_packet_in_frame); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| ParseTreatsNoContinuationFlagAsBeginningOfFrame) { |
| const uint8_t packet[] = {(uint8_t{0} << 7) | kObuCountOne, kObuHeaderFrame}; |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtc::CopyOnWriteBuffer(packet)); |
| ASSERT_TRUE(parsed); |
| EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, ParseTreatsWillContinueFlagAsNotEndOfFrame) { |
| const uint8_t packet[] = {(uint8_t{1} << 6) | kObuCountOne, kObuHeaderFrame}; |
| rtc::CopyOnWriteBuffer rtp_payload(packet); |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtp_payload); |
| ASSERT_TRUE(parsed); |
| EXPECT_FALSE(parsed->video_header.is_last_packet_in_frame); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, ParseTreatsNoWillContinueFlagAsEndOfFrame) { |
| const uint8_t packet[] = {(uint8_t{0} << 6) | kObuCountOne, kObuHeaderFrame}; |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtc::CopyOnWriteBuffer(packet)); |
| ASSERT_TRUE(parsed); |
| EXPECT_TRUE(parsed->video_header.is_last_packet_in_frame); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| ParseUsesNewCodedVideoSequenceBitAsKeyFrameIndidcator) { |
| const uint8_t packet[] = {(uint8_t{1} << 3) | kObuCountOne, |
| kObuHeaderSequenceHeader}; |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtc::CopyOnWriteBuffer(packet)); |
| ASSERT_TRUE(parsed); |
| EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); |
| EXPECT_TRUE(parsed->video_header.frame_type == |
| VideoFrameType::kVideoFrameKey); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| ParseUsesUnsetNewCodedVideoSequenceBitAsDeltaFrameIndidcator) { |
| const uint8_t packet[] = {(uint8_t{0} << 3) | kObuCountOne, |
| kObuHeaderSequenceHeader}; |
| VideoRtpDepacketizerAv1 depacketizer; |
| std::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed = |
| depacketizer.Parse(rtc::CopyOnWriteBuffer(packet)); |
| ASSERT_TRUE(parsed); |
| EXPECT_TRUE(parsed->video_header.is_first_packet_in_frame); |
| EXPECT_TRUE(parsed->video_header.frame_type == |
| VideoFrameType::kVideoFrameDelta); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| ParseRejectsPacketWithNewCVSAndContinuationFlagsBothSet) { |
| const uint8_t packet[] = {0b10'00'1000 | kObuCountOne, |
| kObuHeaderSequenceHeader}; |
| VideoRtpDepacketizerAv1 depacketizer; |
| ASSERT_FALSE(depacketizer.Parse(rtc::CopyOnWriteBuffer(packet))); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenAbsent) { |
| const uint8_t payload1[] = {0b00'01'0000, // aggregation header |
| 0b0'0110'000, // / Frame |
| 20, 30, 40}; // \ OBU |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); |
| EXPECT_EQ(frame_view[1], 3); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameSetsOBUPayloadSizeWhenPresent) { |
| const uint8_t payload1[] = {0b00'01'0000, // aggregation header |
| 0b0'0110'010, // / Frame OBU header |
| 3, // obu_size |
| 20, |
| 30, |
| 40}; // \ obu_payload |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); |
| EXPECT_EQ(frame_view[1], 3); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameSetsOBUPayloadSizeAfterExtensionWhenAbsent) { |
| const uint8_t payload1[] = {0b00'01'0000, // aggregation header |
| 0b0'0110'100, // / Frame |
| 0b010'01'000, // | extension_header |
| 20, 30, 40}; // \ OBU |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); |
| EXPECT_EQ(frame_view[2], 3); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameSetsOBUPayloadSizeAfterExtensionWhenPresent) { |
| const uint8_t payload1[] = {0b00'01'0000, // aggregation header |
| 0b0'0110'110, // / Frame OBU header |
| 0b010'01'000, // | extension_header |
| 3, // | obu_size |
| 20, |
| 30, |
| 40}; // \ obu_payload |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_TRUE(frame_view[0] & kObuHeaderHasSize); |
| EXPECT_EQ(frame_view[2], 3); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithOneObu) { |
| const uint8_t payload1[] = {0b00'01'0000, // aggregation header |
| 0b0'0110'000, // / Frame |
| 20}; // \ OBU |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0110'010, 1, 20)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromOnePacketWithTwoObus) { |
| const uint8_t payload1[] = {0b00'10'0000, // aggregation header |
| 2, // / Sequence |
| 0b0'0001'000, // | Header |
| 10, // \ OBU |
| 0b0'0110'000, // / Frame |
| 20}; // \ OBU |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0001'010, 1, 10, // Sequence Header OBU |
| 0b0'0110'010, 1, 20)); // Frame OBU |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithOneObu) { |
| const uint8_t payload1[] = {0b01'01'0000, // aggregation header |
| 0b0'0110'000, 20, 30}; |
| const uint8_t payload2[] = {0b10'01'0000, // aggregation header |
| 40}; |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0110'010, 3, 20, 30, 40)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameFromTwoPacketsWithTwoObu) { |
| const uint8_t payload1[] = {0b01'10'0000, // aggregation header |
| 2, // / Sequence |
| 0b0'0001'000, // | Header |
| 10, // \ OBU |
| 0b0'0110'000, // |
| 20, |
| 30}; // |
| const uint8_t payload2[] = {0b10'01'0000, // aggregation header |
| 40}; // |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0001'010, 1, 10, // SH |
| 0b0'0110'010, 3, 20, 30, 40)); // Frame |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromTwoPacketsWithManyObusSomeWithExtensions) { |
| const uint8_t payload1[] = {0b01'00'0000, // aggregation header |
| 2, // / |
| 0b0'0001'000, // | Sequence Header |
| 10, // \ OBU |
| 2, // / |
| 0b0'0101'000, // | Metadata OBU |
| 20, // \ without extension |
| 4, // / |
| 0b0'0101'100, // | Metadata OBU |
| 0b001'10'000, // | with extension |
| 20, // | |
| 30, // \ metadata payload |
| 5, // / |
| 0b0'0110'100, // | Frame OBU |
| 0b001'10'000, // | with extension |
| 40, // | |
| 50, // | |
| 60}; // | |
| const uint8_t payload2[] = {0b10'01'0000, // aggregation header |
| 70, 80, 90}; // \ tail of the frame OBU |
| |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre( // Sequence header OBU |
| 0b0'0001'010, 1, 10, |
| // Metadata OBU without extension |
| 0b0'0101'010, 1, 20, |
| // Metadata OBU with extenion |
| 0b0'0101'110, 0b001'10'000, 2, 20, 30, |
| // Frame OBU with extension |
| 0b0'0110'110, 0b001'10'000, 6, 40, 50, 60, 70, 80, 90)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, AssembleFrameWithOneObuFromManyPackets) { |
| const uint8_t payload1[] = {0b01'01'0000, // aggregation header |
| 0b0'0110'000, 11, 12}; |
| const uint8_t payload2[] = {0b11'01'0000, // aggregation header |
| 13, 14}; |
| const uint8_t payload3[] = {0b11'01'0000, // aggregation header |
| 15, 16, 17}; |
| const uint8_t payload4[] = {0b10'01'0000, // aggregation header |
| 18}; |
| |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3, |
| payload4}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0110'010, 8, 11, 12, 13, 14, 15, 16, 17, 18)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromManyPacketsWithSomeObuBorderAligned) { |
| const uint8_t payload1[] = {0b01'10'0000, // aggregation header |
| 3, // size of the 1st fragment |
| 0b0'0011'000, // Frame header OBU |
| 11, |
| 12, |
| 0b0'0100'000, // Tile group OBU |
| 21, |
| 22, |
| 23}; |
| const uint8_t payload2[] = {0b10'01'0000, // aggregation header |
| 24, 25, 26, 27}; |
| // payload2 ends an OBU, payload3 starts a new one. |
| const uint8_t payload3[] = {0b01'10'0000, // aggregation header |
| 3, // size of the 1st fragment |
| 0b0'0111'000, // Redundant frame header OBU |
| 11, |
| 12, |
| 0b0'0100'000, // Tile group OBU |
| 31, |
| 32}; |
| const uint8_t payload4[] = {0b10'01'0000, // aggregation header |
| 33, 34, 35, 36}; |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2, payload3, |
| payload4}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0011'010, 2, 11, 12, // Frame header |
| 0b0'0100'010, 7, 21, 22, 23, 24, 25, 26, 27, // |
| 0b0'0111'010, 2, 11, 12, // |
| 0b0'0100'010, 6, 31, 32, 33, 34, 35, 36)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromOnePacketsOneObuPayloadSize127Bytes) { |
| uint8_t payload1[4 + 127]; |
| memset(payload1, 0, sizeof(payload1)); |
| payload1[0] = 0b00'00'0000; // aggregation header |
| payload1[1] = 0x80; // leb128 encoded size of 128 bytes |
| payload1[2] = 0x01; // in two bytes |
| payload1[3] = 0b0'0110'000; // obu_header with size and extension bits unset. |
| payload1[4 + 42] = 0x42; |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_EQ(frame->size(), 2 + 127u); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set. |
| EXPECT_EQ(frame_view[1], 127); // obu payload size, 1 byte enough to encode. |
| // Check 'random' byte from the payload is at the same 'random' offset. |
| EXPECT_EQ(frame_view[2 + 42], 0x42); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromTwoPacketsOneObuPayloadSize128Bytes) { |
| uint8_t payload1[3 + 32]; |
| memset(payload1, 0, sizeof(payload1)); |
| payload1[0] = 0b01'00'0000; // aggregation header |
| payload1[1] = 33; // leb128 encoded size of 33 bytes in one byte |
| payload1[2] = 0b0'0110'000; // obu_header with size and extension bits unset. |
| payload1[3 + 10] = 0x10; |
| uint8_t payload2[2 + 96]; |
| memset(payload2, 0, sizeof(payload2)); |
| payload2[0] = 0b10'00'0000; // aggregation header |
| payload2[1] = 96; // leb128 encoded size of 96 bytes in one byte |
| payload2[2 + 20] = 0x20; |
| |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_EQ(frame->size(), 3 + 128u); |
| rtc::ArrayView<const uint8_t> frame_view(*frame); |
| EXPECT_EQ(frame_view[0], 0b0'0110'010); // obu_header with size bit set. |
| EXPECT_EQ(frame_view[1], 0x80); // obu payload size of 128 bytes. |
| EXPECT_EQ(frame_view[2], 0x01); // encoded in two byes |
| // Check two 'random' byte from the payload is at the same 'random' offset. |
| EXPECT_EQ(frame_view[3 + 10], 0x10); |
| EXPECT_EQ(frame_view[3 + 32 + 20], 0x20); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromAlmostEmptyPacketStartingAnOBU) { |
| const uint8_t payload1[] = {0b01'01'0000}; |
| const uint8_t payload2[] = {0b10'01'0000, 0b0'0110'000, 10, 20, 30}; |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0110'010, 3, 10, 20, 30)); |
| } |
| |
| TEST(VideoRtpDepacketizerAv1Test, |
| AssembleFrameFromAlmostEmptyPacketFinishingAnOBU) { |
| const uint8_t payload1[] = {0b01'01'0000, 0b0'0110'000, 10, 20, 30}; |
| const uint8_t payload2[] = {0b10'01'0000}; |
| rtc::ArrayView<const uint8_t> payloads[] = {payload1, payload2}; |
| |
| auto frame = VideoRtpDepacketizerAv1().AssembleFrame(payloads); |
| ASSERT_TRUE(frame); |
| EXPECT_THAT(rtc::ArrayView<const uint8_t>(*frame), |
| ElementsAre(0b0'0110'010, 3, 10, 20, 30)); |
| } |
| |
| } // namespace |
| } // namespace webrtc |