Add RTCP packet class for signaling encoder target bitrate.

This is a proposal for a new RTCP message. Feel free to comment on the
message structure, selected type ids etc, as well as code for
serialization/deserialization. Once we agree on this, I'll continue
with wiring it up in the actual rtcp sender and receiver.

BUG=webrtc:6301

Review-Url: https://codereview.webrtc.org/2306873003
Cr-Commit-Position: refs/heads/master@{#14867}
This commit is contained in:
sprang
2016-11-01 02:50:12 -07:00
committed by Commit bot
parent 6ded190864
commit b84ad63b0a
9 changed files with 326 additions and 1 deletions

View File

@ -32,6 +32,7 @@ namespace rtcp {
// | SSRC_2 (SSRC of second receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// : ... : 2
bool Dlrr::Parse(const uint8_t* buffer, uint16_t block_length_32bits) {
RTC_DCHECK(buffer[0] == kBlockType);
// kReserved = buffer[1];

View File

@ -55,6 +55,7 @@ bool ExtendedReports::Parse(const CommonHeader& packet) {
rrtr_block_.reset();
dlrr_block_.ClearItems();
voip_metric_block_.reset();
target_bitrate_ = rtc::Optional<TargetBitrate>();
const uint8_t* current_block = packet.payload() + kXrBaseLength;
const uint8_t* const packet_end =
@ -80,6 +81,9 @@ bool ExtendedReports::Parse(const CommonHeader& packet) {
case VoipMetric::kBlockType:
ParseVoipMetricBlock(current_block, block_length);
break;
case TargetBitrate::kBlockType:
ParseTargetBitrateBlock(current_block, block_length);
break;
default:
// Unknown block, ignore.
LOG(LS_WARNING) << "Unknown extended report block type " << block_type;
@ -107,6 +111,13 @@ void ExtendedReports::SetVoipMetric(const VoipMetric& voip_metric) {
voip_metric_block_.emplace(voip_metric);
}
void ExtendedReports::SetTargetBitrate(const TargetBitrate& bitrate) {
if (target_bitrate_)
LOG(LS_WARNING) << "TargetBitrate already set, overwriting.";
target_bitrate_ = rtc::Optional<TargetBitrate>(bitrate);
}
bool ExtendedReports::Create(uint8_t* packet,
size_t* index,
size_t max_length,
@ -132,10 +143,20 @@ bool ExtendedReports::Create(uint8_t* packet,
voip_metric_block_->Create(packet + *index);
*index += VoipMetric::kLength;
}
if (target_bitrate_) {
target_bitrate_->Create(packet + *index);
*index += target_bitrate_->BlockLength();
}
RTC_CHECK_EQ(*index, index_end);
return true;
}
size_t ExtendedReports::TargetBitrateLength() const {
if (target_bitrate_)
return target_bitrate_->BlockLength();
return 0;
}
void ExtendedReports::ParseRrtrBlock(const uint8_t* block,
uint16_t block_length) {
if (block_length != Rrtr::kBlockLength) {
@ -175,5 +196,12 @@ void ExtendedReports::ParseVoipMetricBlock(const uint8_t* block,
voip_metric_block_.emplace();
voip_metric_block_->Parse(block);
}
void ExtendedReports::ParseTargetBitrateBlock(const uint8_t* block,
uint16_t block_length) {
target_bitrate_ = rtc::Optional<TargetBitrate>(TargetBitrate());
if (!target_bitrate_->Parse(block, block_length))
target_bitrate_ = rtc::Optional<TargetBitrate>();
}
} // namespace rtcp
} // namespace webrtc

View File

@ -18,6 +18,7 @@
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h"
namespace webrtc {
@ -40,6 +41,7 @@ class ExtendedReports : public RtcpPacket {
void SetRrtr(const Rrtr& rrtr);
void AddDlrrItem(const ReceiveTimeInfo& time_info);
void SetVoipMetric(const VoipMetric& voip_metric);
void SetTargetBitrate(const TargetBitrate& target_bitrate);
uint32_t sender_ssrc() const { return sender_ssrc_; }
const rtc::Optional<Rrtr>& rrtr() const { return rrtr_block_; }
@ -47,6 +49,9 @@ class ExtendedReports : public RtcpPacket {
const rtc::Optional<VoipMetric>& voip_metric() const {
return voip_metric_block_;
}
const rtc::Optional<TargetBitrate>& target_bitrate() {
return target_bitrate_;
}
protected:
bool Create(uint8_t* packet,
@ -59,7 +64,7 @@ class ExtendedReports : public RtcpPacket {
size_t BlockLength() const override {
return kHeaderLength + kXrBaseLength + RrtrLength() + DlrrLength() +
VoipMetricLength();
VoipMetricLength() + TargetBitrateLength();
}
size_t RrtrLength() const { return rrtr_block_ ? Rrtr::kLength : 0; }
@ -67,15 +72,18 @@ class ExtendedReports : public RtcpPacket {
size_t VoipMetricLength() const {
return voip_metric_block_ ? VoipMetric::kLength : 0;
}
size_t TargetBitrateLength() const;
void ParseRrtrBlock(const uint8_t* block, uint16_t block_length);
void ParseDlrrBlock(const uint8_t* block, uint16_t block_length);
void ParseVoipMetricBlock(const uint8_t* block, uint16_t block_length);
void ParseTargetBitrateBlock(const uint8_t* block, uint16_t block_length);
uint32_t sender_ssrc_;
rtc::Optional<Rrtr> rrtr_block_;
Dlrr dlrr_block_; // Dlrr without items treated same as no dlrr block.
rtc::Optional<VoipMetric> voip_metric_block_;
rtc::Optional<TargetBitrate> target_bitrate_;
RTC_DISALLOW_COPY_AND_ASSIGN(ExtendedReports);
};

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
namespace webrtc {
namespace rtcp {
constexpr size_t kTargetBitrateHeaderSizeBytes = 4;
constexpr uint8_t TargetBitrate::kBlockType;
const size_t TargetBitrate::kBitrateItemSizeBytes = 4;
TargetBitrate::BitrateItem::BitrateItem()
: spatial_layer(0), temporal_layer(0), target_bitrate_kbps(0) {}
TargetBitrate::BitrateItem::BitrateItem(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps)
: spatial_layer(spatial_layer),
temporal_layer(temporal_layer),
target_bitrate_kbps(target_bitrate_kbps) {}
// RFC 4585: Feedback format.
//
// Common packet format:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=42 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// Target bitrate item (repeat as many times as necessary).
//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | S | T | Target Bitrate |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
//
// Spatial Layer (S): 4 bits
// Indicates which temporal layer this bitrate concerns.
//
// Temporal Layer (T): 4 bits
// Indicates which temporal layer this bitrate concerns.
//
// Target Bitrate: 24 bits
// The encoder target bitrate for this layer, in kbps.
//
// As an example of how S and T are intended to be used, VP8 simulcast will
// use a separate TargetBitrate message per stream, since they are transmitted
// on separate SSRCs, with temporal layers grouped by stream.
// If VP9 SVC is used, there will be only one SSRC, so each spatial and
// temporal layer combo used shall be specified in the TargetBitrate packet.
TargetBitrate::TargetBitrate() {}
TargetBitrate::~TargetBitrate() {}
void TargetBitrate::Create(uint8_t* buffer) const {
buffer[0] = kBlockType;
buffer[1] = 0; // Reserved.
const size_t block_length_words = (BlockLength() / 4) - 1;
ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], block_length_words);
size_t index = kTargetBitrateHeaderSizeBytes;
for (const BitrateItem& item : bitrates_) {
buffer[index] = (item.spatial_layer << 4) | item.temporal_layer;
ByteWriter<uint32_t, 3>::WriteBigEndian(&buffer[index + 1],
item.target_bitrate_kbps);
index += kBitrateItemSizeBytes;
}
}
bool TargetBitrate::Parse(const uint8_t* block, uint16_t block_length) {
if (block_length < 1) {
LOG(LS_WARNING)
<< "Cannot parse TargetBitrate RTCP packet: Too little payload data ("
<< kTargetBitrateHeaderSizeBytes << " bytes needed, got "
<< block_length * 4 << ").";
return false;
}
// Validate block header (should already have been parsed and checked).
RTC_DCHECK_EQ(block[0], kBlockType);
RTC_DCHECK_EQ(block_length, ByteReader<uint16_t>::ReadBigEndian(&block[2]));
// Header specifies block length - 1, but since we ignore the header, which
// occupies exactly on block, we can just treat this as payload length.
const size_t payload_bytes = block_length * 4;
const size_t num_items = payload_bytes / kBitrateItemSizeBytes;
size_t index = kTargetBitrateHeaderSizeBytes;
bitrates_.clear();
for (size_t i = 0; i < num_items; ++i) {
uint8_t layers = block[index];
uint32_t bitrate_kbps =
ByteReader<uint32_t, 3>::ReadBigEndian(&block[index + 1]);
index += kBitrateItemSizeBytes;
AddTargetBitrate((layers >> 4) & 0x0F, layers & 0x0F, bitrate_kbps);
}
return true;
}
void TargetBitrate::AddTargetBitrate(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps) {
RTC_DCHECK_LE(spatial_layer, 0x0F);
RTC_DCHECK_LE(temporal_layer, 0x0F);
RTC_DCHECK_LE(target_bitrate_kbps, 0x00FFFFFFU);
bitrates_.push_back(
BitrateItem(spatial_layer, temporal_layer, target_bitrate_kbps));
}
const std::vector<TargetBitrate::BitrateItem>&
TargetBitrate::GetTargetBitrates() const {
return bitrates_;
}
size_t TargetBitrate::BlockLength() const {
return kTargetBitrateHeaderSizeBytes +
bitrates_.size() * kBitrateItemSizeBytes;
}
} // namespace rtcp
} // namespace webrtc

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
#include <vector>
#include "webrtc/base/basictypes.h"
namespace webrtc {
namespace rtcp {
class TargetBitrate {
public:
// TODO(sprang): This block type is just a place holder. We need to get an
// id assigned by IANA.
static constexpr uint8_t kBlockType = 42;
static const size_t kBitrateItemSizeBytes;
struct BitrateItem {
BitrateItem();
BitrateItem(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps);
uint8_t spatial_layer;
uint8_t temporal_layer;
uint32_t target_bitrate_kbps;
};
TargetBitrate();
~TargetBitrate();
void AddTargetBitrate(uint8_t spatial_layer,
uint8_t temporal_layer,
uint32_t target_bitrate_kbps);
const std::vector<BitrateItem>& GetTargetBitrates() const;
bool Parse(const uint8_t* block, uint16_t block_length);
void Create(uint8_t* buffer) const;
size_t BlockLength() const;
private:
std::vector<BitrateItem> bitrates_;
};
} // namespace rtcp
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
#include "webrtc/base/buffer.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/rtcp_packet_parser.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
namespace webrtc {
namespace {
using BitrateItem = rtcp::TargetBitrate::BitrateItem;
using rtcp::TargetBitrate;
using test::ParseSinglePacket;
constexpr uint32_t kSsrc = 0x12345678;
// clang-format off
const uint8_t kPacket[] = { TargetBitrate::kBlockType, // Block ID.
0x00, // Reserved.
0x00, 0x04, // Length = 4 words.
0x00, 0x01, 0x02, 0x03, // S0T0 0x010203 kbps.
0x01, 0x02, 0x03, 0x04, // S0T1 0x020304 kbps.
0x10, 0x03, 0x04, 0x05, // S1T0 0x030405 kbps.
0x11, 0x04, 0x05, 0x06 }; // S1T1 0x040506 kbps.
constexpr size_t kPacketLengthBlocks = ((sizeof(kPacket) + 3) / 4) - 1;
// clang-format on
void ExpectBirateItemEquals(const BitrateItem& expected,
const BitrateItem& actual) {
EXPECT_EQ(expected.spatial_layer, actual.spatial_layer);
EXPECT_EQ(expected.temporal_layer, actual.temporal_layer);
EXPECT_EQ(expected.target_bitrate_kbps, actual.target_bitrate_kbps);
}
void CheckBitrateItems(const std::vector<BitrateItem>& bitrates) {
EXPECT_EQ(4U, bitrates.size());
ExpectBirateItemEquals(BitrateItem(0, 0, 0x010203), bitrates[0]);
ExpectBirateItemEquals(BitrateItem(0, 1, 0x020304), bitrates[1]);
ExpectBirateItemEquals(BitrateItem(1, 0, 0x030405), bitrates[2]);
ExpectBirateItemEquals(BitrateItem(1, 1, 0x040506), bitrates[3]);
}
} // namespace
TEST(TargetBitrateTest, Parse) {
TargetBitrate target_bitrate;
EXPECT_TRUE(target_bitrate.Parse(kPacket, kPacketLengthBlocks));
CheckBitrateItems(target_bitrate.GetTargetBitrates());
}
TEST(TargetBitrateTest, FullPacket) {
const size_t kXRHeaderSize = 8; // RTCP header (4) + SSRC (4).
const size_t kTotalSize = kXRHeaderSize + sizeof(kPacket);
uint8_t kRtcpPacket[kTotalSize] = {2 << 6, 207, 0x00, (kTotalSize / 4) - 1,
0x12, 0x34, 0x56, 0x78}; // SSRC.
memcpy(&kRtcpPacket[kXRHeaderSize], kPacket, sizeof(kPacket));
rtcp::ExtendedReports xr;
EXPECT_TRUE(ParseSinglePacket(kRtcpPacket, &xr));
EXPECT_EQ(kSsrc, xr.sender_ssrc());
const rtc::Optional<TargetBitrate>& target_bitrate = xr.target_bitrate();
ASSERT_TRUE(static_cast<bool>(target_bitrate));
CheckBitrateItems(target_bitrate->GetTargetBitrates());
}
TEST(TargetBitrateTest, Create) {
TargetBitrate target_bitrate;
target_bitrate.AddTargetBitrate(0, 0, 0x010203);
target_bitrate.AddTargetBitrate(0, 1, 0x020304);
target_bitrate.AddTargetBitrate(1, 0, 0x030405);
target_bitrate.AddTargetBitrate(1, 1, 0x040506);
uint8_t buffer[sizeof(kPacket)] = {};
ASSERT_EQ(sizeof(kPacket), target_bitrate.BlockLength());
target_bitrate.Create(buffer);
EXPECT_EQ(0, memcmp(kPacket, buffer, sizeof(kPacket)));
}
} // namespace webrtc