RtpPacket class introduced.

BUG=webrtc:1994, webrtc:5261

Review URL: https://codereview.webrtc.org/1841453004

Cr-Commit-Position: refs/heads/master@{#12444}
This commit is contained in:
danilchap 2016-04-20 05:25:10 -07:00 committed by Commit bot
parent 2ddf09397f
commit 1edb7ab7bd
14 changed files with 1391 additions and 4 deletions

View File

@ -337,6 +337,7 @@
'rtp_rtcp/source/rtp_format_vp8_unittest.cc',
'rtp_rtcp/source/rtp_format_vp9_unittest.cc',
'rtp_rtcp/source/rtp_packet_history_unittest.cc',
'rtp_rtcp/source/rtp_packet_unittest.cc',
'rtp_rtcp/source/rtp_payload_registry_unittest.cc',
'rtp_rtcp/source/rtp_rtcp_impl_unittest.cc',
'rtp_rtcp/source/rtp_header_extension_unittest.cc',

View File

@ -118,9 +118,15 @@ source_set("rtp_rtcp") {
"source/rtp_format_vp9.h",
"source/rtp_header_extension.cc",
"source/rtp_header_extension.h",
"source/rtp_header_extensions.cc",
"source/rtp_header_extensions.h",
"source/rtp_header_parser.cc",
"source/rtp_packet.cc",
"source/rtp_packet.h",
"source/rtp_packet_history.cc",
"source/rtp_packet_history.h",
"source/rtp_packet_received.h",
"source/rtp_packet_to_send.h",
"source/rtp_payload_registry.cc",
"source/rtp_receiver_audio.cc",
"source/rtp_receiver_audio.h",

View File

@ -35,10 +35,6 @@
'source/receive_statistics_impl.cc',
'source/receive_statistics_impl.h',
'source/remote_ntp_time_estimator.cc',
'source/rtp_header_parser.cc',
'source/rtp_rtcp_config.h',
'source/rtp_rtcp_impl.cc',
'source/rtp_rtcp_impl.h',
'source/rtcp_packet.cc',
'source/rtcp_packet.h',
'source/rtcp_packet/app.cc',
@ -103,8 +99,18 @@
'source/rtcp_utility.h',
'source/rtp_header_extension.cc',
'source/rtp_header_extension.h',
'source/rtp_header_extensions.cc',
'source/rtp_header_extensions.h',
'source/rtp_header_parser.cc',
'source/rtp_packet.cc',
'source/rtp_packet.h',
'source/rtp_packet_received.h',
'source/rtp_packet_to_send.h',
'source/rtp_receiver_impl.cc',
'source/rtp_receiver_impl.h',
'source/rtp_rtcp_config.h',
'source/rtp_rtcp_impl.cc',
'source/rtp_rtcp_impl.h',
'source/rtp_sender.cc',
'source/rtp_sender.h',
'source/rtp_utility.cc',

View File

@ -112,6 +112,14 @@ int32_t RtpHeaderExtensionMap::GetType(const uint8_t id,
return 0;
}
RTPExtensionType RtpHeaderExtensionMap::GetType(uint8_t id) const {
auto it = extensionMap_.find(id);
if (it == extensionMap_.end()) {
return kInvalidType;
}
return it->second->type;
}
int32_t RtpHeaderExtensionMap::GetId(const RTPExtensionType type,
uint8_t* id) const {
assert(id);
@ -129,6 +137,14 @@ int32_t RtpHeaderExtensionMap::GetId(const RTPExtensionType type,
return -1;
}
uint8_t RtpHeaderExtensionMap::GetId(RTPExtensionType type) const {
for (auto kv : extensionMap_) {
if (kv.second->type == type)
return kv.first;
}
return kInvalidId;
}
size_t RtpHeaderExtensionMap::GetTotalLengthInBytes() const {
// Get length for each extension block.
size_t length = 0;

View File

@ -70,6 +70,8 @@ struct HeaderExtension {
class RtpHeaderExtensionMap {
public:
static constexpr RTPExtensionType kInvalidType = kRtpExtensionNone;
static constexpr uint8_t kInvalidId = 0;
RtpHeaderExtensionMap();
~RtpHeaderExtensionMap();
@ -89,8 +91,12 @@ class RtpHeaderExtensionMap {
bool IsRegistered(RTPExtensionType type) const;
int32_t GetType(const uint8_t id, RTPExtensionType* type) const;
// Return kInvalidType if not found.
RTPExtensionType GetType(uint8_t id) const;
int32_t GetId(const RTPExtensionType type, uint8_t* id) const;
// Return kInvalidId if not found.
uint8_t GetId(RTPExtensionType type) const;
//
// Methods below ignore any inactive rtp header extensions.

View File

@ -0,0 +1,203 @@
/*
* 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_header_extensions.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_cvo.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
namespace webrtc {
// Absolute send time in RTP streams.
//
// The absolute send time is signaled to the receiver in-band using the
// general mechanism for RTP header extensions [RFC5285]. The payload
// of this extension (the transmitted value) is a 24-bit unsigned integer
// containing the sender's current time in seconds as a fixed point number
// with 18 bits fractional part.
//
// The form of the absolute send time extension block:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=2 | absolute send time |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const char* AbsoluteSendTime::kName =
"http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
bool AbsoluteSendTime::IsSupportedFor(MediaType type) {
return true;
}
bool AbsoluteSendTime::Parse(const uint8_t* data, uint32_t* value) {
*value = ByteReader<uint32_t, 3>::ReadBigEndian(data);
return true;
}
bool AbsoluteSendTime::Write(uint8_t* data, int64_t time_ms) {
const uint32_t kAbsSendTimeFraction = 18;
uint32_t time_24_bits =
static_cast<uint32_t>(((time_ms << kAbsSendTimeFraction) + 500) / 1000) &
0x00FFFFFF;
ByteWriter<uint32_t, 3>::WriteBigEndian(data, time_24_bits);
return true;
}
// An RTP Header Extension for Client-to-Mixer Audio Level Indication
//
// https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
//
// The form of the audio level extension block:
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=0 |V| level |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
const char* AudioLevel::kName = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
bool AudioLevel::IsSupportedFor(MediaType type) {
switch (type) {
case MediaType::ANY:
case MediaType::AUDIO:
return true;
case MediaType::VIDEO:
case MediaType::DATA:
return false;
}
RTC_NOTREACHED();
return false;
}
bool AudioLevel::Parse(const uint8_t* data,
bool* voice_activity,
uint8_t* audio_level) {
*voice_activity = (data[0] & 0x80) != 0;
*audio_level = data[0] & 0x7F;
return true;
}
bool AudioLevel::Write(uint8_t* data,
bool voice_activity,
uint8_t audio_level) {
RTC_CHECK_LE(audio_level, 0x7f);
data[0] = (voice_activity ? 0x80 : 0x00) | audio_level;
return true;
}
// From RFC 5450: Transmission Time Offsets in RTP Streams.
//
// The transmission time is signaled to the receiver in-band using the
// general mechanism for RTP header extensions [RFC5285]. The payload
// of this extension (the transmitted value) is a 24-bit signed integer.
// When added to the RTP timestamp of the packet, it represents the
// "effective" RTP transmission time of the packet, on the RTP
// timescale.
//
// The form of the transmission offset extension block:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=2 | transmission offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const char* TransmissionOffset::kName = "urn:ietf:params:rtp-hdrext:toffset";
bool TransmissionOffset::IsSupportedFor(MediaType type) {
switch (type) {
case MediaType::ANY:
case MediaType::VIDEO:
return true;
case MediaType::AUDIO:
case MediaType::DATA:
return false;
}
RTC_NOTREACHED();
return false;
}
bool TransmissionOffset::Parse(const uint8_t* data, int32_t* value) {
*value = ByteReader<int32_t, 3>::ReadBigEndian(data);
return true;
}
bool TransmissionOffset::Write(uint8_t* data, int64_t value) {
RTC_CHECK_LE(value, 0x00ffffff);
ByteWriter<int32_t, 3>::WriteBigEndian(data, value);
return true;
}
// 0 1 2
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | L=1 |transport wide sequence number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const char* TransportSequenceNumber::kName =
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions";
bool TransportSequenceNumber::IsSupportedFor(MediaType type) {
return true;
}
bool TransportSequenceNumber::Parse(const uint8_t* data, uint16_t* value) {
*value = ByteReader<uint16_t>::ReadBigEndian(data);
return true;
}
bool TransportSequenceNumber::Write(uint8_t* data, uint16_t value) {
ByteWriter<uint16_t>::WriteBigEndian(data, value);
return true;
}
// Coordination of Video Orientation in RTP streams.
//
// Coordination of Video Orientation consists in signaling of the current
// orientation of the image captured on the sender side to the receiver for
// appropriate rendering and displaying.
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=0 |0 0 0 0 C F R R|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const char* VideoOrientation::kName = "urn:3gpp:video-orientation";
bool VideoOrientation::IsSupportedFor(MediaType type) {
switch (type) {
case MediaType::ANY:
case MediaType::VIDEO:
return true;
case MediaType::AUDIO:
case MediaType::DATA:
return false;
}
RTC_NOTREACHED();
return false;
}
bool VideoOrientation::Parse(const uint8_t* data, VideoRotation* rotation) {
*rotation = ConvertCVOByteToVideoRotation(data[0] & 0x03);
return true;
}
bool VideoOrientation::Write(uint8_t* data, VideoRotation rotation) {
data[0] = ConvertVideoRotationToCVOByte(rotation);
return true;
}
bool VideoOrientation::Parse(const uint8_t* data, uint8_t* value) {
*value = data[0];
return true;
}
bool VideoOrientation::Write(uint8_t* data, uint8_t value) {
data[0] = value;
return true;
}
} // namespace webrtc

View File

@ -0,0 +1,75 @@
/*
* 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_RTP_HEADER_EXTENSIONS_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_
#include "webrtc/base/basictypes.h"
#include "webrtc/call.h"
#include "webrtc/common_video/rotation.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
namespace webrtc {
class AbsoluteSendTime {
public:
static constexpr RTPExtensionType kId = kRtpExtensionAbsoluteSendTime;
static constexpr uint8_t kValueSizeBytes = 3;
static const char* kName;
static bool IsSupportedFor(MediaType type);
static bool Parse(const uint8_t* data, uint32_t* time_ms);
static bool Write(uint8_t* data, int64_t time_ms);
};
class AudioLevel {
public:
static constexpr RTPExtensionType kId = kRtpExtensionAudioLevel;
static constexpr uint8_t kValueSizeBytes = 1;
static const char* kName;
static bool IsSupportedFor(MediaType type);
static bool Parse(const uint8_t* data,
bool* voice_activity,
uint8_t* audio_level);
static bool Write(uint8_t* data, bool voice_activity, uint8_t audio_level);
};
class TransmissionOffset {
public:
static constexpr RTPExtensionType kId = kRtpExtensionTransmissionTimeOffset;
static constexpr uint8_t kValueSizeBytes = 3;
static const char* kName;
static bool IsSupportedFor(MediaType type);
static bool Parse(const uint8_t* data, int32_t* time_ms);
static bool Write(uint8_t* data, int64_t time_ms);
};
class TransportSequenceNumber {
public:
static constexpr RTPExtensionType kId = kRtpExtensionTransportSequenceNumber;
static constexpr uint8_t kValueSizeBytes = 2;
static const char* kName;
static bool IsSupportedFor(MediaType type);
static bool Parse(const uint8_t* data, uint16_t* value);
static bool Write(uint8_t* data, uint16_t value);
};
class VideoOrientation {
public:
static constexpr RTPExtensionType kId = kRtpExtensionVideoRotation;
static constexpr uint8_t kValueSizeBytes = 1;
static const char* kName;
static bool IsSupportedFor(MediaType type);
static bool Parse(const uint8_t* data, VideoRotation* value);
static bool Write(uint8_t* data, VideoRotation value);
static bool Parse(const uint8_t* data, uint8_t* value);
static bool Write(uint8_t* data, uint8_t value);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_

View File

@ -0,0 +1,509 @@
/*
* 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 kOneByteHeaderSize = 1;
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 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;
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 (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 + kOneByteHeaderSize + 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

View File

@ -0,0 +1,186 @@
/*
* 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_RTP_PACKET_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_H_
#include <vector>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/buffer.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
namespace webrtc {
struct RTPHeader;
class RtpHeaderExtensionMap;
class Random;
namespace rtp {
class Packet {
public:
using ExtensionType = RTPExtensionType;
using ExtensionManager = RtpHeaderExtensionMap;
static constexpr size_t kMaxExtensionHeaders = 14;
// Parse and copy given buffer into Packet.
bool Parse(const uint8_t* buffer, size_t size);
// Parse and move given buffer into Packet.
bool Parse(rtc::Buffer packet);
// Maps parsed extensions to their types to allow use of GetExtension.
// Used after parsing when |extensions| can't be provided until base rtp
// header is parsed.
void IdentifyExtensions(const ExtensionManager* extensions);
// Header.
bool Marker() const;
uint8_t PayloadType() const;
uint16_t SequenceNumber() const;
uint32_t Timestamp() const;
uint32_t Ssrc() const;
std::vector<uint32_t> Csrcs() const;
// TODO(danilchap): Remove this function when all code update to use RtpPacket
// directly. Function is there just for easier backward compatibilty.
void GetHeader(RTPHeader* header) const;
size_t headers_size() const;
// Payload.
size_t payload_size() const;
size_t padding_size() const;
const uint8_t* payload() const;
// Buffer.
size_t capacity() const;
size_t size() const;
const uint8_t* data() const;
size_t FreeCapacity() const;
size_t MaxPayloadSize() const;
// Reset fields and buffer.
void Clear();
// Header setters.
void CopyHeader(const Packet& packet);
void SetMarker(bool marker_bit);
void SetPayloadType(uint8_t payload_type);
void SetSequenceNumber(uint16_t seq_no);
void SetTimestamp(uint32_t timestamp);
void SetSsrc(uint32_t ssrc);
// Writes csrc list. Assumes:
// a) There is enough room left in buffer.
// b) Extension headers, payload or padding data has not already been added.
void SetCsrcs(const std::vector<uint32_t>& csrcs);
// Header extensions.
template <typename Extension, typename... Values>
bool GetExtension(Values...) const;
template <typename Extension, typename... Values>
bool SetExtension(Values...);
template <typename Extension>
bool ReserveExtension();
// Reserve size_bytes for payload. Returns nullptr on failure.
uint8_t* AllocatePayload(size_t size_bytes);
void SetPayloadSize(size_t size_bytes);
bool SetPadding(uint8_t size_bytes, Random* random);
protected:
// |extensions| required for SetExtension/ReserveExtension functions during
// packet creating and used if available in Parse function.
// Adding and getting extensions will fail until |extensions| is
// provided via constructor or IdentifyExtensions function.
explicit Packet(const ExtensionManager* extensions);
Packet(const ExtensionManager* extensions, size_t capacity);
virtual ~Packet();
private:
struct ExtensionInfo {
ExtensionType type;
uint16_t offset;
uint8_t length;
};
// Helper function for Parse. Fill header fields using data in given buffer,
// but does not touch packet own buffer, leaving packet in invalid state.
bool ParseBuffer(const uint8_t* buffer, size_t size);
// Find an extension based on the type field of the parameter.
// If found, length field would be validated, the offset field will be set
// and true returned,
// otherwise the parameter will be unchanged and false is returned.
bool FindExtension(ExtensionType type,
uint8_t length,
uint16_t* offset) const;
// Find or allocate an extension, based on the type field of the parameter.
// If found, the length field be checked against what is already registered
// and the offset field will be set, then true is returned. If allocated, the
// length field will be used for allocation and the offset update to indicate
// position, the true is returned.
// If not found and allocations fails, false is returned and parameter remains
// unchanged.
bool AllocateExtension(ExtensionType type, uint8_t length, uint16_t* offset);
uint8_t* WriteAt(size_t offset);
void WriteAt(size_t offset, uint8_t byte);
const ExtensionManager* extensions_;
// Header.
bool marker_;
uint8_t payload_type_;
uint8_t padding_size_;
uint16_t sequence_number_;
uint32_t timestamp_;
uint32_t ssrc_;
size_t payload_offset_; // Match header size with csrcs and extensions.
size_t payload_size_;
uint8_t num_extensions_ = 0;
ExtensionInfo extension_entries_[kMaxExtensionHeaders];
uint16_t extensions_size_ = 0; // Unaligned.
rtc::Buffer buffer_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Packet);
};
template <typename Extension, typename... Values>
bool Packet::GetExtension(Values... values) const {
uint16_t offset = 0;
if (!FindExtension(Extension::kId, Extension::kValueSizeBytes, &offset))
return false;
return Extension::Parse(data() + offset, values...);
}
template <typename Extension, typename... Values>
bool Packet::SetExtension(Values... values) {
uint16_t offset = 0;
if (!AllocateExtension(Extension::kId, Extension::kValueSizeBytes, &offset))
return false;
return Extension::Write(WriteAt(offset), values...);
}
template <typename Extension>
bool Packet::ReserveExtension() {
uint16_t offset = 0;
if (!AllocateExtension(Extension::kId, Extension::kValueSizeBytes, &offset))
return false;
memset(WriteAt(offset), 0, Extension::kValueSizeBytes);
return true;
}
} // namespace rtp
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_H_

View File

@ -0,0 +1,56 @@
/*
* 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_RTP_PACKET_RECEIVED_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet.h"
#include "webrtc/system_wrappers/include/ntp_time.h"
namespace webrtc {
// Class to hold rtp packet with metadata for receiver side.
class RtpPacketReceived : public rtp::Packet {
public:
RtpPacketReceived() : Packet(nullptr) {}
explicit RtpPacketReceived(const ExtensionManager* extensions)
: Packet(extensions) {}
void GetHeader(RTPHeader* header) const {
Packet::GetHeader(header);
header->payload_type_frequency = payload_type_frequency();
}
// Time in local time base as close as it can to packet arrived on the
// network.
int64_t arrival_time_ms() const { return arrival_time_ms_; }
void set_arrival_time_ms(int64_t time) { arrival_time_ms_ = time; }
// Estimated from Timestamp() using rtcp Sender Reports.
NtpTime capture_ntp_time() const { return capture_time_; }
void set_capture_ntp_time(NtpTime time) { capture_time_ = time; }
// Flag if packet arrived via rtx.
bool retransmit() const { return retransmit_; }
void set_retransmit(bool value) { retransmit_ = value; }
int payload_type_frequency() const { return payload_type_frequency_; }
void set_payload_type_frequency(int value) {
payload_type_frequency_ = value;
}
private:
NtpTime capture_time_;
int64_t arrival_time_ms_ = 0;
int payload_type_frequency_ = 0;
bool retransmit_ = false;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_RECEIVED_H_

View File

@ -0,0 +1,33 @@
/*
* 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_RTP_PACKET_TO_SEND_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_TO_SEND_H_
#include "webrtc/modules/rtp_rtcp/source/rtp_packet.h"
namespace webrtc {
// Class to hold rtp packet with metadata for sender side.
class RtpPacketToSend : public rtp::Packet {
public:
explicit RtpPacketToSend(const ExtensionManager* extensions)
: Packet(extensions) {}
RtpPacketToSend(const ExtensionManager* extensions, size_t capacity)
: Packet(extensions, capacity) {}
// Time in local time base as close as it can to frame capture time.
int64_t capture_time_ms() const { return capture_time_ms_; }
void set_capture_time_ms(int64_t time) { capture_time_ms_ = time; }
private:
int64_t capture_time_ms_ = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_PACKET_TO_SEND_H_

View File

@ -0,0 +1,252 @@
/*
* 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_received.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/random.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
using testing::ElementsAreArray;
using testing::make_tuple;
namespace webrtc {
namespace {
constexpr int8_t kPayloadType = 100;
constexpr uint32_t kSsrc = 0x12345678;
constexpr uint16_t kSeqNum = 88;
constexpr uint32_t kTimestamp = 0x65431278;
constexpr uint8_t kTransmissionOffsetExtensionId = 1;
constexpr uint8_t kAudioLevelExtensionId = 9;
constexpr int32_t kTimeOffset = 0x56ce;
constexpr bool kVoiceActive = true;
constexpr uint8_t kAudioLevel = 0x5a;
constexpr size_t kMaxPaddingSize = 224u;
constexpr uint8_t kMinimumPacket[] = {
0x80, kPayloadType, 0x00, kSeqNum,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78};
constexpr uint8_t kPacketWithTO[] = {
0x90, kPayloadType, 0x00, kSeqNum,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x01,
0x12, 0x00, 0x56, 0xce};
constexpr uint8_t kPacketWithTOAndAL[] = {
0x90, kPayloadType, 0x00, kSeqNum,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0xbe, 0xde, 0x00, 0x02,
0x12, 0x00, 0x56, 0xce,
0x90, 0x80|kAudioLevel, 0x00, 0x00};
constexpr uint32_t kCsrcs[] = {0x34567890, 0x32435465};
constexpr uint8_t kPayload[] = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
constexpr uint8_t kPacketPaddingSize = 8;
constexpr uint8_t kPacket[] = {
0xb2, kPayloadType, 0x00, kSeqNum,
0x65, 0x43, 0x12, 0x78,
0x12, 0x34, 0x56, 0x78,
0x34, 0x56, 0x78, 0x90,
0x32, 0x43, 0x54, 0x65,
0xbe, 0xde, 0x00, 0x01,
0x12, 0x00, 0x56, 0xce,
'p', 'a', 'y', 'l', 'o', 'a', 'd',
'p', 'a', 'd', 'd', 'i', 'n', 'g', kPacketPaddingSize};
} // namespace
TEST(RtpPacketTest, CreateMinimum) {
RtpPacketToSend packet(nullptr);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
EXPECT_THAT(kMinimumPacket, ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWithExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
EXPECT_THAT(kPacketWithTO, ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, CreateWith2Extensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.SetExtension<TransmissionOffset>(kTimeOffset);
packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel);
EXPECT_THAT(kPacketWithTOAndAL,
ElementsAreArray(packet.data(), packet.size()));
}
TEST(RtpPacketTest, SetReservedExtensionsAfterPayload) {
const size_t kPayloadSize = 4;
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketToSend packet(&extensions);
EXPECT_TRUE(packet.ReserveExtension<TransmissionOffset>());
packet.AllocatePayload(kPayloadSize);
// Can't set extension after payload.
EXPECT_FALSE(packet.SetExtension<AudioLevel>(kVoiceActive, kAudioLevel));
// Unless reserved.
EXPECT_TRUE(packet.SetExtension<TransmissionOffset>(kTimeOffset));
}
TEST(RtpPacketTest, CreatePurePadding) {
const size_t kPaddingSize = kMaxPaddingSize - 1;
RtpPacketToSend packet(nullptr, 12 + kPaddingSize);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
Random random(0x123456789);
EXPECT_LT(packet.size(), packet.capacity());
EXPECT_FALSE(packet.SetPadding(kPaddingSize + 1, &random));
EXPECT_TRUE(packet.SetPadding(kPaddingSize, &random));
EXPECT_EQ(packet.size(), packet.capacity());
}
TEST(RtpPacketTest, CreateUnalignedPadding) {
const size_t kPayloadSize = 3; // Make padding start at unaligned address.
RtpPacketToSend packet(nullptr, 12 + kPayloadSize + kMaxPaddingSize);
packet.SetPayloadType(kPayloadType);
packet.SetSequenceNumber(kSeqNum);
packet.SetTimestamp(kTimestamp);
packet.SetSsrc(kSsrc);
packet.AllocatePayload(kPayloadSize);
Random r(0x123456789);
EXPECT_LT(packet.size(), packet.capacity());
EXPECT_TRUE(packet.SetPadding(kMaxPaddingSize, &r));
EXPECT_EQ(packet.size(), packet.capacity());
}
TEST(RtpPacketTest, ParseMinimum) {
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(kMinimumPacket, sizeof(kMinimumPacket)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_EQ(0u, packet.padding_size());
EXPECT_EQ(0u, packet.payload_size());
}
TEST(RtpPacketTest, ParseBuffer) {
rtc::Buffer unparsed(kMinimumPacket);
const uint8_t* raw = unparsed.data();
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(std::move(unparsed)));
EXPECT_EQ(raw, packet.data()); // Expect packet took over the buffer.
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_EQ(0u, packet.padding_size());
EXPECT_EQ(0u, packet.payload_size());
}
TEST(RtpPacketTest, ParseWithExtension) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
EXPECT_EQ(0u, packet.payload_size());
EXPECT_EQ(0u, packet.padding_size());
}
TEST(RtpPacketTest, ParseWith2Extensions) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
extensions.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacketWithTOAndAL, sizeof(kPacketWithTOAndAL)));
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
bool voice_active;
uint8_t audio_level;
EXPECT_TRUE(packet.GetExtension<AudioLevel>(&voice_active, &audio_level));
EXPECT_EQ(kVoiceActive, voice_active);
EXPECT_EQ(kAudioLevel, audio_level);
}
TEST(RtpPacketTest, ParseWithAllFeatures) {
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
RtpPacketReceived packet(&extensions);
EXPECT_TRUE(packet.Parse(kPacket, sizeof(kPacket)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
EXPECT_THAT(packet.Csrcs(), ElementsAreArray(kCsrcs));
EXPECT_THAT(make_tuple(packet.payload(), packet.payload_size()),
ElementsAreArray(kPayload));
EXPECT_EQ(kPacketPaddingSize, packet.padding_size());
int32_t time_offset;
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
}
TEST(RtpPacketTest, ParseWithExtensionDelayed) {
RtpPacketReceived packet;
EXPECT_TRUE(packet.Parse(kPacketWithTO, sizeof(kPacketWithTO)));
EXPECT_EQ(kPayloadType, packet.PayloadType());
EXPECT_EQ(kSeqNum, packet.SequenceNumber());
EXPECT_EQ(kTimestamp, packet.Timestamp());
EXPECT_EQ(kSsrc, packet.Ssrc());
RtpPacketToSend::ExtensionManager extensions;
extensions.Register(kRtpExtensionTransmissionTimeOffset,
kTransmissionOffsetExtensionId);
int32_t time_offset;
EXPECT_FALSE(packet.GetExtension<TransmissionOffset>(&time_offset));
packet.IdentifyExtensions(&extensions);
EXPECT_TRUE(packet.GetExtension<TransmissionOffset>(&time_offset));
EXPECT_EQ(kTimeOffset, time_offset);
EXPECT_EQ(0u, packet.payload_size());
EXPECT_EQ(0u, packet.padding_size());
}
} // namespace webrtc

View File

@ -75,6 +75,15 @@ webrtc_fuzzer_test("rtcp_receiver_fuzzer") {
]
}
webrtc_fuzzer_test("rtp_packet_fuzzer") {
sources = [
"rtp_packet_fuzzer.cc",
]
deps = [
"../../modules/rtp_rtcp/",
]
}
source_set("audio_decoder_fuzzer") {
public_configs = [ "../..:common_inherited_config" ]
sources = [

View File

@ -0,0 +1,29 @@
/*
* 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_received.h"
namespace webrtc {
void FuzzOneInput(const uint8_t* data, size_t size) {
RtpPacketReceived packet;
packet.Parse(data, size);
// Call packet accessors because they have extra checks.
packet.Marker();
packet.PayloadType();
packet.SequenceNumber();
packet.Timestamp();
packet.Ssrc();
packet.Csrcs();
}
} // namespace webrtc