diff --git a/docs/native-code/rtp-hdrext/video-layers-allocation00/README.md b/docs/native-code/rtp-hdrext/video-layers-allocation00/README.md index 5c98610fcf..f367adab4c 100644 --- a/docs/native-code/rtp-hdrext/video-layers-allocation00/README.md +++ b/docs/native-code/rtp-hdrext/video-layers-allocation00/README.md @@ -80,3 +80,5 @@ extension size. Encoded (width - 1), 16-bit, (height - 1), 16-bit, max frame rate 8-bit per spatial layer per RTP stream. Values are stored in (RTP stream id, spatial id) ascending order. +An empty layer allocation (i.e nothing sent on ssrc) is encoded as +special case with a single 0 byte. diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc index 1587bc34cf..23b2f3b8a1 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension.cc @@ -201,17 +201,21 @@ SpatialLayersBitmasks SpatialLayersBitmasksPerRtpStream( // Encoded (width - 1), 16-bit, (height - 1), 16-bit, max frame rate 8-bit // per spatial layer per RTP stream. // Values are stored in (RTP stream id, spatial id) ascending order. +// +// An empty layer allocation (i.e nothing sent on ssrc) is encoded as +// special case with a single 0 byte. bool RtpVideoLayersAllocationExtension::Write( rtc::ArrayView data, const VideoLayersAllocation& allocation) { - if (allocation.active_spatial_layers.empty()) { - return false; - } - RTC_DCHECK(AllocationIsValid(allocation)); RTC_DCHECK_GE(data.size(), ValueSize(allocation)); + if (allocation.active_spatial_layers.empty()) { + data[0] = 0; + return true; + } + SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation); uint8_t* write_at = data.data(); // First half of the header byte. @@ -276,10 +280,18 @@ bool RtpVideoLayersAllocationExtension::Parse( if (data.empty() || allocation == nullptr) { return false; } + + allocation->active_spatial_layers.clear(); + const uint8_t* read_at = data.data(); const uint8_t* const end = data.data() + data.size(); - allocation->active_spatial_layers.clear(); + if (data.size() == 1 && *read_at == 0) { + allocation->rtp_stream_index = 0; + allocation->resolution_and_frame_rate_is_valid = true; + return true; + } + // Header byte. allocation->rtp_stream_index = *read_at >> 6; int num_rtp_streams = 1 + ((*read_at >> 4) & 0b11); @@ -374,7 +386,7 @@ bool RtpVideoLayersAllocationExtension::Parse( size_t RtpVideoLayersAllocationExtension::ValueSize( const VideoLayersAllocation& allocation) { if (allocation.active_spatial_layers.empty()) { - return 0; + return 1; } size_t result = 1; // header SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation); diff --git a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc index c8363ae257..23a7961ce8 100644 --- a/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_video_layers_allocation_extension_unittest.cc @@ -19,15 +19,33 @@ namespace webrtc { namespace { -TEST(RtpVideoLayersAllocationExtension, - WriteEmptyLayersAllocationReturnsFalse) { +TEST(RtpVideoLayersAllocationExtension, WriteEmptyLayersAllocationReturnsTrue) { VideoLayersAllocation written_allocation; rtc::Buffer buffer( RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); - EXPECT_FALSE( + EXPECT_TRUE( RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); } +TEST(RtpVideoLayersAllocationExtension, + CanWriteAndParseLayersAllocationWithZeroSpatialLayers) { + // We require the resolution_and_frame_rate_is_valid to be set to true in + // order to send an "empty" allocation. + VideoLayersAllocation written_allocation; + written_allocation.resolution_and_frame_rate_is_valid = true; + written_allocation.rtp_stream_index = 0; + + rtc::Buffer buffer( + RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); + EXPECT_TRUE( + RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); + + VideoLayersAllocation parsed_allocation; + EXPECT_TRUE( + RtpVideoLayersAllocationExtension::Parse(buffer, &parsed_allocation)); + EXPECT_EQ(written_allocation, parsed_allocation); +} + TEST(RtpVideoLayersAllocationExtension, CanWriteAndParse2SpatialWith2TemporalLayers) { VideoLayersAllocation written_allocation;