Allow empty video layer allocation extension

This patch adds support for sending zero video layer allocations
header extensions. This can be used to signal that a stream is
turned off.

Bug: webrtc:12000
Change-Id: Id18fbbff2216ca23179c58ef7bbe2ebea5e242af
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/212743
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33541}
This commit is contained in:
Jonas Oreland 2021-03-23 11:11:56 +01:00 committed by Commit Bot
parent fa4db49532
commit 93ee168671
3 changed files with 41 additions and 9 deletions

View file

@ -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 rate 8-bit per spatial layer per RTP stream. Values are stored in (RTP stream
id, spatial id) ascending order. 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.

View file

@ -201,17 +201,21 @@ SpatialLayersBitmasks SpatialLayersBitmasksPerRtpStream(
// Encoded (width - 1), 16-bit, (height - 1), 16-bit, max frame rate 8-bit // Encoded (width - 1), 16-bit, (height - 1), 16-bit, max frame rate 8-bit
// per spatial layer per RTP stream. // per spatial layer per RTP stream.
// Values are stored in (RTP stream id, spatial id) ascending order. // 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( bool RtpVideoLayersAllocationExtension::Write(
rtc::ArrayView<uint8_t> data, rtc::ArrayView<uint8_t> data,
const VideoLayersAllocation& allocation) { const VideoLayersAllocation& allocation) {
if (allocation.active_spatial_layers.empty()) {
return false;
}
RTC_DCHECK(AllocationIsValid(allocation)); RTC_DCHECK(AllocationIsValid(allocation));
RTC_DCHECK_GE(data.size(), ValueSize(allocation)); RTC_DCHECK_GE(data.size(), ValueSize(allocation));
if (allocation.active_spatial_layers.empty()) {
data[0] = 0;
return true;
}
SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation); SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation);
uint8_t* write_at = data.data(); uint8_t* write_at = data.data();
// First half of the header byte. // First half of the header byte.
@ -276,10 +280,18 @@ bool RtpVideoLayersAllocationExtension::Parse(
if (data.empty() || allocation == nullptr) { if (data.empty() || allocation == nullptr) {
return false; return false;
} }
allocation->active_spatial_layers.clear();
const uint8_t* read_at = data.data(); const uint8_t* read_at = data.data();
const uint8_t* const end = data.data() + data.size(); 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. // Header byte.
allocation->rtp_stream_index = *read_at >> 6; allocation->rtp_stream_index = *read_at >> 6;
int num_rtp_streams = 1 + ((*read_at >> 4) & 0b11); int num_rtp_streams = 1 + ((*read_at >> 4) & 0b11);
@ -374,7 +386,7 @@ bool RtpVideoLayersAllocationExtension::Parse(
size_t RtpVideoLayersAllocationExtension::ValueSize( size_t RtpVideoLayersAllocationExtension::ValueSize(
const VideoLayersAllocation& allocation) { const VideoLayersAllocation& allocation) {
if (allocation.active_spatial_layers.empty()) { if (allocation.active_spatial_layers.empty()) {
return 0; return 1;
} }
size_t result = 1; // header size_t result = 1; // header
SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation); SpatialLayersBitmasks slb = SpatialLayersBitmasksPerRtpStream(allocation);

View file

@ -19,15 +19,33 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
TEST(RtpVideoLayersAllocationExtension, TEST(RtpVideoLayersAllocationExtension, WriteEmptyLayersAllocationReturnsTrue) {
WriteEmptyLayersAllocationReturnsFalse) {
VideoLayersAllocation written_allocation; VideoLayersAllocation written_allocation;
rtc::Buffer buffer( rtc::Buffer buffer(
RtpVideoLayersAllocationExtension::ValueSize(written_allocation)); RtpVideoLayersAllocationExtension::ValueSize(written_allocation));
EXPECT_FALSE( EXPECT_TRUE(
RtpVideoLayersAllocationExtension::Write(buffer, written_allocation)); 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, TEST(RtpVideoLayersAllocationExtension,
CanWriteAndParse2SpatialWith2TemporalLayers) { CanWriteAndParse2SpatialWith2TemporalLayers) {
VideoLayersAllocation written_allocation; VideoLayersAllocation written_allocation;