Send VideoLayersAllocation with resolution if number of spatial layers
increase.

VP9 and other codecs can in theory add spatial layers without a key
frame.

Bug: webrtc:12000
Change-Id: I27461af2e34c855203a130e400a6aa01144d3cf7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198781
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32883}
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc
index 2499d35..934be82 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -153,7 +153,7 @@
               : (kRetransmitBaseLayer | kConditionallyRetransmitHigherLayers)),
       last_rotation_(kVideoRotation_0),
       transmit_color_space_next_frame_(false),
-      send_allocation_(false),
+      send_allocation_(SendVideoLayersAllocation::kDontSend),
       current_playout_delay_{-1, -1},
       playout_delay_pending_(false),
       forced_playout_delay_(LoadVideoPlayoutDelayOverride(config.field_trials)),
@@ -293,8 +293,13 @@
 void RTPSenderVideo::SetVideoLayersAllocationInternal(
     VideoLayersAllocation allocation) {
   RTC_DCHECK_RUNS_SERIALIZED(&send_checker_);
+  if (!allocation_ || allocation.active_spatial_layers.size() >
+                          allocation_->active_spatial_layers.size()) {
+    send_allocation_ = SendVideoLayersAllocation::kSendWithResolution;
+  } else if (send_allocation_ == SendVideoLayersAllocation::kDontSend) {
+    send_allocation_ = SendVideoLayersAllocation::kSendWithoutResolution;
+  }
   allocation_ = std::move(allocation);
-  send_allocation_ = true;
 }
 
 void RTPSenderVideo::AddRtpHeaderExtensions(
@@ -433,16 +438,14 @@
     }
   }
 
-  if (first_packet && send_allocation_) {
-    if (video_header.frame_type == VideoFrameType::kVideoFrameKey) {
-      packet->SetExtension<RtpVideoLayersAllocationExtension>(
-          allocation_.value());
-    } else if (PacketWillLikelyBeRequestedForRestransmitionIfLost(
-                   video_header)) {
-      VideoLayersAllocation allocation = allocation_.value();
-      allocation.resolution_and_frame_rate_is_valid = false;
-      packet->SetExtension<RtpVideoLayersAllocationExtension>(allocation);
-    }
+  if (first_packet &&
+      send_allocation_ != SendVideoLayersAllocation::kDontSend &&
+      (video_header.frame_type == VideoFrameType::kVideoFrameKey ||
+       PacketWillLikelyBeRequestedForRestransmitionIfLost(video_header))) {
+    VideoLayersAllocation allocation = allocation_.value();
+    allocation.resolution_and_frame_rate_is_valid =
+        send_allocation_ == SendVideoLayersAllocation::kSendWithResolution;
+    packet->SetExtension<RtpVideoLayersAllocationExtension>(allocation);
   }
 }
 
@@ -481,7 +484,7 @@
     }
     if (allocation_) {
       // Send the bitrate allocation on every key frame.
-      send_allocation_ = true;
+      send_allocation_ = SendVideoLayersAllocation::kSendWithResolution;
     }
   }
 
@@ -706,7 +709,7 @@
     // This frame will likely be delivered, no need to populate playout
     // delay extensions until it changes again.
     playout_delay_pending_ = false;
-    send_allocation_ = false;
+    send_allocation_ = SendVideoLayersAllocation::kDontSend;
   }
 
   TRACE_EVENT_ASYNC_END1("webrtc", "Video", capture_time_ms, "timestamp",
diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h
index 3f431df..6e46990 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video.h
+++ b/modules/rtp_rtcp/source/rtp_sender_video.h
@@ -159,6 +159,12 @@
     int64_t last_frame_time_ms;
   };
 
+  enum class SendVideoLayersAllocation {
+    kSendWithResolution,
+    kSendWithoutResolution,
+    kDontSend
+  };
+
   void SetVideoStructureInternal(
       const FrameDependencyStructure* video_structure);
   void SetVideoLayersAllocationInternal(VideoLayersAllocation allocation);
@@ -202,7 +208,7 @@
   absl::optional<VideoLayersAllocation> allocation_
       RTC_GUARDED_BY(send_checker_);
   // Flag indicating if we should send |allocation_|.
-  bool send_allocation_ RTC_GUARDED_BY(send_checker_);
+  SendVideoLayersAllocation send_allocation_ RTC_GUARDED_BY(send_checker_);
 
   // Current target playout delay.
   VideoPlayoutDelay current_playout_delay_ RTC_GUARDED_BY(send_checker_);
diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
index e415bad..55bafdc 100644
--- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc
@@ -862,7 +862,7 @@
 }
 
 TEST_P(RtpSenderVideoTest,
-       VideoLayersAllocationWithoutResolutionSentOnDeltaFrames) {
+       VideoLayersAllocationWithoutResolutionSentOnDeltaWhenUpdated) {
   const size_t kFrameSize = 100;
   uint8_t kFrame[kFrameSize];
   rtp_module_->RegisterRtpHeaderExtension(
@@ -876,14 +876,28 @@
   allocation.resolution_and_frame_rate_is_valid = true;
   layer.target_bitrate_per_temporal_layer.push_back(
       DataRate::KilobitsPerSec(50));
-
   allocation.active_spatial_layers.push_back(layer);
   rtp_sender_video_->SetVideoLayersAllocation(allocation);
 
   RTPVideoHeader hdr;
+  hdr.frame_type = VideoFrameType::kVideoFrameKey;
+  rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
+                               kDefaultExpectedRetransmissionTimeMs);
+  EXPECT_TRUE(transport_.last_sent_packet()
+                  .HasExtension<RtpVideoLayersAllocationExtension>());
+
+  // No allocation sent on delta frame unless it has been updated.
   hdr.frame_type = VideoFrameType::kVideoFrameDelta;
   rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
                                kDefaultExpectedRetransmissionTimeMs);
+  EXPECT_FALSE(transport_.last_sent_packet()
+                   .HasExtension<RtpVideoLayersAllocationExtension>());
+
+  // Update the allocation.
+  rtp_sender_video_->SetVideoLayersAllocation(allocation);
+  rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
+                               kDefaultExpectedRetransmissionTimeMs);
+
   VideoLayersAllocation sent_allocation;
   EXPECT_TRUE(
       transport_.last_sent_packet()
@@ -895,6 +909,52 @@
               SizeIs(1));
 }
 
+TEST_P(RtpSenderVideoTest,
+       VideoLayersAllocationWithResolutionSentOnDeltaWhenSpatialLayerAdded) {
+  const size_t kFrameSize = 100;
+  uint8_t kFrame[kFrameSize];
+  rtp_module_->RegisterRtpHeaderExtension(
+      RtpVideoLayersAllocationExtension::kUri,
+      kVideoLayersAllocationExtensionId);
+
+  VideoLayersAllocation allocation;
+  allocation.resolution_and_frame_rate_is_valid = true;
+  VideoLayersAllocation::SpatialLayer layer;
+  layer.width = 360;
+  layer.height = 180;
+  layer.spatial_id = 0;
+  layer.target_bitrate_per_temporal_layer.push_back(
+      DataRate::KilobitsPerSec(50));
+  allocation.active_spatial_layers.push_back(layer);
+  rtp_sender_video_->SetVideoLayersAllocation(allocation);
+
+  RTPVideoHeader hdr;
+  hdr.frame_type = VideoFrameType::kVideoFrameKey;
+  rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
+                               kDefaultExpectedRetransmissionTimeMs);
+  ASSERT_TRUE(transport_.last_sent_packet()
+                  .HasExtension<RtpVideoLayersAllocationExtension>());
+
+  // Update the allocation.
+  layer.width = 640;
+  layer.height = 320;
+  layer.spatial_id = 1;
+  layer.target_bitrate_per_temporal_layer.push_back(
+      DataRate::KilobitsPerSec(100));
+  allocation.active_spatial_layers.push_back(layer);
+  rtp_sender_video_->SetVideoLayersAllocation(allocation);
+  hdr.frame_type = VideoFrameType::kVideoFrameDelta;
+  rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
+                               kDefaultExpectedRetransmissionTimeMs);
+
+  VideoLayersAllocation sent_allocation;
+  EXPECT_TRUE(
+      transport_.last_sent_packet()
+          .GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
+  EXPECT_THAT(sent_allocation.active_spatial_layers, SizeIs(2));
+  EXPECT_TRUE(sent_allocation.resolution_and_frame_rate_is_valid);
+}
+
 TEST_P(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) {
   const size_t kFrameSize = 100;
   uint8_t kFrame[kFrameSize];
@@ -904,6 +964,8 @@
 
   VideoLayersAllocation allocation;
   VideoLayersAllocation::SpatialLayer layer;
+  layer.width = 360;
+  layer.height = 180;
   layer.target_bitrate_per_temporal_layer.push_back(
       DataRate::KilobitsPerSec(50));
   allocation.active_spatial_layers.push_back(layer);
@@ -943,7 +1005,10 @@
       kVideoLayersAllocationExtensionId);
 
   VideoLayersAllocation allocation;
+  allocation.resolution_and_frame_rate_is_valid = true;
   VideoLayersAllocation::SpatialLayer layer;
+  layer.width = 360;
+  layer.height = 180;
   layer.target_bitrate_per_temporal_layer.push_back(
       DataRate::KilobitsPerSec(50));
   allocation.active_spatial_layers.push_back(layer);
@@ -957,18 +1022,15 @@
 
   rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
                                kDefaultExpectedRetransmissionTimeMs);
-  VideoLayersAllocation sent_allocation;
-  EXPECT_FALSE(
-      transport_.last_sent_packet()
-          .GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
+  EXPECT_FALSE(transport_.last_sent_packet()
+                   .HasExtension<RtpVideoLayersAllocationExtension>());
 
   // Send a delta frame on tl0.
   vp8_header.temporalIdx = 0;
   rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
                                kDefaultExpectedRetransmissionTimeMs);
-  EXPECT_TRUE(
-      transport_.last_sent_packet()
-          .GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
+  EXPECT_TRUE(transport_.last_sent_packet()
+                  .HasExtension<RtpVideoLayersAllocationExtension>());
 }
 
 TEST_P(RtpSenderVideoTest, AbsoluteCaptureTime) {