| Index: webrtc/modules/rtp_rtcp/source/rtp_packet.cc
|
| diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..eb5498b7813527ca3551170dfad6628dc27f676c
|
| --- /dev/null
|
| +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
|
| @@ -0,0 +1,514 @@
|
| +/*
|
| + * 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/rtp_packet.h"
|
| +
|
| +#include <cstring>
|
| +
|
| +#include "webrtc/base/checks.h"
|
| +#include "webrtc/base/logging.h"
|
| +#include "webrtc/base/random.h"
|
| +#include "webrtc/common_types.h"
|
| +#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
| +#include "webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h"
|
| +#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
|
| +
|
| +namespace webrtc {
|
| +namespace rtp {
|
| +namespace {
|
| +constexpr size_t kFixedHeaderSize = 12;
|
| +constexpr uint8_t kRtpVersion = 2;
|
| +constexpr uint16_t kOneByteExtensionId = 0xBEDE;
|
| +constexpr size_t kDefaultPacketSize = 1500;
|
| +} // namespace
|
| +// 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
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// |V=2|P|X| CC |M| PT | sequence number |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | timestamp |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | synchronization source (SSRC) identifier |
|
| +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
| +// | Contributing source (CSRC) identifiers |
|
| +// | .... |
|
| +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
| +// |One-byte eXtensions id = 0xbede| length in 32bits |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | Extensions |
|
| +// | .... |
|
| +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
| +// | Payload |
|
| +// | .... : padding... |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | padding | Padding size |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +Packet::Packet(const ExtensionManager* extensions)
|
| + : extensions_(extensions), buffer_(kDefaultPacketSize) {
|
| + Clear();
|
| +}
|
| +
|
| +Packet::Packet(const ExtensionManager* extensions, size_t capacity)
|
| + : extensions_(extensions), buffer_(capacity) {
|
| + RTC_DCHECK_GE(capacity, kFixedHeaderSize);
|
| + Clear();
|
| +}
|
| +
|
| +Packet::~Packet() {}
|
| +
|
| +void Packet::IdentifyExtensions(const ExtensionManager* extensions) {
|
| + RTC_DCHECK(extensions);
|
| + extensions_ = extensions;
|
| + for (size_t i = 0; i < num_extensions_; ++i) {
|
| + uint8_t id = data()[extension_entries_[i].offset - 1] >> 4;
|
| + extension_entries_[i].type = extensions_->GetType(id);
|
| + }
|
| +}
|
| +
|
| +bool Packet::Parse(const uint8_t* buffer, size_t buffer_size) {
|
| + if (!ParseBuffer(buffer, buffer_size)) {
|
| + Clear();
|
| + return false;
|
| + }
|
| + RTC_DCHECK_EQ(size(), buffer_size);
|
| + buffer_.SetData(buffer, buffer_size);
|
| + return true;
|
| +}
|
| +
|
| +bool Packet::Parse(rtc::Buffer buffer) {
|
| + if (!ParseBuffer(buffer.data(), buffer.size())) {
|
| + Clear();
|
| + return false;
|
| + }
|
| + RTC_DCHECK_EQ(size(), buffer.size());
|
| + buffer_ = std::move(buffer);
|
| + return true;
|
| +}
|
| +
|
| +bool Packet::Marker() const {
|
| + RTC_DCHECK_EQ(marker_, (data()[1] & 0x80) != 0);
|
| + return marker_;
|
| +}
|
| +
|
| +uint8_t Packet::PayloadType() const {
|
| + RTC_DCHECK_EQ(payload_type_, data()[1] & 0x7f);
|
| + return payload_type_;
|
| +}
|
| +
|
| +uint16_t Packet::SequenceNumber() const {
|
| + RTC_DCHECK_EQ(sequence_number_,
|
| + ByteReader<uint16_t>::ReadBigEndian(data() + 2));
|
| + return sequence_number_;
|
| +}
|
| +
|
| +uint32_t Packet::Timestamp() const {
|
| + RTC_DCHECK_EQ(timestamp_, ByteReader<uint32_t>::ReadBigEndian(data() + 4));
|
| + return timestamp_;
|
| +}
|
| +
|
| +uint32_t Packet::Ssrc() const {
|
| + RTC_DCHECK_EQ(ssrc_, ByteReader<uint32_t>::ReadBigEndian(data() + 8));
|
| + return ssrc_;
|
| +}
|
| +
|
| +std::vector<uint32_t> Packet::Csrcs() const {
|
| + size_t num_csrc = data()[0] & 0x0F;
|
| + RTC_DCHECK_GE(capacity(), kFixedHeaderSize + num_csrc * 4);
|
| + std::vector<uint32_t> csrcs(num_csrc);
|
| + for (size_t i = 0; i < num_csrc; ++i) {
|
| + csrcs[i] =
|
| + ByteReader<uint32_t>::ReadBigEndian(&data()[kFixedHeaderSize + i * 4]);
|
| + }
|
| + return csrcs;
|
| +}
|
| +
|
| +void Packet::GetHeader(RTPHeader* header) const {
|
| + header->markerBit = Marker();
|
| + header->payloadType = PayloadType();
|
| + header->sequenceNumber = SequenceNumber();
|
| + header->timestamp = Timestamp();
|
| + header->ssrc = Ssrc();
|
| + std::vector<uint32_t> csrcs = Csrcs();
|
| + header->numCSRCs = csrcs.size();
|
| + for (size_t i = 0; i < csrcs.size(); ++i) {
|
| + header->arrOfCSRCs[i] = csrcs[i];
|
| + }
|
| + header->paddingLength = padding_size();
|
| + header->headerLength = headers_size();
|
| + header->payload_type_frequency = 0;
|
| + header->extension.hasTransmissionTimeOffset =
|
| + GetExtension<TransmissionOffset>(
|
| + &header->extension.transmissionTimeOffset);
|
| + header->extension.hasAbsoluteSendTime =
|
| + GetExtension<AbsoluteSendTime>(&header->extension.absoluteSendTime);
|
| + header->extension.hasTransportSequenceNumber =
|
| + GetExtension<TransportSequenceNumber>(
|
| + &header->extension.transportSequenceNumber);
|
| + header->extension.hasAudioLevel = GetExtension<AudioLevel>(
|
| + &header->extension.voiceActivity, &header->extension.audioLevel);
|
| + header->extension.hasVideoRotation =
|
| + GetExtension<VideoOrientation>(&header->extension.videoRotation);
|
| +}
|
| +
|
| +size_t Packet::headers_size() const {
|
| + return payload_offset_;
|
| +}
|
| +
|
| +size_t Packet::payload_size() const {
|
| + return payload_size_;
|
| +}
|
| +
|
| +size_t Packet::padding_size() const {
|
| + return padding_size_;
|
| +}
|
| +
|
| +const uint8_t* Packet::payload() const {
|
| + return data() + payload_offset_;
|
| +}
|
| +
|
| +size_t Packet::capacity() const {
|
| + return buffer_.size();
|
| +}
|
| +
|
| +size_t Packet::size() const {
|
| + return payload_offset_ + payload_size_ + padding_size_;
|
| +}
|
| +
|
| +const uint8_t* Packet::data() const {
|
| + return buffer_.data();
|
| +}
|
| +
|
| +size_t Packet::FreeCapacity() const {
|
| + return capacity() - size();
|
| +}
|
| +
|
| +size_t Packet::MaxPayloadSize() const {
|
| + return capacity() - payload_offset_;
|
| +}
|
| +
|
| +void Packet::CopyHeader(const Packet& packet) {
|
| + RTC_DCHECK_GE(capacity(), packet.headers_size());
|
| +
|
| + marker_ = packet.marker_;
|
| + payload_type_ = packet.payload_type_;
|
| + sequence_number_ = packet.sequence_number_;
|
| + timestamp_ = packet.timestamp_;
|
| + ssrc_ = packet.ssrc_;
|
| + payload_offset_ = packet.payload_offset_;
|
| + num_extensions_ = packet.num_extensions_;
|
| + for (size_t i = 0; i < num_extensions_; ++i) {
|
| + extension_entries_[i] = packet.extension_entries_[i];
|
| + }
|
| + extensions_size_ = packet.extensions_size_;
|
| + buffer_.SetData(packet.data(), packet.headers_size());
|
| + // Reset payload and padding.
|
| + payload_size_ = 0;
|
| + padding_size_ = 0;
|
| +}
|
| +
|
| +void Packet::SetMarker(bool marker_bit) {
|
| + marker_ = marker_bit;
|
| + if (marker_) {
|
| + WriteAt(1, data()[1] | 0x80);
|
| + } else {
|
| + WriteAt(1, data()[1] & 0x7F);
|
| + }
|
| +}
|
| +
|
| +void Packet::SetPayloadType(uint8_t payload_type) {
|
| + RTC_DCHECK_LE(payload_type, 0x7Fu);
|
| + payload_type_ = payload_type;
|
| + WriteAt(1, (data()[1] & 0x80) | payload_type);
|
| +}
|
| +
|
| +void Packet::SetSequenceNumber(uint16_t seq_no) {
|
| + sequence_number_ = seq_no;
|
| + ByteWriter<uint16_t>::WriteBigEndian(WriteAt(2), seq_no);
|
| +}
|
| +
|
| +void Packet::SetTimestamp(uint32_t timestamp) {
|
| + timestamp_ = timestamp;
|
| + ByteWriter<uint32_t>::WriteBigEndian(WriteAt(4), timestamp);
|
| +}
|
| +
|
| +void Packet::SetSsrc(uint32_t ssrc) {
|
| + ssrc_ = ssrc;
|
| + ByteWriter<uint32_t>::WriteBigEndian(WriteAt(8), ssrc);
|
| +}
|
| +
|
| +void Packet::SetCsrcs(const std::vector<uint32_t>& csrcs) {
|
| + RTC_DCHECK_EQ(num_extensions_, 0u);
|
| + RTC_DCHECK_EQ(payload_size_, 0u);
|
| + RTC_DCHECK_EQ(padding_size_, 0u);
|
| + RTC_DCHECK_LE(csrcs.size(), 0x0fu);
|
| + RTC_DCHECK_LE(kFixedHeaderSize + 4 * csrcs.size(), capacity());
|
| + payload_offset_ = kFixedHeaderSize + 4 * csrcs.size();
|
| + WriteAt(0, (data()[0] & 0xF0) | csrcs.size());
|
| + size_t offset = kFixedHeaderSize;
|
| + for (uint32_t csrc : csrcs) {
|
| + ByteWriter<uint32_t>::WriteBigEndian(WriteAt(offset), csrc);
|
| + offset += 4;
|
| + }
|
| +}
|
| +
|
| +uint8_t* Packet::AllocatePayload(size_t size_bytes) {
|
| + RTC_DCHECK_EQ(padding_size_, 0u);
|
| + if (payload_offset_ + size_bytes > capacity()) {
|
| + LOG(LS_WARNING) << "Cannot set payload, not enough space in buffer.";
|
| + return nullptr;
|
| + }
|
| + payload_size_ = size_bytes;
|
| + return WriteAt(payload_offset_);
|
| +}
|
| +
|
| +void Packet::SetPayloadSize(size_t size_bytes) {
|
| + RTC_DCHECK_EQ(padding_size_, 0u);
|
| + RTC_DCHECK_LE(size_bytes, payload_size_);
|
| + payload_size_ = size_bytes;
|
| +}
|
| +
|
| +bool Packet::SetPadding(uint8_t size_bytes, Random* random) {
|
| + RTC_DCHECK(random);
|
| + if (payload_offset_ + payload_size_ + size_bytes > capacity()) {
|
| + LOG(LS_WARNING) << "Cannot set padding size " << size_bytes << ", only "
|
| + << (capacity() - payload_offset_ - payload_size_)
|
| + << " bytes left in buffer.";
|
| + return false;
|
| + }
|
| + padding_size_ = size_bytes;
|
| + if (padding_size_ > 0) {
|
| + size_t padding_offset = payload_offset_ + payload_size_;
|
| + size_t padding_end = padding_offset + padding_size_;
|
| + for (size_t offset = padding_offset; offset < padding_end - 1; ++offset) {
|
| + WriteAt(offset, random->Rand<uint8_t>());
|
| + }
|
| + WriteAt(padding_end - 1, padding_size_);
|
| + WriteAt(0, data()[0] | 0x20); // Set padding bit.
|
| + } else {
|
| + WriteAt(0, data()[0] & ~0x20); // Clear padding bit.
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void Packet::Clear() {
|
| + marker_ = false;
|
| + payload_type_ = 0;
|
| + sequence_number_ = 0;
|
| + timestamp_ = 0;
|
| + ssrc_ = 0;
|
| + payload_offset_ = kFixedHeaderSize;
|
| + payload_size_ = 0;
|
| + padding_size_ = 0;
|
| + num_extensions_ = 0;
|
| + extensions_size_ = 0;
|
| +
|
| + memset(WriteAt(0), 0, kFixedHeaderSize);
|
| + WriteAt(0, kRtpVersion << 6);
|
| +}
|
| +
|
| +bool Packet::ParseBuffer(const uint8_t* buffer, size_t size) {
|
| + if (size < kFixedHeaderSize) {
|
| + return false;
|
| + }
|
| + const uint8_t version = buffer[0] >> 6;
|
| + if (version != kRtpVersion) {
|
| + return false;
|
| + }
|
| + const bool has_padding = (buffer[0] & 0x20) != 0;
|
| + const bool has_extension = (buffer[0] & 0x10) != 0;
|
| + const uint8_t number_of_crcs = buffer[0] & 0x0f;
|
| + marker_ = (buffer[1] & 0x80) != 0;
|
| + payload_type_ = buffer[1] & 0x7f;
|
| +
|
| + sequence_number_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]);
|
| + timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
|
| + ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
|
| + if (size < kFixedHeaderSize + number_of_crcs * 4) {
|
| + return false;
|
| + }
|
| + payload_offset_ = kFixedHeaderSize + number_of_crcs * 4;
|
| +
|
| + if (has_padding) {
|
| + padding_size_ = buffer[size - 1];
|
| + if (padding_size_ == 0) {
|
| + LOG(LS_WARNING) << "Padding was set, but padding size is zero";
|
| + return false;
|
| + }
|
| + } else {
|
| + padding_size_ = 0;
|
| + }
|
| +
|
| + num_extensions_ = 0;
|
| + extensions_size_ = 0;
|
| + if (has_extension) {
|
| + /* RTP header extension, RFC 3550.
|
| + 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
|
| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| + | defined by profile | length |
|
| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| + | header extension |
|
| + | .... |
|
| + */
|
| + size_t extension_offset = payload_offset_ + 4;
|
| + if (extension_offset > size) {
|
| + return false;
|
| + }
|
| + uint16_t profile =
|
| + ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_]);
|
| + size_t extensions_capacity =
|
| + ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_ + 2]);
|
| + extensions_capacity *= 4;
|
| + if (extension_offset + extensions_capacity > size) {
|
| + return false;
|
| + }
|
| + if (profile != kOneByteExtensionId) {
|
| + LOG(LS_WARNING) << "Unsupported rtp extension " << profile;
|
| + } else {
|
| + constexpr size_t kOneByteHeaderSize = 1;
|
| + constexpr uint8_t kPaddingId = 0;
|
| + constexpr uint8_t kReservedId = 15;
|
| + while (extensions_size_ + kOneByteHeaderSize < extensions_capacity) {
|
| + uint8_t id = buffer[extension_offset + extensions_size_] >> 4;
|
| + if (id == kReservedId) {
|
| + break;
|
| + } else if (id == kPaddingId) {
|
| + extensions_size_++;
|
| + continue;
|
| + }
|
| + uint8_t length =
|
| + 1 + (buffer[extension_offset + extensions_size_] & 0xf);
|
| + extensions_size_ += kOneByteHeaderSize;
|
| + if (num_extensions_ >= kMaxExtensionHeaders) {
|
| + LOG(LS_WARNING) << "Too many extensions.";
|
| + return false;
|
| + }
|
| + extension_entries_[num_extensions_].type =
|
| + extensions_ ? extensions_->GetType(id)
|
| + : ExtensionManager::kInvalidType;
|
| + extension_entries_[num_extensions_].length = length;
|
| + extension_entries_[num_extensions_].offset =
|
| + extension_offset + extensions_size_;
|
| + num_extensions_++;
|
| + extensions_size_ += length;
|
| + }
|
| + }
|
| + payload_offset_ = extension_offset + extensions_capacity;
|
| + }
|
| +
|
| + if (payload_offset_ + padding_size_ > size) {
|
| + return false;
|
| + }
|
| + payload_size_ = size - payload_offset_ - padding_size_;
|
| + return true;
|
| +}
|
| +
|
| +bool Packet::FindExtension(ExtensionType type,
|
| + uint8_t length,
|
| + uint16_t* offset) const {
|
| + RTC_DCHECK(offset);
|
| + for (size_t i = 0; i < num_extensions_; ++i) {
|
| + if (extension_entries_[i].type == type) {
|
| + RTC_CHECK_EQ(length, extension_entries_[i].length)
|
| + << "Length mismatch for extension '" << type << "'"
|
| + << "should be " << length << ", received "
|
| + << extension_entries_[i].length;
|
| + *offset = extension_entries_[i].offset;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool Packet::AllocateExtension(ExtensionType type,
|
| + uint8_t length,
|
| + uint16_t* offset) {
|
| + if (!extensions_) {
|
| + return false;
|
| + }
|
| + if (FindExtension(type, length, 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;
|
| + }
|
| +
|
| + uint8_t extension_id = extensions_->GetId(type);
|
| + if (extension_id == ExtensionManager::kInvalidId) {
|
| + return false;
|
| + }
|
| + RTC_DCHECK_GT(length, 0u);
|
| + RTC_DCHECK_LE(length, 16u);
|
| +
|
| + size_t num_csrc = data()[0] & 0x0F;
|
| + size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
|
| + constexpr uint8_t kExtensionHeaderSize = 1; // One-byte extensions only.
|
| + if (extensions_offset + extensions_size_ + kExtensionHeaderSize + length >
|
| + capacity()) {
|
| + LOG(LS_WARNING) << "Extension cannot be registered: "
|
| + "Not enough space left in buffer.";
|
| + return false;
|
| + }
|
| +
|
| + uint16_t new_extensions_size =
|
| + extensions_size_ + kExtensionHeaderSize + length;
|
| + uint16_t extensions_words =
|
| + (new_extensions_size + 3) / 4; // Wrap up to 32bit.
|
| + if (extensions_words > 0xFFFF) {
|
| + LOG(LS_WARNING) << "Too much extension header data, exceeds 2^16 DWORDS.";
|
| + return false;
|
| + }
|
| +
|
| + // All checks passed, write down the extension.
|
| + if (num_extensions_ == 0) {
|
| + RTC_DCHECK_EQ(payload_offset_, kFixedHeaderSize + (num_csrc * 4));
|
| + RTC_DCHECK_EQ(extensions_size_, 0);
|
| + 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));
|
| + RTC_DCHECK(num_extensions_ < kMaxExtensionHeaders);
|
| + extension_entries_[num_extensions_].type = type;
|
| + extension_entries_[num_extensions_].length = length;
|
| + *offset = extensions_offset + kExtensionHeaderSize + extensions_size_;
|
| + extension_entries_[num_extensions_].offset = *offset;
|
| + ++num_extensions_;
|
| + 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;
|
| + return true;
|
| +}
|
| +
|
| +uint8_t* Packet::WriteAt(size_t offset) {
|
| + return buffer_.data() + offset;
|
| +}
|
| +
|
| +void Packet::WriteAt(size_t offset, uint8_t byte) {
|
| + buffer_.data()[offset] = byte;
|
| +}
|
| +
|
| +} // namespace rtp
|
| +} // namespace webrtc
|
|
|