Allow sending DependencyDescriptor rtp header extension in call

Bug: webrtc:10342
Change-Id: I8ccbc7381fc8ac436066f5b817fa32180fc8603e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/168542
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30546}
diff --git a/api/rtp_parameters.cc b/api/rtp_parameters.cc
index c3f14d8..2b580b1 100644
--- a/api/rtp_parameters.cc
+++ b/api/rtp_parameters.cc
@@ -132,6 +132,9 @@
     "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::kDependencyDescriptorUri[] =
+    "https://aomediacodec.github.io/av1-rtp-spec/"
+    "#dependency-descriptor-rtp-header-extension";
 const char RtpExtension::kGenericFrameDescriptorUri[] =
     "http://www.webrtc.org/experiments/rtp-hdrext/generic-frame-descriptor-00";
 
@@ -180,6 +183,7 @@
          uri == webrtc::RtpExtension::kFrameMarkingUri ||
          uri == webrtc::RtpExtension::kGenericFrameDescriptorUri00 ||
          uri == webrtc::RtpExtension::kGenericFrameDescriptorUri01 ||
+         uri == webrtc::RtpExtension::kDependencyDescriptorUri ||
          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 01c6ed4..4f45d65 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -289,6 +289,7 @@
   // Experimental codec agnostic frame descriptor.
   static const char kGenericFrameDescriptorUri00[];
   static const char kGenericFrameDescriptorUri01[];
+  static const char kDependencyDescriptorUri[];
   // TODO(bugs.webrtc.org/10243): Remove once dependencies have been updated.
   static const char kGenericFrameDescriptorUri[];
 
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index 42a03c0..8ec534e 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -493,6 +493,16 @@
         rtp_streams_[stream_index].rtp_rtcp->ExpectedRetransmissionTimeMs();
   }
 
+  if (encoded_image._frameType == VideoFrameType::kVideoFrameKey) {
+    // If encoder adapter produce FrameDependencyStructure, pass it so that
+    // dependency descriptor rtp header extension can be used.
+    // If not supported, disable using dependency descriptor by passing nullptr.
+    rtp_streams_[stream_index].sender_video->SetVideoStructure(
+        (codec_specific_info && codec_specific_info->template_structure)
+            ? &*codec_specific_info->template_structure
+            : nullptr);
+  }
+
   bool send_result = rtp_streams_[stream_index].sender_video->SendVideo(
       rtp_config_.payload_type, codec_type_, rtp_timestamp,
       encoded_image.capture_time_ms_, encoded_image, fragmentation,
diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc
index 9f4aef4..a7336da 100644
--- a/call/rtp_video_sender_unittest.cc
+++ b/call/rtp_video_sender_unittest.cc
@@ -18,6 +18,7 @@
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "modules/rtp_rtcp/source/byte_io.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/nack.h"
+#include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h"
 #include "modules/rtp_rtcp/source/rtp_packet.h"
 #include "modules/video_coding/fec_controller_default.h"
 #include "modules/video_coding/include/video_codec_interface.h"
@@ -33,10 +34,9 @@
 #include "video/send_statistics_proxy.h"
 
 using ::testing::_;
-using ::testing::Invoke;
 using ::testing::NiceMock;
 using ::testing::SaveArg;
-using ::testing::Unused;
+using ::testing::SizeIs;
 
 namespace webrtc {
 namespace {
@@ -51,6 +51,7 @@
 const int16_t kInitialTl0PicIdx2 = 199;
 const int64_t kRetransmitWindowSizeMs = 500;
 const int kTransportsSequenceExtensionId = 7;
+const int kDependencyDescriptorExtensionId = 8;
 
 class MockRtcpIntraFrameObserver : public RtcpIntraFrameObserver {
  public:
@@ -104,6 +105,8 @@
   config.rtp.nack.rtp_history_ms = 1000;
   config.rtp.extensions.emplace_back(RtpExtension::kTransportSequenceNumberUri,
                                      kTransportsSequenceExtensionId);
+  config.rtp.extensions.emplace_back(RtpDependencyDescriptorExtension::kUri,
+                                     kDependencyDescriptorExtensionId);
   return config;
 }
 
@@ -648,6 +651,135 @@
   test.AdvanceTime(TimeDelta::Millis(33));
 }
 
+TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) {
+  test::ScopedFieldTrials trials("WebRTC-GenericDescriptor/Enabled/");
+
+  RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {});
+  test.router()->SetActive(true);
+
+  RtpHeaderExtensionMap extensions;
+  extensions.Register<RtpDependencyDescriptorExtension>(
+      kDependencyDescriptorExtensionId);
+  std::vector<RtpPacket> sent_packets;
+  ON_CALL(test.transport(), SendRtp)
+      .WillByDefault([&](const uint8_t* packet, size_t length,
+                         const PacketOptions& options) {
+        sent_packets.emplace_back(&extensions);
+        EXPECT_TRUE(sent_packets.back().Parse(packet, length));
+        return true;
+      });
+
+  const uint8_t kPayload[1] = {'a'};
+  EncodedImage encoded_image;
+  encoded_image.SetTimestamp(1);
+  encoded_image.capture_time_ms_ = 2;
+  encoded_image.SetEncodedData(
+      EncodedImageBuffer::Create(kPayload, sizeof(kPayload)));
+
+  CodecSpecificInfo codec_specific;
+  codec_specific.codecType = VideoCodecType::kVideoCodecGeneric;
+  codec_specific.template_structure.emplace();
+  codec_specific.template_structure->num_decode_targets = 1;
+  codec_specific.template_structure->templates = {
+      GenericFrameInfo::Builder().T(0).Dtis("S").Build(),
+      GenericFrameInfo::Builder().T(0).Dtis("S").Fdiffs({2}).Build(),
+      GenericFrameInfo::Builder().T(1).Dtis("D").Fdiffs({1}).Build(),
+  };
+
+  // Send two tiny images, mapping to single RTP packets.
+  // Send in key frame.
+  encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+  codec_specific.generic_frame_info =
+      GenericFrameInfo::Builder().T(0).Dtis("S").Build();
+  codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}};
+  EXPECT_EQ(test.router()
+                ->OnEncodedImage(encoded_image, &codec_specific, nullptr)
+                .error,
+            EncodedImageCallback::Result::OK);
+  test.AdvanceTime(TimeDelta::Millis(33));
+  ASSERT_THAT(sent_packets, SizeIs(1));
+  EXPECT_TRUE(
+      sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
+
+  // Send in delta frame.
+  encoded_image._frameType = VideoFrameType::kVideoFrameDelta;
+  codec_specific.template_structure = absl::nullopt;
+  codec_specific.generic_frame_info =
+      GenericFrameInfo::Builder().T(1).Dtis("D").Build();
+  codec_specific.generic_frame_info->encoder_buffers = {{0, true, false}};
+  EXPECT_EQ(test.router()
+                ->OnEncodedImage(encoded_image, &codec_specific, nullptr)
+                .error,
+            EncodedImageCallback::Result::OK);
+  test.AdvanceTime(TimeDelta::Millis(33));
+  ASSERT_THAT(sent_packets, SizeIs(2));
+  EXPECT_TRUE(
+      sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
+}
+
+TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) {
+  test::ScopedFieldTrials trials("WebRTC-GenericDescriptor/Enabled/");
+
+  RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {});
+  test.router()->SetActive(true);
+
+  RtpHeaderExtensionMap extensions;
+  extensions.Register<RtpDependencyDescriptorExtension>(
+      kDependencyDescriptorExtensionId);
+  std::vector<RtpPacket> sent_packets;
+  ON_CALL(test.transport(), SendRtp)
+      .WillByDefault([&](const uint8_t* packet, size_t length,
+                         const PacketOptions& options) {
+        sent_packets.emplace_back(&extensions);
+        EXPECT_TRUE(sent_packets.back().Parse(packet, length));
+        return true;
+      });
+
+  const uint8_t kPayload[1] = {'a'};
+  EncodedImage encoded_image;
+  encoded_image.SetTimestamp(1);
+  encoded_image.capture_time_ms_ = 2;
+  encoded_image.SetEncodedData(
+      EncodedImageBuffer::Create(kPayload, sizeof(kPayload)));
+
+  CodecSpecificInfo codec_specific;
+  codec_specific.codecType = VideoCodecType::kVideoCodecGeneric;
+  codec_specific.template_structure.emplace();
+  codec_specific.template_structure->num_decode_targets = 1;
+  codec_specific.template_structure->templates = {
+      GenericFrameInfo::Builder().T(0).Dtis("S").Build(),
+      GenericFrameInfo::Builder().T(0).Dtis("S").Fdiffs({2}).Build(),
+      GenericFrameInfo::Builder().T(1).Dtis("D").Fdiffs({1}).Build(),
+  };
+
+  // Send two tiny images, mapping to single RTP packets.
+  // Send in a key frame.
+  encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+  codec_specific.generic_frame_info =
+      GenericFrameInfo::Builder().T(0).Dtis("S").Build();
+  codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}};
+  EXPECT_EQ(test.router()
+                ->OnEncodedImage(encoded_image, &codec_specific, nullptr)
+                .error,
+            EncodedImageCallback::Result::OK);
+  test.AdvanceTime(TimeDelta::Millis(33));
+  ASSERT_THAT(sent_packets, SizeIs(1));
+  EXPECT_TRUE(
+      sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
+
+  // Send in a new key frame without the support for the dependency descriptor.
+  encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+  codec_specific.template_structure = absl::nullopt;
+  EXPECT_EQ(test.router()
+                ->OnEncodedImage(encoded_image, &codec_specific, nullptr)
+                .error,
+            EncodedImageCallback::Result::OK);
+  test.AdvanceTime(TimeDelta::Millis(33));
+  ASSERT_THAT(sent_packets, SizeIs(2));
+  EXPECT_FALSE(
+      sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
+}
+
 TEST(RtpVideoSenderTest, CanSetZeroBitrateWithOverhead) {
   test::ScopedFieldTrials trials("WebRTC-SendSideBwe-WithOverhead/Enabled/");
   RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {});