diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 8ae5bbb2ed..6474253f30 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -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', diff --git a/webrtc/modules/rtp_rtcp/BUILD.gn b/webrtc/modules/rtp_rtcp/BUILD.gn index d386951cb0..9d69811ef3 100644 --- a/webrtc/modules/rtp_rtcp/BUILD.gn +++ b/webrtc/modules/rtp_rtcp/BUILD.gn @@ -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", diff --git a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi index 23a64b752f..3f1e935b2a 100644 --- a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi @@ -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', diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc index 8605925785..2c2a0a1356 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc @@ -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; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h index 342e38a1f2..beaf989c89 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h @@ -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. diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc new file mode 100644 index 0000000000..a551b15617 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc @@ -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::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(((time_ms << kAbsSendTimeFraction) + 500) / 1000) & + 0x00FFFFFF; + + ByteWriter::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::ReadBigEndian(data); + return true; +} + +bool TransmissionOffset::Write(uint8_t* data, int64_t value) { + RTC_CHECK_LE(value, 0x00ffffff); + ByteWriter::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::ReadBigEndian(data); + return true; +} + +bool TransportSequenceNumber::Write(uint8_t* data, uint16_t value) { + ByteWriter::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 diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h new file mode 100644 index 0000000000..cdbf806170 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h @@ -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_ 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 0000000000..f6634867f6 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet.cc @@ -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 + +#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::ReadBigEndian(data() + 2)); + return sequence_number_; +} + +uint32_t Packet::Timestamp() const { + RTC_DCHECK_EQ(timestamp_, ByteReader::ReadBigEndian(data() + 4)); + return timestamp_; +} + +uint32_t Packet::Ssrc() const { + RTC_DCHECK_EQ(ssrc_, ByteReader::ReadBigEndian(data() + 8)); + return ssrc_; +} + +std::vector Packet::Csrcs() const { + size_t num_csrc = data()[0] & 0x0F; + RTC_DCHECK_GE(capacity(), kFixedHeaderSize + num_csrc * 4); + std::vector csrcs(num_csrc); + for (size_t i = 0; i < num_csrc; ++i) { + csrcs[i] = + ByteReader::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 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( + &header->extension.transmissionTimeOffset); + header->extension.hasAbsoluteSendTime = + GetExtension(&header->extension.absoluteSendTime); + header->extension.hasTransportSequenceNumber = + GetExtension( + &header->extension.transportSequenceNumber); + header->extension.hasAudioLevel = GetExtension( + &header->extension.voiceActivity, &header->extension.audioLevel); + header->extension.hasVideoRotation = + GetExtension(&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::WriteBigEndian(WriteAt(2), seq_no); +} + +void Packet::SetTimestamp(uint32_t timestamp) { + timestamp_ = timestamp; + ByteWriter::WriteBigEndian(WriteAt(4), timestamp); +} + +void Packet::SetSsrc(uint32_t ssrc) { + ssrc_ = ssrc; + ByteWriter::WriteBigEndian(WriteAt(8), ssrc); +} + +void Packet::SetCsrcs(const std::vector& 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::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()); + } + 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::ReadBigEndian(&buffer[2]); + timestamp_ = ByteReader::ReadBigEndian(&buffer[4]); + ssrc_ = ByteReader::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::ReadBigEndian(&buffer[payload_offset_]); + size_t extensions_capacity = + ByteReader::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::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::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 diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet.h b/webrtc/modules/rtp_rtcp/source/rtp_packet.h new file mode 100644 index 0000000000..9e9f4c1375 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet.h @@ -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 + +#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 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& csrcs); + + // Header extensions. + template + bool GetExtension(Values...) const; + + template + bool SetExtension(Values...); + + template + 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 +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 +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 +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_ diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet_received.h b/webrtc/modules/rtp_rtcp/source/rtp_packet_received.h new file mode 100644 index 0000000000..e2222b9200 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet_received.h @@ -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_ diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h b/webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h new file mode 100644 index 0000000000..ad749ffb61 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet_to_send.h @@ -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_ diff --git a/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc new file mode 100644 index 0000000000..b992d2da90 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc @@ -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(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(kTimeOffset); + packet.SetExtension(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()); + packet.AllocatePayload(kPayloadSize); + // Can't set extension after payload. + EXPECT_FALSE(packet.SetExtension(kVoiceActive, kAudioLevel)); + // Unless reserved. + EXPECT_TRUE(packet.SetExtension(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(&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(&time_offset)); + EXPECT_EQ(kTimeOffset, time_offset); + bool voice_active; + uint8_t audio_level; + EXPECT_TRUE(packet.GetExtension(&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(&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(&time_offset)); + packet.IdentifyExtensions(&extensions); + EXPECT_TRUE(packet.GetExtension(&time_offset)); + EXPECT_EQ(kTimeOffset, time_offset); + EXPECT_EQ(0u, packet.payload_size()); + EXPECT_EQ(0u, packet.padding_size()); +} + +} // namespace webrtc diff --git a/webrtc/test/fuzzers/BUILD.gn b/webrtc/test/fuzzers/BUILD.gn index c46cc1e2c0..3e59339299 100644 --- a/webrtc/test/fuzzers/BUILD.gn +++ b/webrtc/test/fuzzers/BUILD.gn @@ -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 = [ diff --git a/webrtc/test/fuzzers/rtp_packet_fuzzer.cc b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc new file mode 100644 index 0000000000..a9efdb96ec --- /dev/null +++ b/webrtc/test/fuzzers/rtp_packet_fuzzer.cc @@ -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 +