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:
@ -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];
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
134
webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc
Normal file
134
webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc
Normal 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
|
||||
60
webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h
Normal file
60
webrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h
Normal 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_
|
||||
@ -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
|
||||
Reference in New Issue
Block a user