Create version 01 of Generic Frame Descriptor - with discardability flag

The discardability flag denotes whether the frame may be dropped by
the decoder with no effect on the decodability of subsequent frames.

Bug: webrtc:10214
Change-Id: I3654951d8863b50effe9670b8d1d7eb051240039
Reviewed-on: https://webrtc-review.googlesource.com/c/122241
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Elad Alon <eladalon@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26763}
diff --git a/api/rtp_parameters.cc b/api/rtp_parameters.cc
index 6de14da..b51ea7d 100644
--- a/api/rtp_parameters.cc
+++ b/api/rtp_parameters.cc
@@ -135,6 +135,10 @@
     "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07";
 const int RtpExtension::kFrameMarkingDefaultId = 10;
 
+const char RtpExtension::kGenericFrameDescriptorUri00[] =
+    "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00";
+const char RtpExtension::kGenericFrameDescriptorUri01[] =
+    "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-01";
 const char RtpExtension::kGenericFrameDescriptorUri[] =
     "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00";
 const int RtpExtension::kGenericFrameDescriptorDefaultId = 11;
@@ -178,7 +182,8 @@
          uri == webrtc::RtpExtension::kVideoTimingUri ||
          uri == webrtc::RtpExtension::kMidUri ||
          uri == webrtc::RtpExtension::kFrameMarkingUri ||
-         uri == webrtc::RtpExtension::kGenericFrameDescriptorUri ||
+         uri == webrtc::RtpExtension::kGenericFrameDescriptorUri00 ||
+         uri == webrtc::RtpExtension::kGenericFrameDescriptorUri01 ||
          uri == webrtc::RtpExtension::kColorSpaceUri ||
          uri == webrtc::RtpExtension::kRidUri ||
          uri == webrtc::RtpExtension::kRepairedRidUri;
diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h
index 6138c37..1cf4f36 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -297,6 +297,9 @@
   RTC_DEPRECATED static const int kFrameMarkingDefaultId;
 
   // Experimental codec agnostic frame descriptor.
+  static const char kGenericFrameDescriptorUri00[];
+  static const char kGenericFrameDescriptorUri01[];
+  // TODO(bugs.webrtc.org/10243): Remove once dependencies have been updated.
   static const char kGenericFrameDescriptorUri[];
   // TODO(bugs.webrtc.org/10288): Remove once dependencies have been updated.
   RTC_DEPRECATED static const int kGenericFrameDescriptorDefaultId;
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 09d37b3..a51c11a 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -494,7 +494,9 @@
       webrtc::RtpExtension(webrtc::RtpExtension::kColorSpaceUri, id++));
   if (webrtc::field_trial::IsEnabled("WebRTC-GenericDescriptorAdvertised")) {
     capabilities.header_extensions.push_back(webrtc::RtpExtension(
-        webrtc::RtpExtension::kGenericFrameDescriptorUri, id++));
+        webrtc::RtpExtension::kGenericFrameDescriptorUri00, id++));
+    capabilities.header_extensions.push_back(webrtc::RtpExtension(
+        webrtc::RtpExtension::kGenericFrameDescriptorUri01, id++));
   }
 
   return capabilities;
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 482da39..f8341c1 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -313,8 +313,12 @@
   ExpectRtpCapabilitySupport(RtpExtension::kColorSpaceUri, true);
 }
 
-TEST_F(WebRtcVideoEngineTest, AdvertiseGenericDescriptor) {
-  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri, false);
+TEST_F(WebRtcVideoEngineTest, AdvertiseGenericDescriptor00) {
+  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri00, false);
+}
+
+TEST_F(WebRtcVideoEngineTest, AdvertiseGenericDescriptor01) {
+  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri01, false);
 }
 
 class WebRtcVideoEngineTestWithGenericDescriptor
@@ -324,8 +328,14 @@
       : WebRtcVideoEngineTest("WebRTC-GenericDescriptorAdvertised/Enabled/") {}
 };
 
-TEST_F(WebRtcVideoEngineTestWithGenericDescriptor, AdvertiseGenericDescriptor) {
-  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri, true);
+TEST_F(WebRtcVideoEngineTestWithGenericDescriptor,
+       AdvertiseGenericDescriptor00) {
+  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri00, true);
+}
+
+TEST_F(WebRtcVideoEngineTestWithGenericDescriptor,
+       AdvertiseGenericDescriptor01) {
+  ExpectRtpCapabilitySupport(RtpExtension::kGenericFrameDescriptorUri01, true);
 }
 
 TEST_F(WebRtcVideoEngineTest, CVOSetHeaderExtensionBeforeCapturer) {
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index 1e80bb2..8232898 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -62,7 +62,9 @@
   kRtpExtensionRtpStreamId,
   kRtpExtensionRepairedRtpStreamId,
   kRtpExtensionMid,
-  kRtpExtensionGenericFrameDescriptor,
+  kRtpExtensionGenericFrameDescriptor00,
+  kRtpExtensionGenericFrameDescriptor = kRtpExtensionGenericFrameDescriptor00,
+  kRtpExtensionGenericFrameDescriptor01,
   kRtpExtensionColorSpace,
   kRtpExtensionNumberOfExtensions  // Must be the last entity in the enum.
 };
diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h
index 52d401f..47a2a74 100644
--- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h
+++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h
@@ -14,6 +14,7 @@
 #include <stdint.h>
 #include <vector>
 
+#include "absl/types/optional.h"
 #include "api/array_view.h"
 
 namespace webrtc {
@@ -36,8 +37,14 @@
   bool LastPacketInSubFrame() const { return end_of_subframe_; }
   void SetLastPacketInSubFrame(bool last) { end_of_subframe_ = last; }
 
-  bool FirstSubFrameInFrame() const { return beginning_of_frame_; }
-  bool LastSubFrameInFrame() const { return end_of_frame_; }
+  // Denotes whether the frame is discardable. That is, whether skipping it
+  // would have no effect on the decodability of subsequent frames.
+  // An absl::optional is used because version 0 of the extension did not
+  // support this flag. (The optional aspect is relevant only when parsing.)
+  // TODO(bugs.webrtc.org/10243): Make this into a plain bool when v00 of
+  // the extension is deprecated.
+  absl::optional<bool> Discardable() const { return discardable_; }
+  void SetDiscardable(bool discardable) { discardable_ = discardable; }
 
   // Properties below undefined if !FirstPacketInSubFrame()
   // Valid range for temporal layer: [0, 7]
@@ -66,14 +73,10 @@
   rtc::ArrayView<const uint8_t> GetByteRepresentation();
 
  private:
-  friend class RtpGenericFrameDescriptorExtension;
-  void SetFirstSubFrameInFrame(bool first) { beginning_of_frame_ = first; }
-  void SetLastSubFrameInFrame(bool last) { end_of_frame_ = last; }
-
   bool beginning_of_subframe_ = false;
   bool end_of_subframe_ = false;
-  bool beginning_of_frame_ = true;
-  bool end_of_frame_ = true;
+
+  absl::optional<bool> discardable_;
 
   uint16_t frame_id_ = 0;
   uint8_t spatial_layers_ = 1;
diff --git a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc
index 7cd120d..a705b5a 100644
--- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc
+++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.cc
@@ -17,15 +17,21 @@
 
 constexpr uint8_t kFlagBeginOfSubframe = 0x80;
 constexpr uint8_t kFlagEndOfSubframe = 0x40;
-constexpr uint8_t kFlagFirstSubframe = 0x20;
-constexpr uint8_t kFlagLastSubframe = 0x10;
+
+// In version 00, the flags F and L in the first byte correspond to
+// kFlagFirstSubframeV00 and kFlagLastSubframeV00. In practice, they were
+// always set to |true|. In version 01, these flags are deprecated, and we use
+// one of their bits for the discardability flag.
+constexpr uint8_t kFlagFirstSubframeV00 = 0x20;
+constexpr uint8_t kFlagLastSubframeV00 = 0x10;
+constexpr uint8_t kFlagDiscardableV01 = 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  |
@@ -52,10 +58,9 @@
 //      +---------------+
 //      |      ...      |
 //      +-+-+-+-+-+-+-+-+
-constexpr RTPExtensionType RtpGenericFrameDescriptorExtension::kId;
-constexpr char RtpGenericFrameDescriptorExtension::kUri[];
 
-bool RtpGenericFrameDescriptorExtension::Parse(
+bool RtpGenericFrameDescriptorExtensionParse(
+    size_t version,
     rtc::ArrayView<const uint8_t> data,
     RtpGenericFrameDescriptor* descriptor) {
   if (data.empty()) {
@@ -65,8 +70,10 @@
   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);
+
+  if (version >= 1) {
+    descriptor->SetDiscardable((data[0] & kFlagDiscardableV01) != 0);
+  }
 
   // Parse Subframe details provided in 1st packet of subframe.
   if (!begins_subframe) {
@@ -108,7 +115,7 @@
   return true;
 }
 
-size_t RtpGenericFrameDescriptorExtension::ValueSize(
+size_t RtpGenericFrameDescriptorExtensionValueSize(
     const RtpGenericFrameDescriptor& descriptor) {
   if (!descriptor.FirstPacketInSubFrame())
     return 1;
@@ -125,15 +132,24 @@
   return size;
 }
 
-bool RtpGenericFrameDescriptorExtension::Write(
+bool RtpGenericFrameDescriptorExtensionWrite(
+    size_t version,
     rtc::ArrayView<uint8_t> data,
     const RtpGenericFrameDescriptor& descriptor) {
-  RTC_CHECK_EQ(data.size(), ValueSize(descriptor));
+  RTC_CHECK_EQ(data.size(),
+
+               RtpGenericFrameDescriptorExtensionValueSize(descriptor));
   uint8_t base_header =
       (descriptor.FirstPacketInSubFrame() ? kFlagBeginOfSubframe : 0) |
-      (descriptor.LastPacketInSubFrame() ? kFlagEndOfSubframe : 0) |
-      (descriptor.FirstSubFrameInFrame() ? kFlagFirstSubframe : 0) |
-      (descriptor.LastSubFrameInFrame() ? kFlagLastSubframe : 0);
+      (descriptor.LastPacketInSubFrame() ? kFlagEndOfSubframe : 0);
+  if (version == 0) {
+    base_header |= kFlagFirstSubframeV00;
+    base_header |= kFlagLastSubframeV00;
+  } else if (version >= 1) {
+    const absl::optional<bool> discardable = descriptor.Discardable();
+    base_header |= (discardable.value_or(false) ? kFlagDiscardableV01 : 0);
+  }
+
   if (!descriptor.FirstPacketInSubFrame()) {
     data[0] = base_header;
     return true;
@@ -168,4 +184,48 @@
   return true;
 }
 
+}  // namespace
+
+constexpr RTPExtensionType RtpGenericFrameDescriptorExtension00::kId;
+constexpr char RtpGenericFrameDescriptorExtension00::kUri[];
+
+bool RtpGenericFrameDescriptorExtension00::Parse(
+    rtc::ArrayView<const uint8_t> data,
+    RtpGenericFrameDescriptor* descriptor) {
+  return RtpGenericFrameDescriptorExtensionParse(0, data, descriptor);
+}
+
+size_t RtpGenericFrameDescriptorExtension00::ValueSize(
+    const RtpGenericFrameDescriptor& descriptor) {
+  // No difference between existing versions.
+  return RtpGenericFrameDescriptorExtensionValueSize(descriptor);
+}
+
+bool RtpGenericFrameDescriptorExtension00::Write(
+    rtc::ArrayView<uint8_t> data,
+    const RtpGenericFrameDescriptor& descriptor) {
+  return RtpGenericFrameDescriptorExtensionWrite(0, data, descriptor);
+}
+
+constexpr RTPExtensionType RtpGenericFrameDescriptorExtension01::kId;
+constexpr char RtpGenericFrameDescriptorExtension01::kUri[];
+
+bool RtpGenericFrameDescriptorExtension01::Parse(
+    rtc::ArrayView<const uint8_t> data,
+    RtpGenericFrameDescriptor* descriptor) {
+  return RtpGenericFrameDescriptorExtensionParse(1, data, descriptor);
+}
+
+size_t RtpGenericFrameDescriptorExtension01::ValueSize(
+    const RtpGenericFrameDescriptor& descriptor) {
+  // No difference between existing versions.
+  return RtpGenericFrameDescriptorExtensionValueSize(descriptor);
+}
+
+bool RtpGenericFrameDescriptorExtension01::Write(
+    rtc::ArrayView<uint8_t> data,
+    const RtpGenericFrameDescriptor& descriptor) {
+  return RtpGenericFrameDescriptorExtensionWrite(1, data, descriptor);
+}
+
 }  // 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
index 5c1bc52..a52588e 100644
--- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h
+++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h
@@ -19,14 +19,10 @@
 
 namespace webrtc {
 
-// TODO(bugs.webrtc.org/10243): Remove when downstream projects stop using
-// RtpGenericFrameDescriptorExtension.
-using RtpGenericFrameDescriptorExtension00 = RtpGenericFrameDescriptorExtension;
-
-class RtpGenericFrameDescriptorExtension {
+class RtpGenericFrameDescriptorExtension00 {
  public:
   using value_type = RtpGenericFrameDescriptor;
-  static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor;
+  static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor00;
   static constexpr char kUri[] =
       "http://www.webrtc.org/experiments/rtp-hdrext/"
       "generic-frame-descriptor-00";
@@ -34,7 +30,23 @@
 
   static bool Parse(rtc::ArrayView<const uint8_t> data,
                     RtpGenericFrameDescriptor* descriptor);
-  static size_t ValueSize(const RtpGenericFrameDescriptor&);
+  static size_t ValueSize(const RtpGenericFrameDescriptor& descriptor);
+  static bool Write(rtc::ArrayView<uint8_t> data,
+                    const RtpGenericFrameDescriptor& descriptor);
+};
+
+class RtpGenericFrameDescriptorExtension01 {
+ public:
+  using value_type = RtpGenericFrameDescriptor;
+  static constexpr RTPExtensionType kId = kRtpExtensionGenericFrameDescriptor01;
+  static constexpr char kUri[] =
+      "http://www.webrtc.org/experiments/rtp-hdrext/"
+      "generic-frame-descriptor-01";
+  static constexpr int kMaxSizeBytes = 16;
+
+  static bool Parse(rtc::ArrayView<const uint8_t> data,
+                    RtpGenericFrameDescriptor* descriptor);
+  static size_t ValueSize(const RtpGenericFrameDescriptor& descriptor);
   static bool Write(rtc::ArrayView<uint8_t> data,
                     const RtpGenericFrameDescriptor& descriptor);
 };
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
index 5e6c6cd..13cacb5 100644
--- a/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension_unittest.cc
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *  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
@@ -23,28 +23,87 @@
 
 // TODO(danilchap): Add fuzzer to test for various invalid inputs.
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     ParseFirstPacketOfIndependenSubFrame) {
+class RtpGenericFrameDescriptorExtensionTest
+    : public ::testing::Test,
+      public ::testing::WithParamInterface<int> {
+ public:
+  RtpGenericFrameDescriptorExtensionTest() : version_(GetParam()) {}
+
+  bool Parse(rtc::ArrayView<const uint8_t> data,
+             RtpGenericFrameDescriptor* descriptor) const {
+    switch (version_) {
+      case 0:
+        return RtpGenericFrameDescriptorExtension00::Parse(data, descriptor);
+      case 1:
+        return RtpGenericFrameDescriptorExtension01::Parse(data, descriptor);
+    }
+    RTC_NOTREACHED();
+    return false;
+  }
+
+  size_t ValueSize(const RtpGenericFrameDescriptor& descriptor) const {
+    switch (version_) {
+      case 0:
+        return RtpGenericFrameDescriptorExtension00::ValueSize(descriptor);
+      case 1:
+        return RtpGenericFrameDescriptorExtension01::ValueSize(descriptor);
+    }
+    RTC_NOTREACHED();
+    return 0;
+  }
+
+  bool Write(rtc::ArrayView<uint8_t> data,
+             const RtpGenericFrameDescriptor& descriptor) const {
+    switch (version_) {
+      case 0:
+        return RtpGenericFrameDescriptorExtension00::Write(data, descriptor);
+      case 1:
+        return RtpGenericFrameDescriptorExtension01::Write(data, descriptor);
+    }
+    RTC_NOTREACHED();
+    return false;
+  }
+
+ protected:
+  const int version_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         RtpGenericFrameDescriptorExtensionTest,
+                         ::testing::Values(0, 1));
+
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       ParseFirstPacketOfIndependenSubFrame) {
   const int kTemporalLayer = 5;
   constexpr uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
 
   EXPECT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_FALSE(descriptor.LastPacketInSubFrame());
-  EXPECT_FALSE(descriptor.FirstSubFrameInFrame());
-  EXPECT_FALSE(descriptor.LastSubFrameInFrame());
+
+  const absl::optional<bool> discardable = descriptor.Discardable();
+  if (version_ == 0) {
+    ASSERT_FALSE(discardable.has_value());
+  } else {
+    ASSERT_TRUE(discardable.has_value());
+    EXPECT_FALSE(discardable.value());
+  }
+
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), IsEmpty());
   EXPECT_EQ(descriptor.TemporalLayer(), kTemporalLayer);
   EXPECT_EQ(descriptor.SpatialLayersBitmask(), 0x49);
   EXPECT_EQ(descriptor.FrameId(), 0x3412);
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     WriteFirstPacketOfIndependenSubFrame) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       WriteFirstPacketOfIndependenSubFrame) {
   const int kTemporalLayer = 5;
-  constexpr uint8_t kRaw[] = {0xb0 | kTemporalLayer, 0x49, 0x12, 0x34};
+  uint8_t kRaw[] = {0x80 | kTemporalLayer, 0x49, 0x12, 0x34};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
 
   descriptor.SetFirstPacketInSubFrame(true);
@@ -52,240 +111,263 @@
   descriptor.SetSpatialLayersBitmask(0x49);
   descriptor.SetFrameId(0x3412);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
+
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseLastPacketOfSubFrame) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseLastPacketOfSubFrame) {
   constexpr uint8_t kRaw[] = {0x40};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
 
   EXPECT_FALSE(descriptor.FirstPacketInSubFrame());
-  EXPECT_FALSE(descriptor.FirstSubFrameInFrame());
-  EXPECT_FALSE(descriptor.LastSubFrameInFrame());
+
+  const absl::optional<bool> discardable = descriptor.Discardable();
+  if (version_ == 0) {
+    ASSERT_FALSE(discardable.has_value());
+  } else {
+    ASSERT_TRUE(discardable.has_value());
+    EXPECT_FALSE(discardable.value());
+  }
 
   EXPECT_TRUE(descriptor.LastPacketInSubFrame());
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteLastPacketOfSubFrame) {
-  constexpr uint8_t kRaw[] = {0x40 | kDeprecatedFlags};
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteLastPacketOfSubFrame) {
+  uint8_t kRaw[] = {0x40};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
+
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetLastPacketInSubFrame(true);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseFirstSubFrameInFrame) {
-  constexpr uint8_t kRaw[] = {0x20};
-  RtpGenericFrameDescriptor descriptor;
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseDiscardable) {
+  if (version_ == 0) {
+    return;
+  }
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
-
-  EXPECT_FALSE(descriptor.FirstPacketInSubFrame());
-  EXPECT_FALSE(descriptor.LastPacketInSubFrame());
-  EXPECT_FALSE(descriptor.LastSubFrameInFrame());
-
-  EXPECT_TRUE(descriptor.FirstSubFrameInFrame());
-}
-
-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());
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
+  const absl::optional<bool> discardable = descriptor.Discardable();
+  ASSERT_TRUE(discardable.has_value());
+  EXPECT_TRUE(discardable.value());
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinShortFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteDiscardable) {
+  if (version_ == 0) {
+    return;
+  }
+
+  constexpr uint8_t kRaw[] = {0x10};
+  RtpGenericFrameDescriptor descriptor;
+  descriptor.SetDiscardable(true);
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
+  uint8_t buffer[sizeof(kRaw)];
+  EXPECT_TRUE(Write(buffer, descriptor));
+  EXPECT_THAT(buffer, ElementsAreArray(kRaw));
+}
+
+TEST_P(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(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinShortFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteMinShortFrameDependencies) {
   constexpr uint16_t kDiff = 1;
-  constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0x04};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x04};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxShortFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseMaxShortFrameDependencies) {
   constexpr uint16_t kDiff = 0x3f;
   constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0xfc};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxShortFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteMaxShortFrameDependencies) {
   constexpr uint16_t kDiff = 0x3f;
-  constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0xfc};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfc};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseMinLongFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseMinLongFrameDependencies) {
   constexpr uint16_t kDiff = 0x40;
   constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0x02, 0x01};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteMinLongFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteMinLongFrameDependencies) {
   constexpr uint16_t kDiff = 0x40;
-  constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0x02, 0x01};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x02, 0x01};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     ParseLongFrameDependenciesAsBigEndian) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       ParseLongFrameDependenciesAsBigEndian) {
   constexpr uint16_t kDiff = 0x7654 >> 2;
   constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     WriteLongFrameDependenciesAsBigEndian) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       WriteLongFrameDependenciesAsBigEndian) {
   constexpr uint16_t kDiff = 0x7654 >> 2;
-  constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0x54 | 0x02, 0x76};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseMaxLongFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseMaxLongFrameDependencies) {
   constexpr uint16_t kDiff = 0x3fff;
   constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0xfe, 0xff};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteMaxLongFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteMaxLongFrameDependencies) {
   constexpr uint16_t kDiff = 0x3fff;
-  constexpr uint8_t kRaw[] = {0xb8, 0x01, 0x00, 0x00, 0xfe, 0xff};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, 0xfe, 0xff};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, ParseTwoFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, ParseTwoFrameDependencies) {
   constexpr uint16_t kDiff1 = 9;
   constexpr uint16_t kDiff2 = 15;
   constexpr uint8_t kRaw[] = {
       0xb8, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   ASSERT_TRUE(descriptor.FirstPacketInSubFrame());
   EXPECT_THAT(descriptor.FrameDependenciesDiffs(), ElementsAre(kDiff1, kDiff2));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest, WriteTwoFrameDependencies) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest, WriteTwoFrameDependencies) {
   constexpr uint16_t kDiff1 = 9;
   constexpr uint16_t kDiff2 = 15;
-  constexpr uint8_t kRaw[] = {
-      0xb8, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2};
+  uint8_t kRaw[] = {0x88, 0x01, 0x00, 0x00, (kDiff1 << 2) | 0x01, kDiff2 << 2};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.AddFrameDependencyDiff(kDiff1);
   descriptor.AddFrameDependencyDiff(kDiff2);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     ParseResolutionOnIndependentFrame) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       ParseResolutionOnIndependentFrame) {
   constexpr int kWidth = 0x2468;
   constexpr int kHeight = 0x6543;
   constexpr uint8_t kRaw[] = {0xb0, 0x01, 0x00, 0x00, 0x24, 0x68, 0x65, 0x43};
   RtpGenericFrameDescriptor descriptor;
 
-  ASSERT_TRUE(RtpGenericFrameDescriptorExtension::Parse(kRaw, &descriptor));
+  ASSERT_TRUE(Parse(kRaw, &descriptor));
   EXPECT_EQ(descriptor.Width(), kWidth);
   EXPECT_EQ(descriptor.Height(), kHeight);
 }
 
-TEST(RtpGenericFrameDescriptorExtensionTest,
-     WriteResolutionOnIndependentFrame) {
+TEST_P(RtpGenericFrameDescriptorExtensionTest,
+       WriteResolutionOnIndependentFrame) {
   constexpr int kWidth = 0x2468;
   constexpr int kHeight = 0x6543;
-  constexpr uint8_t kRaw[] = {0xb0, 0x01, 0x00, 0x00, 0x24, 0x68, 0x65, 0x43};
+  uint8_t kRaw[] = {0x80, 0x01, 0x00, 0x00, 0x24, 0x68, 0x65, 0x43};
+  if (version_ == 0) {
+    kRaw[0] |= kDeprecatedFlags;
+  }
   RtpGenericFrameDescriptor descriptor;
   descriptor.SetFirstPacketInSubFrame(true);
   descriptor.SetResolution(kWidth, kHeight);
 
-  ASSERT_EQ(RtpGenericFrameDescriptorExtension::ValueSize(descriptor),
-            sizeof(kRaw));
+  ASSERT_EQ(ValueSize(descriptor), sizeof(kRaw));
   uint8_t buffer[sizeof(kRaw)];
-  EXPECT_TRUE(RtpGenericFrameDescriptorExtension::Write(buffer, descriptor));
+  EXPECT_TRUE(Write(buffer, descriptor));
   EXPECT_THAT(buffer, ElementsAreArray(kRaw));
 }
 }  // namespace
diff --git a/modules/rtp_rtcp/source/rtp_header_extension_map.cc b/modules/rtp_rtcp/source/rtp_header_extension_map.cc
index 8e0a484..4eec8d3 100644
--- a/modules/rtp_rtcp/source/rtp_header_extension_map.cc
+++ b/modules/rtp_rtcp/source/rtp_header_extension_map.cc
@@ -42,7 +42,8 @@
     CreateExtensionInfo<RtpStreamId>(),
     CreateExtensionInfo<RepairedRtpStreamId>(),
     CreateExtensionInfo<RtpMid>(),
-    CreateExtensionInfo<RtpGenericFrameDescriptorExtension>(),
+    CreateExtensionInfo<RtpGenericFrameDescriptorExtension00>(),
+    CreateExtensionInfo<RtpGenericFrameDescriptorExtension01>(),
     CreateExtensionInfo<ColorSpaceExtension>(),
 };
 
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index 73d6413..ce10848 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -79,8 +79,10 @@
     CreateMaxExtensionSize<RtpStreamId>(),
     CreateMaxExtensionSize<RepairedRtpStreamId>(),
     CreateMaxExtensionSize<RtpMid>(),
-    {RtpGenericFrameDescriptorExtension::kId,
-     RtpGenericFrameDescriptorExtension::kMaxSizeBytes},
+    {RtpGenericFrameDescriptorExtension00::kId,
+     RtpGenericFrameDescriptorExtension00::kMaxSizeBytes},
+    {RtpGenericFrameDescriptorExtension01::kId,
+     RtpGenericFrameDescriptorExtension01::kMaxSizeBytes},
 };
 
 }  // namespace
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 0b881f8..85566c4 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -43,7 +43,8 @@
 enum : int {  // The first valid value is 1.
   kAbsoluteSendTimeExtensionId = 1,
   kAudioLevelExtensionId,
-  kGenericDescriptorId,
+  kGenericDescriptorId00,
+  kGenericDescriptorId01,
   kMidExtensionId,
   kRepairedRidExtensionId,
   kRidExtensionId,
@@ -92,8 +93,10 @@
     receivers_extensions_.Register(kRtpExtensionVideoTiming,
                                    kVideoTimingExtensionId);
     receivers_extensions_.Register(kRtpExtensionMid, kMidExtensionId);
-    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor,
-                                   kGenericDescriptorId);
+    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor00,
+                                   kGenericDescriptorId00);
+    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor01,
+                                   kGenericDescriptorId01);
     receivers_extensions_.Register(kRtpExtensionRtpStreamId, kRidExtensionId);
     receivers_extensions_.Register(kRtpExtensionRepairedRtpStreamId,
                                    kRepairedRidExtensionId);
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index 3adc98a..cc1c58b 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -97,6 +97,7 @@
     RtpGenericFrameDescriptor generic_descriptor;
     generic_descriptor.SetFirstPacketInSubFrame(first_packet);
     generic_descriptor.SetLastPacketInSubFrame(last_packet);
+    generic_descriptor.SetDiscardable(video_header.generic->discardable);
 
     if (first_packet) {
       generic_descriptor.SetFrameId(
@@ -121,8 +122,13 @@
                                          video_header.height);
       }
     }
-    packet->SetExtension<RtpGenericFrameDescriptorExtension>(
-        generic_descriptor);
+    if (!packet->SetExtension<RtpGenericFrameDescriptorExtension01>(
+            generic_descriptor) &&
+        !packet->SetExtension<RtpGenericFrameDescriptorExtension00>(
+            generic_descriptor)) {
+      RTC_LOG(LS_ERROR)
+          << "Could not set RTP extension - Generic Frame Descriptor";
+    }
   }
 }
 
@@ -539,8 +545,21 @@
 
   RTPVideoHeader minimized_video_header;
   const RTPVideoHeader* packetize_video_header = video_header;
+
+  rtc::ArrayView<const uint8_t> generic_descriptor_raw_00 =
+      first_packet->GetRawExtension<RtpGenericFrameDescriptorExtension00>();
+  rtc::ArrayView<const uint8_t> generic_descriptor_raw_01 =
+      first_packet->GetRawExtension<RtpGenericFrameDescriptorExtension01>();
+
+  if (!generic_descriptor_raw_00.empty() &&
+      !generic_descriptor_raw_01.empty()) {
+    RTC_LOG(LS_WARNING) << "Two versions of GFD extension used.";
+    return false;
+  }
+
   rtc::ArrayView<const uint8_t> generic_descriptor_raw =
-      first_packet->GetRawExtension<RtpGenericFrameDescriptorExtension>();
+      !generic_descriptor_raw_01.empty() ? generic_descriptor_raw_01
+                                         : generic_descriptor_raw_00;
   if (!generic_descriptor_raw.empty()) {
     if (MinimizeDescriptor(*video_header, &minimized_video_header)) {
       packetize_video_header = &minimized_video_header;
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
index 0cd0572..6dd5353 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
@@ -37,7 +37,8 @@
 enum : int {  // The first valid value is 1.
   kAbsoluteSendTimeExtensionId = 1,
   kFrameMarkingExtensionId,
-  kGenericDescriptorId,
+  kGenericDescriptorId00,
+  kGenericDescriptorId01,
   kTransmissionTimeOffsetExtensionId,
   kTransportSequenceNumberExtensionId,
   kVideoRotationExtensionId,
@@ -65,8 +66,10 @@
                                    kVideoRotationExtensionId);
     receivers_extensions_.Register(kRtpExtensionVideoTiming,
                                    kVideoTimingExtensionId);
-    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor,
-                                   kGenericDescriptorId);
+    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor00,
+                                   kGenericDescriptorId00);
+    receivers_extensions_.Register(kRtpExtensionGenericFrameDescriptor01,
+                                   kGenericDescriptorId01);
     receivers_extensions_.Register(kRtpExtensionFrameMarking,
                                    kFrameMarkingExtensionId);
   }
@@ -139,6 +142,11 @@
     rtp_sender_video_.RegisterPayloadType(kPayload, "generic");
   }
 
+  void PopulateGenericFrameDescriptor(int version);
+
+  void UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(
+      int version);
+
  protected:
   test::ScopedFieldTrials field_trials_;
   SimulatedClock fake_clock_;
@@ -512,11 +520,16 @@
             rtp_sender_video_.GetStorageType(header, kSettings, kRttMs));
 }
 
-TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor) {
+void RtpSenderVideoTest::PopulateGenericFrameDescriptor(int version) {
+  const RTPExtensionType ext_type =
+      (version == 0) ? RTPExtensionType::kRtpExtensionGenericFrameDescriptor00
+                     : RTPExtensionType::kRtpExtensionGenericFrameDescriptor01;
+  const int ext_id =
+      (version == 0) ? kGenericDescriptorId00 : kGenericDescriptorId01;
+
   const int64_t kFrameId = 100000;
   uint8_t kFrame[100];
-  EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(
-                   kRtpExtensionGenericFrameDescriptor, kGenericDescriptorId));
+  EXPECT_EQ(0, rtp_sender_.RegisterRtpHeaderExtension(ext_type, ext_id));
 
   RTPVideoHeader hdr;
   RTPVideoHeader::GenericDescriptorInfo& generic = hdr.generic.emplace();
@@ -532,9 +545,15 @@
 
   RtpGenericFrameDescriptor descriptor_wire;
   EXPECT_EQ(1, transport_.packets_sent());
-  EXPECT_TRUE(
-      transport_.last_sent_packet()
-          .GetExtension<RtpGenericFrameDescriptorExtension>(&descriptor_wire));
+  if (version == 0) {
+    ASSERT_TRUE(transport_.last_sent_packet()
+                    .GetExtension<RtpGenericFrameDescriptorExtension00>(
+                        &descriptor_wire));
+  } else {
+    ASSERT_TRUE(transport_.last_sent_packet()
+                    .GetExtension<RtpGenericFrameDescriptorExtension01>(
+                        &descriptor_wire));
+  }
   EXPECT_EQ(static_cast<uint16_t>(generic.frame_id), descriptor_wire.FrameId());
   EXPECT_EQ(generic.temporal_index, descriptor_wire.TemporalLayer());
   EXPECT_THAT(descriptor_wire.FrameDependenciesDiffs(), ElementsAre(1, 500));
@@ -542,13 +561,28 @@
   EXPECT_EQ(spatial_bitmask, descriptor_wire.SpatialLayersBitmask());
 }
 
-TEST_P(RtpSenderVideoTest,
-       UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed) {
+TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor00) {
+  PopulateGenericFrameDescriptor(0);
+}
+
+TEST_P(RtpSenderVideoTest, PopulateGenericFrameDescriptor01) {
+  PopulateGenericFrameDescriptor(1);
+}
+
+void RtpSenderVideoTest::
+    UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(
+        int version) {
   const int64_t kFrameId = 100000;
   const size_t kFrameSize = 100;
   uint8_t kFrame[kFrameSize];
-  ASSERT_TRUE(rtp_sender_.RegisterRtpHeaderExtension(
-      RtpGenericFrameDescriptorExtension::kUri, kGenericDescriptorId));
+
+  if (version == 0) {
+    ASSERT_TRUE(rtp_sender_.RegisterRtpHeaderExtension(
+        RtpGenericFrameDescriptorExtension00::kUri, kGenericDescriptorId00));
+  } else {
+    ASSERT_TRUE(rtp_sender_.RegisterRtpHeaderExtension(
+        RtpGenericFrameDescriptorExtension01::kUri, kGenericDescriptorId01));
+  }
 
   RTPVideoHeader hdr;
   hdr.codec = kVideoCodecVP8;
@@ -569,6 +603,16 @@
   EXPECT_EQ(transport_.last_sent_packet().payload_size(), 1 + kFrameSize);
 }
 
+TEST_P(RtpSenderVideoTest,
+       UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed00) {
+  UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(0);
+}
+
+TEST_P(RtpSenderVideoTest,
+       UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed01) {
+  UsesMinimalVp8DescriptorWhenGenericFrameDescriptorExtensionIsUsed(1);
+}
+
 INSTANTIATE_TEST_SUITE_P(WithAndWithoutOverhead,
                          RtpSenderVideoTest,
                          ::testing::Bool());
diff --git a/modules/rtp_rtcp/source/rtp_utility.cc b/modules/rtp_rtcp/source/rtp_utility.cc
index 9db5ed9..1203823 100644
--- a/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/modules/rtp_rtcp/source/rtp_utility.cc
@@ -502,7 +502,8 @@
           header->extension.mid.Set(rtc::MakeArrayView(ptr, len + 1));
           break;
         }
-        case kRtpExtensionGenericFrameDescriptor:
+        case kRtpExtensionGenericFrameDescriptor00:
+        case kRtpExtensionGenericFrameDescriptor01:
           RTC_LOG(WARNING)
               << "RtpGenericFrameDescriptor unsupported by rtp header parser.";
           break;
diff --git a/modules/rtp_rtcp/source/rtp_video_header.h b/modules/rtp_rtcp/source/rtp_video_header.h
index 49d8c28..417c38c 100644
--- a/modules/rtp_rtcp/source/rtp_video_header.h
+++ b/modules/rtp_rtcp/source/rtp_video_header.h
@@ -43,6 +43,7 @@
     int temporal_index = 0;
     absl::InlinedVector<int64_t, 5> dependencies;
     absl::InlinedVector<int, 5> higher_spatial_layers;
+    bool discardable = false;
   };
 
   RTPVideoHeader();
diff --git a/test/call_test.cc b/test/call_test.cc
index ca0d7bb..b8334b5 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -262,7 +262,9 @@
                        &video_config->rtp.extensions);
   AddRtpExtensionByUri(RtpExtension::kVideoContentTypeUri,
                        &video_config->rtp.extensions);
-  AddRtpExtensionByUri(RtpExtension::kGenericFrameDescriptorUri,
+  AddRtpExtensionByUri(RtpExtension::kGenericFrameDescriptorUri00,
+                       &video_config->rtp.extensions);
+  AddRtpExtensionByUri(RtpExtension::kGenericFrameDescriptorUri01,
                        &video_config->rtp.extensions);
   if (video_encoder_configs_.empty()) {
     video_encoder_configs_.emplace_back();
diff --git a/test/fuzzers/rtp_packet_fuzzer.cc b/test/fuzzers/rtp_packet_fuzzer.cc
index f774c0c..c658096 100644
--- a/test/fuzzers/rtp_packet_fuzzer.cc
+++ b/test/fuzzers/rtp_packet_fuzzer.cc
@@ -116,9 +116,14 @@
         packet.GetExtension<RtpMid>(&mid);
         break;
       }
-      case kRtpExtensionGenericFrameDescriptor: {
+      case kRtpExtensionGenericFrameDescriptor00: {
         RtpGenericFrameDescriptor descriptor;
-        packet.GetExtension<RtpGenericFrameDescriptorExtension>(&descriptor);
+        packet.GetExtension<RtpGenericFrameDescriptorExtension00>(&descriptor);
+        break;
+      }
+      case kRtpExtensionGenericFrameDescriptor01: {
+        RtpGenericFrameDescriptor descriptor;
+        packet.GetExtension<RtpGenericFrameDescriptorExtension01>(&descriptor);
         break;
       }
       case kRtpExtensionColorSpace: {
diff --git a/video/rtp_video_stream_receiver.cc b/video/rtp_video_stream_receiver.cc
index 656dd7a..aec6ecd 100644
--- a/video/rtp_video_stream_receiver.cc
+++ b/video/rtp_video_stream_receiver.cc
@@ -531,17 +531,31 @@
 
   absl::optional<RtpGenericFrameDescriptor> generic_descriptor_wire;
   generic_descriptor_wire.emplace();
-  if (packet.GetExtension<RtpGenericFrameDescriptorExtension>(
-          &generic_descriptor_wire.value())) {
-    generic_descriptor_wire->SetByteRepresentation(
-        packet.GetRawExtension<RtpGenericFrameDescriptorExtension>());
+  const bool generic_descriptor_v00 =
+      packet.GetExtension<RtpGenericFrameDescriptorExtension00>(
+          &generic_descriptor_wire.value());
+  const bool generic_descriptor_v01 =
+      packet.GetExtension<RtpGenericFrameDescriptorExtension01>(
+          &generic_descriptor_wire.value());
+  if (generic_descriptor_v00 && generic_descriptor_v01) {
+    RTC_LOG(LS_WARNING) << "RTP packet had two different GFD versions.";
+    return;
+  }
+
+  if (generic_descriptor_v00 || generic_descriptor_v01) {
+    if (generic_descriptor_v00) {
+      generic_descriptor_wire->SetByteRepresentation(
+          packet.GetRawExtension<RtpGenericFrameDescriptorExtension00>());
+    } else {
+      generic_descriptor_wire->SetByteRepresentation(
+          packet.GetRawExtension<RtpGenericFrameDescriptorExtension01>());
+    }
+
     webrtc_rtp_header.video_header().is_first_packet_in_frame =
-        generic_descriptor_wire->FirstSubFrameInFrame() &&
         generic_descriptor_wire->FirstPacketInSubFrame();
     webrtc_rtp_header.video_header().is_last_packet_in_frame =
         webrtc_rtp_header.header.markerBit ||
-        (generic_descriptor_wire->LastSubFrameInFrame() &&
-         generic_descriptor_wire->LastPacketInSubFrame());
+        generic_descriptor_wire->LastPacketInSubFrame();
 
     if (generic_descriptor_wire->FirstPacketInSubFrame()) {
       webrtc_rtp_header.frameType =
diff --git a/video/rtp_video_stream_receiver_unittest.cc b/video/rtp_video_stream_receiver_unittest.cc
index d80e551..c6fb13d 100644
--- a/video/rtp_video_stream_receiver_unittest.cc
+++ b/video/rtp_video_stream_receiver_unittest.cc
@@ -32,6 +32,7 @@
 
 using ::testing::_;
 using ::testing::Invoke;
+using ::testing::Values;
 
 namespace webrtc {
 
@@ -284,10 +285,9 @@
   RtpVideoStreamReceiverTestH264() : RtpVideoStreamReceiverTest(GetParam()) {}
 };
 
-INSTANTIATE_TEST_SUITE_P(
-    SpsPpsIdrIsKeyframe,
-    RtpVideoStreamReceiverTestH264,
-    ::testing::Values("", "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/"));
+INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe,
+                         RtpVideoStreamReceiverTestH264,
+                         Values("", "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/"));
 
 TEST_P(RtpVideoStreamReceiverTestH264, InBandSpsPps) {
   std::vector<uint8_t> sps_data;
@@ -500,7 +500,51 @@
   rtp_video_stream_receiver_->RemoveSecondarySink(&secondary_sink);
 }
 
-TEST_F(RtpVideoStreamReceiverTest, ParseGenericDescriptorOnePacket) {
+class RtpVideoStreamReceiverGenericDescriptorTest
+    : public RtpVideoStreamReceiverTest,
+      public ::testing::WithParamInterface<int> {
+ public:
+  void RegisterRtpGenericFrameDescriptorExtension(
+      RtpHeaderExtensionMap* extension_map,
+      int version) {
+    constexpr int kId00 = 5;
+    constexpr int kId01 = 6;
+    switch (version) {
+      case 0:
+        extension_map->Register<RtpGenericFrameDescriptorExtension00>(kId00);
+        return;
+      case 1:
+        extension_map->Register<RtpGenericFrameDescriptorExtension01>(kId01);
+        return;
+    }
+    RTC_NOTREACHED();
+  }
+
+  bool SetExtensionRtpGenericFrameDescriptorExtension(
+      const RtpGenericFrameDescriptor& generic_descriptor,
+      RtpPacketReceived* rtp_packet,
+      int version) {
+    switch (version) {
+      case 0:
+        return rtp_packet->SetExtension<RtpGenericFrameDescriptorExtension00>(
+            generic_descriptor);
+      case 1:
+        return rtp_packet->SetExtension<RtpGenericFrameDescriptorExtension01>(
+            generic_descriptor);
+    }
+    RTC_NOTREACHED();
+    return false;
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         RtpVideoStreamReceiverGenericDescriptorTest,
+                         Values(0, 1));
+
+TEST_P(RtpVideoStreamReceiverGenericDescriptorTest,
+       ParseGenericDescriptorOnePacket) {
+  const int version = GetParam();
+
   const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
   const int kPayloadType = 123;
   const int kSpatialIndex = 1;
@@ -511,7 +555,7 @@
   rtp_video_stream_receiver_->StartReceive();
 
   RtpHeaderExtensionMap extension_map;
-  extension_map.Register<RtpGenericFrameDescriptorExtension>(5);
+  RegisterRtpGenericFrameDescriptorExtension(&extension_map, version);
   RtpPacketReceived rtp_packet(&extension_map);
 
   RtpGenericFrameDescriptor generic_descriptor;
@@ -521,8 +565,8 @@
   generic_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex);
   generic_descriptor.AddFrameDependencyDiff(90);
   generic_descriptor.AddFrameDependencyDiff(80);
-  EXPECT_TRUE(rtp_packet.SetExtension<RtpGenericFrameDescriptorExtension>(
-      generic_descriptor));
+  ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension(
+      generic_descriptor, &rtp_packet, version));
 
   uint8_t* payload = rtp_packet.SetPayloadSize(data.size());
   memcpy(payload, data.data(), data.size());
@@ -545,7 +589,10 @@
   rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
 }
 
-TEST_F(RtpVideoStreamReceiverTest, ParseGenericDescriptorTwoPackets) {
+TEST_P(RtpVideoStreamReceiverGenericDescriptorTest,
+       ParseGenericDescriptorTwoPackets) {
+  const int version = GetParam();
+
   const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
   const int kPayloadType = 123;
   const int kSpatialIndex = 1;
@@ -556,7 +603,7 @@
   rtp_video_stream_receiver_->StartReceive();
 
   RtpHeaderExtensionMap extension_map;
-  extension_map.Register<RtpGenericFrameDescriptorExtension>(5);
+  RegisterRtpGenericFrameDescriptorExtension(&extension_map, version);
   RtpPacketReceived first_packet(&extension_map);
 
   RtpGenericFrameDescriptor first_packet_descriptor;
@@ -565,8 +612,8 @@
   first_packet_descriptor.SetFrameId(100);
   first_packet_descriptor.SetSpatialLayersBitmask(1 << kSpatialIndex);
   first_packet_descriptor.SetResolution(480, 360);
-  EXPECT_TRUE(first_packet.SetExtension<RtpGenericFrameDescriptorExtension>(
-      first_packet_descriptor));
+  ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension(
+      first_packet_descriptor, &first_packet, version));
 
   uint8_t* first_packet_payload = first_packet.SetPayloadSize(data.size());
   memcpy(first_packet_payload, data.data(), data.size());
@@ -582,8 +629,8 @@
   RtpGenericFrameDescriptor second_packet_descriptor;
   second_packet_descriptor.SetFirstPacketInSubFrame(false);
   second_packet_descriptor.SetLastPacketInSubFrame(true);
-  EXPECT_TRUE(second_packet.SetExtension<RtpGenericFrameDescriptorExtension>(
-      second_packet_descriptor));
+  ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension(
+      second_packet_descriptor, &second_packet, version));
 
   second_packet.SetMarker(true);
   second_packet.SetPayloadType(kPayloadType);
@@ -606,6 +653,45 @@
   rtp_video_stream_receiver_->OnRtpPacket(second_packet);
 }
 
+TEST_F(RtpVideoStreamReceiverGenericDescriptorTest,
+       DropPacketsWithMultipleVersionsOfExtension) {
+  const std::vector<uint8_t> data = {0, 1, 2, 3, 4};
+  const int kPayloadType = 123;
+
+  VideoCodec codec;
+  codec.plType = kPayloadType;
+  rtp_video_stream_receiver_->AddReceiveCodec(codec, {});
+  rtp_video_stream_receiver_->StartReceive();
+
+  RtpHeaderExtensionMap extension_map;
+  RegisterRtpGenericFrameDescriptorExtension(&extension_map, 0);
+  RegisterRtpGenericFrameDescriptorExtension(&extension_map, 1);
+  RtpPacketReceived rtp_packet(&extension_map);
+
+  RtpGenericFrameDescriptor generic_descriptors[2];
+  for (size_t i = 0; i < 2; ++i) {
+    generic_descriptors[i].SetFirstPacketInSubFrame(true);
+    generic_descriptors[i].SetLastPacketInSubFrame(true);
+    generic_descriptors[i].SetFrameId(100);
+    ASSERT_TRUE(SetExtensionRtpGenericFrameDescriptorExtension(
+        generic_descriptors[i], &rtp_packet, i));
+  }
+
+  uint8_t* payload = rtp_packet.SetPayloadSize(data.size());
+  memcpy(payload, data.data(), data.size());
+  // The first byte is the header, so we ignore the first byte of |data|.
+  mock_on_complete_frame_callback_.AppendExpectedBitstream(data.data() + 1,
+                                                           data.size() - 1);
+
+  rtp_packet.SetMarker(true);
+  rtp_packet.SetPayloadType(kPayloadType);
+  rtp_packet.SetSequenceNumber(1);
+
+  EXPECT_CALL(mock_on_complete_frame_callback_, DoOnCompleteFrame).Times(0);
+
+  rtp_video_stream_receiver_->OnRtpPacket(rtp_packet);
+}
+
 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 TEST_F(RtpVideoStreamReceiverTest, RepeatedSecondarySinkDisallowed) {
   MockRtpPacketSink secondary_sink;
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 0ac5cc2..3627918 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -49,7 +49,8 @@
 namespace {
 enum : int {  // The first valid value is 1.
   kAbsSendTimeExtensionId = 1,
-  kGenericFrameDescriptorExtensionId,
+  kGenericFrameDescriptorExtensionId00,
+  kGenericFrameDescriptorExtensionId01,
   kTransportSequenceNumberExtensionId,
   kVideoContentTypeExtensionId,
   kVideoTimingExtensionId,
@@ -732,8 +733,11 @@
       RTC_CHECK(field_trial::IsEnabled("WebRTC-GenericDescriptor"));
 
       video_send_configs_[video_idx].rtp.extensions.emplace_back(
-          RtpExtension::kGenericFrameDescriptorUri,
-          kGenericFrameDescriptorExtensionId);
+          RtpExtension::kGenericFrameDescriptorUri00,
+          kGenericFrameDescriptorExtensionId00);
+      video_send_configs_[video_idx].rtp.extensions.emplace_back(
+          RtpExtension::kGenericFrameDescriptorUri01,
+          kGenericFrameDescriptorExtensionId01);
     }
 
     video_send_configs_[video_idx].rtp.extensions.emplace_back(