Add functions to get/set rtp header extension by id.

This helps with dynamic size extensions.

BUG=webrtc:7433

Review-Url: https://codereview.webrtc.org/2789773004
Cr-Commit-Position: refs/heads/master@{#17505}
This commit is contained in:
danilchap
2017-04-03 06:16:30 -07:00
committed by Commit bot
parent 23425f9068
commit 653063f6fd
3 changed files with 191 additions and 90 deletions

View File

@ -30,6 +30,11 @@ constexpr uint16_t kOneByteExtensionId = 0xBEDE;
constexpr size_t kOneByteHeaderSize = 1;
constexpr size_t kDefaultPacketSize = 1500;
} // namespace
constexpr size_t Packet::kMaxExtensionHeaders;
constexpr int Packet::kMinExtensionId;
constexpr int Packet::kMaxExtensionId;
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -273,6 +278,97 @@ void Packet::SetCsrcs(const std::vector<uint32_t>& csrcs) {
buffer_.SetSize(payload_offset_);
}
bool Packet::HasRawExtension(int id) const {
RTC_DCHECK_GE(id, kMinExtensionId);
RTC_DCHECK_LE(id, kMaxExtensionId);
return extension_entries_[id - 1].offset != 0;
}
rtc::ArrayView<const uint8_t> Packet::GetRawExtension(int id) const {
RTC_DCHECK_GE(id, kMinExtensionId);
RTC_DCHECK_LE(id, kMaxExtensionId);
const ExtensionInfo& extension = extension_entries_[id - 1];
if (extension.offset == 0)
return nullptr;
return rtc::MakeArrayView(data() + extension.offset, extension.length);
}
bool Packet::SetRawExtension(int id, rtc::ArrayView<const uint8_t> data) {
auto buffer = AllocateRawExtension(id, data.size());
if (buffer.empty())
return false;
RTC_DCHECK_EQ(buffer.size(), data.size());
memcpy(buffer.data(), data.data(), data.size());
return true;
}
rtc::ArrayView<uint8_t> Packet::AllocateRawExtension(int id, size_t length) {
RTC_DCHECK_GE(id, kMinExtensionId);
RTC_DCHECK_LE(id, kMaxExtensionId);
RTC_DCHECK_GE(length, 1);
RTC_DCHECK_LE(length, 16);
ExtensionInfo* extension_entry = &extension_entries_[id - 1];
if (extension_entry->offset != 0) {
// Extension already reserved. Check if same length is used.
if (extension_entry->length == length)
return rtc::MakeArrayView(WriteAt(extension_entry->offset), length);
LOG(LS_ERROR) << "Length mismatch for extension id " << id << " type "
<< static_cast<int>(extension_entry->type) << ": expected "
<< static_cast<int>(extension_entry->length) << ". received "
<< length;
return nullptr;
}
if (payload_size_ > 0) {
LOG(LS_ERROR) << "Can't add new extension id " << id
<< " after payload was set.";
return nullptr;
}
if (padding_size_ > 0) {
LOG(LS_ERROR) << "Can't add new extension id " << id
<< " after padding was set.";
return nullptr;
}
size_t num_csrc = data()[0] & 0x0F;
size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
size_t new_extensions_size = extensions_size_ + kOneByteHeaderSize + length;
if (extensions_offset + new_extensions_size > capacity()) {
LOG(LS_ERROR)
<< "Extension cannot be registered: Not enough space left in buffer.";
return nullptr;
}
// All checks passed, write down the extension headers.
if (extensions_size_ == 0) {
RTC_DCHECK_EQ(payload_offset_, kFixedHeaderSize + (num_csrc * 4));
WriteAt(0, data()[0] | 0x10); // Set extension bit.
// Profile specific ID always set to OneByteExtensionHeader.
ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
kOneByteExtensionId);
}
WriteAt(extensions_offset + extensions_size_, (id << 4) | (length - 1));
extension_entry->offset =
extensions_offset + extensions_size_ + kOneByteHeaderSize;
extension_entry->length = length;
extensions_size_ = new_extensions_size;
// Update header length field.
uint16_t extensions_words = (extensions_size_ + 3) / 4; // Wrap up to 32bit.
ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 2),
extensions_words);
// Fill extension padding place with zeroes.
size_t extension_padding_size = 4 * extensions_words - extensions_size_;
memset(WriteAt(extensions_offset + extensions_size_), 0,
extension_padding_size);
payload_offset_ = extensions_offset + 4 * extensions_words;
buffer_.SetSize(payload_offset_);
return rtc::MakeArrayView(WriteAt(extension_entry->offset), length);
}
uint8_t* Packet::AllocatePayload(size_t size_bytes) {
// Reset payload size to 0. If CopyOnWrite buffer_ was shared, this will cause
// reallocation and memcpy. Keeping just header reduces memcpy size.
@ -460,86 +556,16 @@ bool Packet::FindExtension(ExtensionType type,
return false;
}
bool Packet::AllocateExtension(ExtensionType type,
uint8_t length,
uint16_t* offset) {
uint8_t extension_id = ExtensionManager::kInvalidId;
ExtensionInfo* extension_entry = nullptr;
rtc::ArrayView<uint8_t> Packet::AllocateExtension(ExtensionType type,
size_t length) {
for (size_t i = 0; i < kMaxExtensionHeaders; ++i) {
if (extension_entries_[i].type == type) {
extension_id = i + 1;
extension_entry = &extension_entries_[i];
break;
int extension_id = i + 1;
return AllocateRawExtension(extension_id, length);
}
}
if (!extension_entry) // Extension not registered.
return false;
if (extension_entry->length != 0) { // Already allocated.
if (length != extension_entry->length) {
LOG(LS_WARNING) << "Length mismatch for extension '" << type
<< "': expected " << static_cast<int>(length)
<< ", received "
<< static_cast<int>(extension_entry->length);
return false;
}
*offset = extension_entry->offset;
return true;
}
// Can't add new extension after payload/padding was set.
if (payload_size_ > 0) {
return false;
}
if (padding_size_ > 0) {
return false;
}
RTC_DCHECK_GT(length, 0);
RTC_DCHECK_LE(length, 16);
size_t num_csrc = data()[0] & 0x0F;
size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
if (extensions_offset + extensions_size_ + kOneByteHeaderSize + length >
capacity()) {
LOG(LS_WARNING) << "Extension cannot be registered: "
"Not enough space left in buffer.";
return false;
}
uint16_t new_extensions_size =
extensions_size_ + kOneByteHeaderSize + length;
uint16_t extensions_words =
(new_extensions_size + 3) / 4; // Wrap up to 32bit.
// All checks passed, write down the extension.
if (extensions_size_ == 0) {
RTC_DCHECK_EQ(payload_offset_, kFixedHeaderSize + (num_csrc * 4));
WriteAt(0, data()[0] | 0x10); // Set extension bit.
// Profile specific ID always set to OneByteExtensionHeader.
ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
kOneByteExtensionId);
}
WriteAt(extensions_offset + extensions_size_,
(extension_id << 4) | (length - 1));
extension_entry->length = length;
*offset = extensions_offset + kOneByteHeaderSize + extensions_size_;
extension_entry->offset = *offset;
extensions_size_ = new_extensions_size;
// Update header length field.
ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 2),
extensions_words);
// Fill extension padding place with zeroes.
size_t extension_padding_size = 4 * extensions_words - extensions_size_;
memset(WriteAt(extensions_offset + extensions_size_), 0,
extension_padding_size);
payload_offset_ = extensions_offset + 4 * extensions_words;
buffer_.SetSize(payload_offset_);
return true;
// Extension not registered.
return nullptr;
}
uint8_t* Packet::WriteAt(size_t offset) {