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 <philipel@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23662}
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index 286f1e0..6d39e9e 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -41,6 +41,7 @@
     "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 @@
     "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 @@
       "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 caa8162..ab70b0d 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 @@
 }
 
 rtc::ArrayView<const uint16_t>
-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 07536e7..51a9ac0 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 @@
   uint16_t FrameId() const;
   void SetFrameId(uint16_t frame_id);
 
-  rtc::ArrayView<const uint16_t> FrameDepedenciesDiffs() const;
+  rtc::ArrayView<const uint16_t> 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 0000000..daf797a
--- /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<const uint8_t> 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<uint8_t> 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<const uint16_t> 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 0000000..1387d11
--- /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 <stddef.h>
+#include <stdint.h>
+
+#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<const uint8_t> data,
+                    RtpGenericFrameDescriptor* descriptor);
+  static size_t ValueSize(const RtpGenericFrameDescriptor&);
+  static bool Write(rtc::ArrayView<uint8_t> 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 0000000..bb69e14
--- /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