From faf282700c7fc35510e505ed7644db937084ec7b Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Tue, 19 Jun 2018 14:24:17 +0200 Subject: [PATCH] Add Parsing/Building generic frame descriptor extension Bug: webrtc:9361 Change-Id: I7e85826117348e2d4f4726e8d515bb1d4a289966 Reviewed-on: https://webrtc-review.googlesource.com/83622 Reviewed-by: Philip Eliasson Commit-Queue: Danil Chapovalov Cr-Commit-Position: refs/heads/master@{#23662} --- modules/rtp_rtcp/BUILD.gn | 3 + .../source/rtp_generic_frame_descriptor.cc | 2 +- .../source/rtp_generic_frame_descriptor.h | 2 +- .../rtp_generic_frame_descriptor_extension.cc | 143 +++++++++ .../rtp_generic_frame_descriptor_extension.h | 34 +++ ...ric_frame_descriptor_extension_unittest.cc | 283 ++++++++++++++++++ 6 files changed, 465 insertions(+), 2 deletions(-) create mode 100644 modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc create mode 100644 modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h create mode 100644 modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 286f1e0ff5..6d39e9e590 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -41,6 +41,7 @@ rtc_source_set("rtp_rtcp_format") { "source/rtcp_packet/transport_feedback.h", "source/rtcp_packet/voip_metric.h", "source/rtp_generic_frame_descriptor.h", + "source/rtp_generic_frame_descriptor_extension.h", "source/rtp_header_extensions.h", "source/rtp_packet.h", "source/rtp_packet_received.h", @@ -75,6 +76,7 @@ rtc_source_set("rtp_rtcp_format") { "source/rtcp_packet/transport_feedback.cc", "source/rtcp_packet/voip_metric.cc", "source/rtp_generic_frame_descriptor.cc", + "source/rtp_generic_frame_descriptor_extension.cc", "source/rtp_header_extension_map.cc", "source/rtp_header_extensions.cc", "source/rtp_packet.cc", @@ -381,6 +383,7 @@ if (rtc_include_tests) { "source/rtp_format_vp8_test_helper.h", "source/rtp_format_vp8_unittest.cc", "source/rtp_format_vp9_unittest.cc", + "source/rtp_generic_frame_descriptor_extension_unittest.cc", "source/rtp_header_extension_map_unittest.cc", "source/rtp_packet_history_unittest.cc", "source/rtp_packet_unittest.cc", diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc index caa81625c3..ab70b0da77 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.cc @@ -51,7 +51,7 @@ void RtpGenericFrameDescriptor::SetFrameId(uint16_t frame_id) { } rtc::ArrayView -RtpGenericFrameDescriptor::FrameDepedenciesDiffs() const { +RtpGenericFrameDescriptor::FrameDependenciesDiffs() const { RTC_DCHECK(FirstPacketInSubFrame()); return rtc::MakeArrayView(frame_deps_id_diffs_, num_frame_deps_); } diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h index 07536e7af6..51a9ac0022 100644 --- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h @@ -45,7 +45,7 @@ class RtpGenericFrameDescriptor { uint16_t FrameId() const; void SetFrameId(uint16_t frame_id); - rtc::ArrayView FrameDepedenciesDiffs() const; + rtc::ArrayView FrameDependenciesDiffs() const; void ClearFrameDependencies() { num_frame_deps_ = 0; } // Returns false on failure, i.e. number of dependencies is too large. bool AddFrameDependencyDiff(uint16_t fdiff); diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc new file mode 100644 index 0000000000..daf797aead --- /dev/null +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018 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/rtp_generic_frame_descriptor_extension.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr uint8_t kFlagBeginOfSubframe = 0x80; +constexpr uint8_t kFlagEndOfSubframe = 0x40; +constexpr uint8_t kFlagFirstSubframe = 0x20; +constexpr uint8_t kFlagLastSubframe = 0x10; +constexpr uint8_t kFlagDependencies = 0x08; +constexpr uint8_t kMaskTemporalLayer = 0x07; + +constexpr uint8_t kFlagMoreDependencies = 0x01; +constexpr uint8_t kFlageXtendedOffset = 0x02; + +} // namespace +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |B|E|F|L|D| T | +// +-+-+-+-+-+-+-+-+ +// B: | S | +// +-+-+-+-+-+-+-+-+ +// | | +// B: + FID + +// | | +// +-+-+-+-+-+-+-+-+ +// D: | FDIFF |X|M| +// +---------------+ +// X: | ... | +// +-+-+-+-+-+-+-+-+ +// M: | FDIFF |X|M| +// +---------------+ +// | ... | +// +-+-+-+-+-+-+-+-+ + +bool RtpGenericFrameDescriptorExtension::Parse( + rtc::ArrayView data, + RtpGenericFrameDescriptor* descriptor) { + if (data.empty()) { + return false; + } + + bool begins_subframe = (data[0] & kFlagBeginOfSubframe) != 0; + descriptor->SetFirstPacketInSubFrame(begins_subframe); + descriptor->SetLastPacketInSubFrame((data[0] & kFlagEndOfSubframe) != 0); + descriptor->SetFirstSubFrameInFrame((data[0] & kFlagFirstSubframe) != 0); + descriptor->SetLastSubFrameInFrame((data[0] & kFlagLastSubframe) != 0); + + // Parse Subframe details provided in 1st packet of subframe. + if (!begins_subframe) { + return data.size() == 1; + } + if (data.size() < 4) { + return false; + } + descriptor->SetTemporalLayer(data[0] & kMaskTemporalLayer); + descriptor->SetSpatialLayersBitmask(data[1]); + descriptor->SetFrameId(data[2] | (data[3] << 8)); + + // Parse dependencies. + descriptor->ClearFrameDependencies(); + size_t offset = 4; + bool has_more_dependencies = (data[0] & kFlagDependencies) != 0; + while (has_more_dependencies) { + if (data.size() == offset) + return false; + has_more_dependencies = (data[offset] & kFlagMoreDependencies) != 0; + bool extended = (data[offset] & kFlageXtendedOffset) != 0; + uint16_t fdiff = data[offset] >> 2; + offset++; + if (extended) { + if (data.size() == offset) + return false; + fdiff |= (data[offset] << 6); + offset++; + } + if (!descriptor->AddFrameDependencyDiff(fdiff)) + return false; + } + return data.size() == offset; +} + +size_t RtpGenericFrameDescriptorExtension::ValueSize( + const RtpGenericFrameDescriptor& descriptor) { + if (!descriptor.FirstPacketInSubFrame()) + return 1; + + size_t size = 4; + for (uint16_t fdiff : descriptor.FrameDependenciesDiffs()) { + size += (fdiff >= (1 << 6)) ? 2 : 1; + } + return size; +} + +bool RtpGenericFrameDescriptorExtension::Write( + rtc::ArrayView data, + const RtpGenericFrameDescriptor& descriptor) { + RTC_CHECK_EQ(data.size(), ValueSize(descriptor)); + uint8_t base_header = + (descriptor.FirstPacketInSubFrame() ? kFlagBeginOfSubframe : 0) | + (descriptor.LastPacketInSubFrame() ? kFlagEndOfSubframe : 0) | + (descriptor.FirstSubFrameInFrame() ? kFlagFirstSubframe : 0) | + (descriptor.LastSubFrameInFrame() ? kFlagLastSubframe : 0); + if (!descriptor.FirstPacketInSubFrame()) { + data[0] = base_header; + return true; + } + data[0] = + base_header | + (descriptor.FrameDependenciesDiffs().empty() ? 0 : kFlagDependencies) | + descriptor.TemporalLayer(); + data[1] = descriptor.SpatialLayersBitmask(); + uint16_t frame_id = descriptor.FrameId(); + data[2] = frame_id & 0xff; + data[3] = frame_id >> 8; + rtc::ArrayView fdiffs = descriptor.FrameDependenciesDiffs(); + size_t offset = 4; + for (size_t i = 0; i < fdiffs.size(); i++) { + bool extended = fdiffs[i] >= (1 << 6); + bool more = i < fdiffs.size() - 1; + data[offset++] = ((fdiffs[i] & 0x3f) << 2) | + (extended ? kFlageXtendedOffset : 0) | + (more ? kFlagMoreDependencies : 0); + if (extended) { + data[offset++] = fdiffs[i] >> 6; + } + } + return true; +} + +} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h new file mode 100644 index 0000000000..1387d11fde --- /dev/null +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 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. + */ +#ifndef MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_ +#define MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" + +namespace webrtc { + +class RtpGenericFrameDescriptorExtension { + public: + // TODO(bugs.webrtc.org/9361): Add kId and kUri to make it extension trait. + + static bool Parse(rtc::ArrayView data, + RtpGenericFrameDescriptor* descriptor); + static size_t ValueSize(const RtpGenericFrameDescriptor&); + static bool Write(rtc::ArrayView data, + const RtpGenericFrameDescriptor& descriptor); +}; + +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_SOURCE_RTP_GENERIC_FRAME_DESCRIPTOR_EXTENSION_H_ diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc new file mode 100644 index 0000000000..bb69e1455d --- /dev/null +++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012 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/rtp_generic_frame_descriptor_extension.h" + +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::IsEmpty; + +// TODO(danilchap): Add fuzzer to test for various invalid inputs. + +TEST(RtpGenericFrameDescriptorExtensionTest, + ParseFirstPacketOfIndependenSubFrame) { + const int kTemporalLayer = 5; + constexpr uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + + EXPECT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_FALSE(descriptor.LastPacketInSubFrame()); + EXPECT_FALSE(descriptor.FirstSubFrameInFrame()); + EXPECT_FALSE(descriptor.LastSubFrameInFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), IsEmpty()); + EXPECT_EQ(descriptor.TemporalLayer(), kTemporalLayer); + EXPECT_EQ(descriptor.SpatialLayersBitmask(), 0x49); + EXPECT_EQ(descriptor.FrameId(), 0x3412); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, + WriteFirstPacketOfIndependenSubFrame) { + const int kTemporalLayer = 5; + constexpr uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34}; + RtpGenericFrameDescriptor descriptor; + + descriptor.SetFirstPacketInSubFrame(true); + descriptor.SetTemporalLayer(kTemporalLayer); + descriptor.SetSpatialLayersBitmask(0x49); + descriptor.SetFrameId(0x3412); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseLastPacketOfSubFrame) { + constexpr uint8_t kRaw[] = {0x40}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + + EXPECT_FALSE(descriptor.FirstPacketInSubFrame()); + EXPECT_TRUE(descriptor.LastPacketInSubFrame()); + EXPECT_FALSE(descriptor.FirstSubFrameInFrame()); + EXPECT_FALSE(descriptor.LastSubFrameInFrame()); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteLastPacketOfSubFrame) { + constexpr uint8_t kRaw[] = {0x40}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetLastPacketInSubFrame(true); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} +TEST(RtpGenericFrameDescriptorExtensionTest, ParseFirstSubFrameInFrame) { + constexpr uint8_t kRaw[] = {0x20}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + + EXPECT_FALSE(descriptor.FirstPacketInSubFrame()); + EXPECT_FALSE(descriptor.LastPacketInSubFrame()); + EXPECT_TRUE(descriptor.FirstSubFrameInFrame()); + EXPECT_FALSE(descriptor.LastSubFrameInFrame()); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteFirstSubFrameInFrame) { + constexpr uint8_t kRaw[] = {0x20}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstSubFrameInFrame(true); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseLastSubFrameInFrame) { + constexpr uint8_t kRaw[] = {0x10}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + + EXPECT_FALSE(descriptor.FirstPacketInSubFrame()); + EXPECT_FALSE(descriptor.LastPacketInSubFrame()); + EXPECT_FALSE(descriptor.FirstSubFrameInFrame()); + EXPECT_TRUE(descriptor.LastSubFrameInFrame()); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteLastSubFrameInFrame) { + constexpr uint8_t kRaw[] = {0x10}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetLastSubFrameInFrame(true); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinShortFrameDependencies) { + constexpr uint16_t kDiff = 1; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x04}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinShortFrameDependencies) { + constexpr uint16_t kDiff = 1; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x04}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxShortFrameDependencies) { + constexpr uint16_t kDiff = 0x3f; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfc}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxShortFrameDependencies) { + constexpr uint16_t kDiff = 0x3f; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfc}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinLongFrameDependencies) { + constexpr uint16_t kDiff = 0x40; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x02, 0x01}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinLongFrameDependencies) { + constexpr uint16_t kDiff = 0x40; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x02, 0x01}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, + ParseLongFrameDependenciesAsBigEndian) { + constexpr uint16_t kDiff = 0x7654 >> 2; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, + WriteLongFrameDependenciesAsBigEndian) { + constexpr uint16_t kDiff = 0x7654 >> 2; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxLongFrameDependencies) { + constexpr uint16_t kDiff = 0x3fff; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfe, 0xff}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxLongFrameDependencies) { + constexpr uint16_t kDiff = 0x3fff; + constexpr uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfe, 0xff}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, ParseTwoFrameDependencies) { + constexpr uint16_t kDiff1 = 9; + constexpr uint16_t kDiff2 = 15; + constexpr uint8_t kRaw[] = { + 0x88, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2}; + RtpGenericFrameDescriptor descriptor; + + ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor)); + ASSERT_TRUE(descriptor.FirstPacketInSubFrame()); + EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff1, kDiff2)); +} + +TEST(RtpGenericFrameDescriptorExtensionTest, WriteTwoFrameDependencies) { + constexpr uint16_t kDiff1 = 9; + constexpr uint16_t kDiff2 = 15; + constexpr uint8_t kRaw[] = { + 0x88, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2}; + RtpGenericFrameDescriptor descriptor; + descriptor.SetFirstPacketInSubFrame(true); + descriptor.AddFrameDependencyDiff(kDiff1); + descriptor.AddFrameDependencyDiff(kDiff2); + + ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor), + sizeof(kRaw)); + uint8_t buffer[sizeof(kRaw)]; + EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor)); + EXPECT_THAT(buffer, ElementsAreArray(kRaw)); +} + +} // namespace +} // namespace webrtc