Allow setting different number of temporal layers per simulcast layer.

Setting different number of temporal layers is supported by SimulcastEncodeAdapter and LibvpxVp8Encoder will fallback to SimulcastEncoderAdapter if InitEncode fails.

Bug: none
Change-Id: I8a09ee1e6c70a0006317957c0802d019a0d28ca2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/228642
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34785}
diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h
index 71ae984..2664ae7 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -489,8 +489,6 @@
 
   // Specifies the number of temporal layers for video (if the feature is
   // supported by the codec implementation).
-  // TODO(asapersson): Different number of temporal layers are not supported
-  // per simulcast layer.
   // Screencast support is experimental.
   absl::optional<int> num_temporal_layers;
 
diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc
index 36a9694..21c3787 100644
--- a/media/base/media_engine.cc
+++ b/media/base/media_engine.cc
@@ -106,15 +106,6 @@
                              "num_temporal_layers to an invalid number.");
       }
     }
-    if (i > 0 && (rtp_parameters.encodings[i].num_temporal_layers !=
-                  rtp_parameters.encodings[i - 1].num_temporal_layers)) {
-      LOG_AND_RETURN_ERROR(
-          RTCErrorType::INVALID_MODIFICATION,
-          "Attempted to set RtpParameters num_temporal_layers "
-          "at encoding layer i: " +
-              rtc::ToString(i) +
-              " to a different value than other encoding layers.");
-    }
   }
 
   return webrtc::RTCError::OK();
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 7c1bf6e..025a553 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -7706,28 +7706,6 @@
             channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
 }
 
-TEST_F(WebRtcVideoChannelTest,
-       SetRtpSendParametersNumTemporalLayersFailsForInvalidModification) {
-  const size_t kNumSimulcastStreams = 3;
-  SetUpSimulcast(true, false);
-
-  // Get and set the rtp encoding parameters.
-  webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
-  EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
-
-  // No/all layers should be set.
-  parameters.encodings[0].num_temporal_layers = 1;
-  EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION,
-            channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
-
-  // Different values not supported.
-  parameters.encodings[0].num_temporal_layers = 1;
-  parameters.encodings[1].num_temporal_layers = 2;
-  parameters.encodings[2].num_temporal_layers = 2;
-  EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION,
-            channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
-}
-
 TEST_F(WebRtcVideoChannelTest, GetAndSetRtpSendParametersNumTemporalLayers) {
   const size_t kNumSimulcastStreams = 3;
   SetUpSimulcast(true, false);
@@ -7767,9 +7745,9 @@
   // Change the value and set it on the VideoChannel.
   webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
-  parameters.encodings[0].num_temporal_layers = 2;
+  parameters.encodings[0].num_temporal_layers = 3;
   parameters.encodings[1].num_temporal_layers = 2;
-  parameters.encodings[2].num_temporal_layers = 2;
+  parameters.encodings[2].num_temporal_layers = 1;
   EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
 
   // Verify that the new value is propagated down to the encoder.
@@ -7778,16 +7756,16 @@
   webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
   EXPECT_EQ(kNumSimulcastStreams, encoder_config.number_of_streams);
   EXPECT_EQ(kNumSimulcastStreams, encoder_config.simulcast_layers.size());
-  EXPECT_EQ(2UL, encoder_config.simulcast_layers[0].num_temporal_layers);
+  EXPECT_EQ(3UL, encoder_config.simulcast_layers[0].num_temporal_layers);
   EXPECT_EQ(2UL, encoder_config.simulcast_layers[1].num_temporal_layers);
-  EXPECT_EQ(2UL, encoder_config.simulcast_layers[2].num_temporal_layers);
+  EXPECT_EQ(1UL, encoder_config.simulcast_layers[2].num_temporal_layers);
 
   // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
   // VideoStreams are created appropriately for the simulcast case.
   EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
-  EXPECT_EQ(2UL, stream->GetVideoStreams()[0].num_temporal_layers);
+  EXPECT_EQ(3UL, stream->GetVideoStreams()[0].num_temporal_layers);
   EXPECT_EQ(2UL, stream->GetVideoStreams()[1].num_temporal_layers);
-  EXPECT_EQ(2UL, stream->GetVideoStreams()[2].num_temporal_layers);
+  EXPECT_EQ(1UL, stream->GetVideoStreams()[2].num_temporal_layers);
 
   // No parameter changed, encoder should not be reconfigured.
   EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
@@ -7809,29 +7787,28 @@
   channel_->SetSend(true);
   frame_forwarder.IncomingCapturedFrame(frame_source_.GetFrame());
 
-  // Change rtp encoding parameters, num_temporal_layers not changed.
+  // Change rtp encoding parameters.
   webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
   EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
-  parameters.encodings[0].min_bitrate_bps = 33000;
+  parameters.encodings[0].num_temporal_layers = 2;
+  parameters.encodings[2].num_temporal_layers = 1;
   EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
 
   // Verify that no value is propagated down to the encoder.
   webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
   EXPECT_EQ(kNumSimulcastStreams, encoder_config.number_of_streams);
   EXPECT_EQ(kNumSimulcastStreams, encoder_config.simulcast_layers.size());
-  EXPECT_FALSE(encoder_config.simulcast_layers[0].num_temporal_layers);
+  EXPECT_EQ(2UL, encoder_config.simulcast_layers[0].num_temporal_layers);
   EXPECT_FALSE(encoder_config.simulcast_layers[1].num_temporal_layers);
-  EXPECT_FALSE(encoder_config.simulcast_layers[2].num_temporal_layers);
+  EXPECT_EQ(1UL, encoder_config.simulcast_layers[2].num_temporal_layers);
 
   // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
   // VideoStreams are created appropriately for the simulcast case.
   EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
-  EXPECT_EQ(kDefaultNumTemporalLayers,
-            stream->GetVideoStreams()[0].num_temporal_layers);
+  EXPECT_EQ(2UL, stream->GetVideoStreams()[0].num_temporal_layers);
   EXPECT_EQ(kDefaultNumTemporalLayers,
             stream->GetVideoStreams()[1].num_temporal_layers);
-  EXPECT_EQ(kDefaultNumTemporalLayers,
-            stream->GetVideoStreams()[2].num_temporal_layers);
+  EXPECT_EQ(1UL, stream->GetVideoStreams()[2].num_temporal_layers);
 
   EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
 }
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index a8140e8..f361833 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -1260,6 +1260,30 @@
   DestroyVideoRtpSender();
 }
 
+TEST_F(RtpSenderReceiverTest, VideoSenderCanSetNumTemporalLayers) {
+  CreateVideoRtpSender();
+
+  RtpParameters params = video_rtp_sender_->GetParameters();
+  params.encodings[0].num_temporal_layers = 2;
+
+  EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
+  params = video_rtp_sender_->GetParameters();
+  EXPECT_EQ(2, params.encodings[0].num_temporal_layers);
+
+  DestroyVideoRtpSender();
+}
+
+TEST_F(RtpSenderReceiverTest, VideoSenderDetectInvalidNumTemporalLayers) {
+  CreateVideoRtpSender();
+
+  RtpParameters params = video_rtp_sender_->GetParameters();
+  params.encodings[0].num_temporal_layers = webrtc::kMaxTemporalStreams + 1;
+  RTCError result = video_rtp_sender_->SetParameters(params);
+  EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.type());
+
+  DestroyVideoRtpSender();
+}
+
 TEST_F(RtpSenderReceiverTest, VideoSenderCanSetMaxFramerate) {
   CreateVideoRtpSender();
 
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 8cb4313..6d6ab05 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -25,13 +25,18 @@
 #include "call/rtp_transport_controller_send.h"
 #include "call/simulated_network.h"
 #include "call/video_send_stream.h"
+#include "media/engine/internal_encoder_factory.h"
+#include "media/engine/simulcast_encoder_adapter.h"
+#include "media/engine/webrtc_video_engine.h"
 #include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
+#include "modules/rtp_rtcp/source/create_video_rtp_depacketizer.h"
 #include "modules/rtp_rtcp/source/rtcp_sender.h"
 #include "modules/rtp_rtcp/source/rtp_header_extensions.h"
 #include "modules/rtp_rtcp/source/rtp_packet.h"
 #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h"
 #include "modules/rtp_rtcp/source/rtp_util.h"
 #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h"
+#include "modules/video_coding/codecs/interface/common_constants.h"
 #include "modules/video_coding/codecs/vp8/include/vp8.h"
 #include "modules/video_coding/codecs/vp9/include/vp9.h"
 #include "rtc_base/checks.h"
@@ -122,6 +127,10 @@
                           uint8_t num_spatial_layers);
 
   void TestRequestSourceRotateVideo(bool support_orientation_ext);
+
+  void TestTemporalLayers(VideoEncoderFactory* encoder_factory,
+                          const std::string& payload_name,
+                          const std::vector<int>& num_temporal_layers);
 };
 
 TEST_F(VideoSendStreamTest, CanStartStartedStream) {
@@ -3971,4 +3980,204 @@
   RunBaseTest(&test);
 }
 
+void VideoSendStreamTest::TestTemporalLayers(
+    VideoEncoderFactory* encoder_factory,
+    const std::string& payload_name,
+    const std::vector<int>& num_temporal_layers) {
+  static constexpr int kMaxBitrateBps = 1000000;
+  static constexpr int kMinFramesToObservePerStream = 8;
+
+  class TemporalLayerObserver
+      : public test::EndToEndTest,
+        public test::FrameGeneratorCapturer::SinkWantsObserver {
+   public:
+    TemporalLayerObserver(VideoEncoderFactory* encoder_factory,
+                          const std::string& payload_name,
+                          const std::vector<int>& num_temporal_layers)
+        : EndToEndTest(kDefaultTimeoutMs),
+          encoder_factory_(encoder_factory),
+          payload_name_(payload_name),
+          num_temporal_layers_(num_temporal_layers),
+          depacketizer_(CreateVideoRtpDepacketizer(
+              PayloadStringToCodecType(payload_name))) {}
+
+   private:
+    void OnFrameGeneratorCapturerCreated(
+        test::FrameGeneratorCapturer* frame_generator_capturer) override {
+      frame_generator_capturer->ChangeResolution(640, 360);
+    }
+
+    void OnSinkWantsChanged(rtc::VideoSinkInterface<VideoFrame>* sink,
+                            const rtc::VideoSinkWants& wants) override {}
+
+    void ModifySenderBitrateConfig(
+        BitrateConstraints* bitrate_config) override {
+      bitrate_config->start_bitrate_bps = kMaxBitrateBps / 2;
+    }
+
+    size_t GetNumVideoStreams() const override {
+      return num_temporal_layers_.size();
+    }
+
+    void ModifyVideoConfigs(
+        VideoSendStream::Config* send_config,
+        std::vector<VideoReceiveStream::Config>* receive_configs,
+        VideoEncoderConfig* encoder_config) override {
+      send_config->encoder_settings.encoder_factory = encoder_factory_;
+      send_config->rtp.payload_name = payload_name_;
+      send_config->rtp.payload_type = test::CallTest::kVideoSendPayloadType;
+      encoder_config->video_format.name = payload_name_;
+      encoder_config->codec_type = PayloadStringToCodecType(payload_name_);
+      encoder_config->video_stream_factory =
+          rtc::make_ref_counted<cricket::EncoderStreamFactory>(
+              payload_name_, /*max_qp=*/56, /*is_screenshare=*/false,
+              /*conference_mode=*/false);
+      encoder_config->max_bitrate_bps = kMaxBitrateBps;
+
+      for (size_t i = 0; i < num_temporal_layers_.size(); ++i) {
+        VideoStream& stream = encoder_config->simulcast_layers[i];
+        stream.num_temporal_layers = num_temporal_layers_[i];
+        configured_num_temporal_layers_[send_config->rtp.ssrcs[i]] =
+            num_temporal_layers_[i];
+      }
+    }
+
+    struct ParsedPacket {
+      uint32_t timestamp;
+      uint32_t ssrc;
+      int temporal_idx;
+    };
+
+    bool ParsePayload(const uint8_t* packet,
+                      size_t length,
+                      ParsedPacket& parsed) const {
+      RtpPacket rtp_packet;
+      EXPECT_TRUE(rtp_packet.Parse(packet, length));
+
+      if (rtp_packet.payload_size() == 0) {
+        return false;  // Padding packet.
+      }
+      parsed.timestamp = rtp_packet.Timestamp();
+      parsed.ssrc = rtp_packet.Ssrc();
+
+      absl::optional<VideoRtpDepacketizer::ParsedRtpPayload> parsed_payload =
+          depacketizer_->Parse(rtp_packet.PayloadBuffer());
+      EXPECT_TRUE(parsed_payload);
+
+      if (const auto* vp8_header = absl::get_if<RTPVideoHeaderVP8>(
+              &parsed_payload->video_header.video_type_header)) {
+        parsed.temporal_idx = vp8_header->temporalIdx;
+      } else {
+        RTC_NOTREACHED();
+      }
+      return true;
+    }
+
+    Action OnSendRtp(const uint8_t* packet, size_t length) override {
+      ParsedPacket parsed;
+      if (!ParsePayload(packet, length, parsed))
+        return SEND_PACKET;
+
+      uint32_t ssrc = parsed.ssrc;
+      int temporal_idx =
+          parsed.temporal_idx == kNoTemporalIdx ? 0 : parsed.temporal_idx;
+      max_observed_tl_idxs_[ssrc] =
+          std::max(temporal_idx, max_observed_tl_idxs_[ssrc]);
+
+      if (last_observed_packet_.count(ssrc) == 0 ||
+          parsed.timestamp != last_observed_packet_[ssrc].timestamp) {
+        num_observed_frames_[ssrc]++;
+      }
+      last_observed_packet_[ssrc] = parsed;
+
+      if (HighestTemporalLayerSentPerStream())
+        observation_complete_.Set();
+
+      return SEND_PACKET;
+    }
+
+    bool HighestTemporalLayerSentPerStream() const {
+      if (num_observed_frames_.size() !=
+          configured_num_temporal_layers_.size()) {
+        return false;
+      }
+      for (const auto& num_frames : num_observed_frames_) {
+        if (num_frames.second < kMinFramesToObservePerStream) {
+          return false;
+        }
+      }
+      if (max_observed_tl_idxs_.size() !=
+          configured_num_temporal_layers_.size()) {
+        return false;
+      }
+      for (const auto& max_tl_idx : max_observed_tl_idxs_) {
+        uint32_t ssrc = max_tl_idx.first;
+        int configured_num_tls =
+            configured_num_temporal_layers_.find(ssrc)->second;
+        if (max_tl_idx.second != configured_num_tls - 1)
+          return false;
+      }
+      return true;
+    }
+
+    void PerformTest() override { EXPECT_TRUE(Wait()); }
+
+    VideoEncoderFactory* const encoder_factory_;
+    const std::string payload_name_;
+    const std::vector<int> num_temporal_layers_;
+    const std::unique_ptr<VideoRtpDepacketizer> depacketizer_;
+    // Mapped by SSRC.
+    std::map<uint32_t, int> configured_num_temporal_layers_;
+    std::map<uint32_t, int> max_observed_tl_idxs_;
+    std::map<uint32_t, int> num_observed_frames_;
+    std::map<uint32_t, ParsedPacket> last_observed_packet_;
+  } test(encoder_factory, payload_name, num_temporal_layers);
+
+  RunBaseTest(&test);
+}
+
+TEST_F(VideoSendStreamTest, TestTemporalLayersVp8) {
+  InternalEncoderFactory internal_encoder_factory;
+  test::FunctionVideoEncoderFactory encoder_factory(
+      [&internal_encoder_factory]() {
+        return std::make_unique<SimulcastEncoderAdapter>(
+            &internal_encoder_factory, SdpVideoFormat("VP8"));
+      });
+
+  TestTemporalLayers(&encoder_factory, "VP8",
+                     /*num_temporal_layers=*/{2});
+}
+
+TEST_F(VideoSendStreamTest, TestTemporalLayersVp8Simulcast) {
+  InternalEncoderFactory internal_encoder_factory;
+  test::FunctionVideoEncoderFactory encoder_factory(
+      [&internal_encoder_factory]() {
+        return std::make_unique<SimulcastEncoderAdapter>(
+            &internal_encoder_factory, SdpVideoFormat("VP8"));
+      });
+
+  TestTemporalLayers(&encoder_factory, "VP8",
+                     /*num_temporal_layers=*/{2, 2});
+}
+
+TEST_F(VideoSendStreamTest, TestTemporalLayersVp8SimulcastWithDifferentNumTls) {
+  InternalEncoderFactory internal_encoder_factory;
+  test::FunctionVideoEncoderFactory encoder_factory(
+      [&internal_encoder_factory]() {
+        return std::make_unique<SimulcastEncoderAdapter>(
+            &internal_encoder_factory, SdpVideoFormat("VP8"));
+      });
+
+  TestTemporalLayers(&encoder_factory, "VP8",
+                     /*num_temporal_layers=*/{3, 1});
+}
+
+TEST_F(VideoSendStreamTest, TestTemporalLayersVp8SimulcastWithoutSimAdapter) {
+  test::FunctionVideoEncoderFactory encoder_factory(
+      []() { return VP8Encoder::Create(); });
+
+  TestTemporalLayers(&encoder_factory, "VP8",
+                     /*num_temporal_layers=*/{2, 2});
+}
+
 }  // namespace webrtc