blob: 9d4dce4dea27e4db04951ffc4f14d7f5b004fb41 [file] [log] [blame]
danilchap1edb7ab2016-04-20 05:25:10 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/rtp_rtcp/source/rtp_packet.h"
danilchap1edb7ab2016-04-20 05:25:10 -070012
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <cstdint>
danilchap1edb7ab2016-04-20 05:25:10 -070014#include <cstring>
Danil Chapovalova64a2fb2016-09-12 11:41:35 +020015#include <utility>
danilchap1edb7ab2016-04-20 05:25:10 -070016
Johannes Kron07ba2b92018-09-26 13:33:35 +020017#include "api/rtpparameters.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/rtp_rtcp/source/byte_io.h"
19#include "rtc_base/checks.h"
20#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010021#include "rtc_base/numerics/safe_conversions.h"
danilchap1edb7ab2016-04-20 05:25:10 -070022
23namespace webrtc {
danilchap1edb7ab2016-04-20 05:25:10 -070024namespace {
25constexpr size_t kFixedHeaderSize = 12;
26constexpr uint8_t kRtpVersion = 2;
Johannes Kron07ba2b92018-09-26 13:33:35 +020027constexpr uint16_t kOneByteExtensionProfileId = 0xBEDE;
28constexpr uint16_t kTwoByteExtensionProfileId = 0x1000;
29constexpr size_t kOneByteExtensionHeaderLength = 1;
30constexpr size_t kTwoByteExtensionHeaderLength = 2;
danilchap1edb7ab2016-04-20 05:25:10 -070031constexpr size_t kDefaultPacketSize = 1500;
32} // namespace
danilchap653063f2017-04-03 06:16:30 -070033
danilchap1edb7ab2016-04-20 05:25:10 -070034// 0 1 2 3
35// 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
36// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37// |V=2|P|X| CC |M| PT | sequence number |
38// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39// | timestamp |
40// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41// | synchronization source (SSRC) identifier |
42// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
43// | Contributing source (CSRC) identifiers |
44// | .... |
45// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Johannes Kron07ba2b92018-09-26 13:33:35 +020046// | header eXtension profile id | length in 32bits |
danilchap1edb7ab2016-04-20 05:25:10 -070047// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48// | Extensions |
49// | .... |
50// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
51// | Payload |
52// | .... : padding... |
53// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54// | padding | Padding size |
55// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Danil Chapovalov8769e172017-09-14 14:08:22 +020056RtpPacket::RtpPacket() : RtpPacket(nullptr, kDefaultPacketSize) {}
danilchap70f39a32016-12-16 05:48:18 -080057
Danil Chapovalov8769e172017-09-14 14:08:22 +020058RtpPacket::RtpPacket(const ExtensionManager* extensions)
59 : RtpPacket(extensions, kDefaultPacketSize) {}
danilchap1edb7ab2016-04-20 05:25:10 -070060
Danil Chapovalov8769e172017-09-14 14:08:22 +020061RtpPacket::RtpPacket(const RtpPacket&) = default;
nisse76e62b02017-05-31 02:24:52 -070062
Danil Chapovalov8769e172017-09-14 14:08:22 +020063RtpPacket::RtpPacket(const ExtensionManager* extensions, size_t capacity)
danilchap70f39a32016-12-16 05:48:18 -080064 : buffer_(capacity) {
danilchap1edb7ab2016-04-20 05:25:10 -070065 RTC_DCHECK_GE(capacity, kFixedHeaderSize);
66 Clear();
danilchap70f39a32016-12-16 05:48:18 -080067 if (extensions) {
Johannes Kronc5744b82018-09-24 14:50:48 +020068 extensions_ = *extensions;
danilchap70f39a32016-12-16 05:48:18 -080069 }
danilchap1edb7ab2016-04-20 05:25:10 -070070}
71
Danil Chapovalov8769e172017-09-14 14:08:22 +020072RtpPacket::~RtpPacket() {}
danilchap1edb7ab2016-04-20 05:25:10 -070073
Danil Chapovalov8769e172017-09-14 14:08:22 +020074void RtpPacket::IdentifyExtensions(const ExtensionManager& extensions) {
Johannes Kronc5744b82018-09-24 14:50:48 +020075 extensions_ = extensions;
danilchap1edb7ab2016-04-20 05:25:10 -070076}
77
Danil Chapovalov8769e172017-09-14 14:08:22 +020078bool RtpPacket::Parse(const uint8_t* buffer, size_t buffer_size) {
danilchap1edb7ab2016-04-20 05:25:10 -070079 if (!ParseBuffer(buffer, buffer_size)) {
80 Clear();
81 return false;
82 }
danilchap1edb7ab2016-04-20 05:25:10 -070083 buffer_.SetData(buffer, buffer_size);
Danil Chapovalov31e4e802016-08-03 18:27:40 +020084 RTC_DCHECK_EQ(size(), buffer_size);
danilchap1edb7ab2016-04-20 05:25:10 -070085 return true;
86}
87
Danil Chapovalov8769e172017-09-14 14:08:22 +020088bool RtpPacket::Parse(rtc::ArrayView<const uint8_t> packet) {
brandtrb29e6522016-12-21 06:37:18 -080089 return Parse(packet.data(), packet.size());
90}
91
Danil Chapovalov8769e172017-09-14 14:08:22 +020092bool RtpPacket::Parse(rtc::CopyOnWriteBuffer buffer) {
Danil Chapovalov31e4e802016-08-03 18:27:40 +020093 if (!ParseBuffer(buffer.cdata(), buffer.size())) {
danilchap1edb7ab2016-04-20 05:25:10 -070094 Clear();
95 return false;
96 }
Danil Chapovalov31e4e802016-08-03 18:27:40 +020097 size_t buffer_size = buffer.size();
danilchap1edb7ab2016-04-20 05:25:10 -070098 buffer_ = std::move(buffer);
Danil Chapovalov31e4e802016-08-03 18:27:40 +020099 RTC_DCHECK_EQ(size(), buffer_size);
danilchap1edb7ab2016-04-20 05:25:10 -0700100 return true;
101}
102
Danil Chapovalov8769e172017-09-14 14:08:22 +0200103std::vector<uint32_t> RtpPacket::Csrcs() const {
danilchap1edb7ab2016-04-20 05:25:10 -0700104 size_t num_csrc = data()[0] & 0x0F;
105 RTC_DCHECK_GE(capacity(), kFixedHeaderSize + num_csrc * 4);
106 std::vector<uint32_t> csrcs(num_csrc);
107 for (size_t i = 0; i < num_csrc; ++i) {
108 csrcs[i] =
109 ByteReader<uint32_t>::ReadBigEndian(&data()[kFixedHeaderSize + i * 4]);
110 }
111 return csrcs;
112}
113
Danil Chapovalov8769e172017-09-14 14:08:22 +0200114void RtpPacket::CopyHeaderFrom(const RtpPacket& packet) {
danilchap1edb7ab2016-04-20 05:25:10 -0700115 RTC_DCHECK_GE(capacity(), packet.headers_size());
116
117 marker_ = packet.marker_;
118 payload_type_ = packet.payload_type_;
119 sequence_number_ = packet.sequence_number_;
120 timestamp_ = packet.timestamp_;
121 ssrc_ = packet.ssrc_;
122 payload_offset_ = packet.payload_offset_;
Johannes Kronc5744b82018-09-24 14:50:48 +0200123 extensions_ = packet.extensions_;
124 extension_entries_ = packet.extension_entries_;
danilchap1edb7ab2016-04-20 05:25:10 -0700125 extensions_size_ = packet.extensions_size_;
126 buffer_.SetData(packet.data(), packet.headers_size());
127 // Reset payload and padding.
128 payload_size_ = 0;
129 padding_size_ = 0;
130}
131
Danil Chapovalov8769e172017-09-14 14:08:22 +0200132void RtpPacket::SetMarker(bool marker_bit) {
danilchap1edb7ab2016-04-20 05:25:10 -0700133 marker_ = marker_bit;
134 if (marker_) {
135 WriteAt(1, data()[1] | 0x80);
136 } else {
137 WriteAt(1, data()[1] & 0x7F);
138 }
139}
140
Danil Chapovalov8769e172017-09-14 14:08:22 +0200141void RtpPacket::SetPayloadType(uint8_t payload_type) {
danilchap1edb7ab2016-04-20 05:25:10 -0700142 RTC_DCHECK_LE(payload_type, 0x7Fu);
143 payload_type_ = payload_type;
144 WriteAt(1, (data()[1] & 0x80) | payload_type);
145}
146
Danil Chapovalov8769e172017-09-14 14:08:22 +0200147void RtpPacket::SetSequenceNumber(uint16_t seq_no) {
danilchap1edb7ab2016-04-20 05:25:10 -0700148 sequence_number_ = seq_no;
149 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(2), seq_no);
150}
151
Danil Chapovalov8769e172017-09-14 14:08:22 +0200152void RtpPacket::SetTimestamp(uint32_t timestamp) {
danilchap1edb7ab2016-04-20 05:25:10 -0700153 timestamp_ = timestamp;
154 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(4), timestamp);
155}
156
Danil Chapovalov8769e172017-09-14 14:08:22 +0200157void RtpPacket::SetSsrc(uint32_t ssrc) {
danilchap1edb7ab2016-04-20 05:25:10 -0700158 ssrc_ = ssrc;
159 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(8), ssrc);
160}
161
Danil Chapovalov7d2df3f2018-08-14 17:18:07 +0200162void RtpPacket::SetCsrcs(rtc::ArrayView<const uint32_t> csrcs) {
danilchap70f39a32016-12-16 05:48:18 -0800163 RTC_DCHECK_EQ(extensions_size_, 0);
kwibergaf476c72016-11-28 15:21:39 -0800164 RTC_DCHECK_EQ(payload_size_, 0);
165 RTC_DCHECK_EQ(padding_size_, 0);
danilchap1edb7ab2016-04-20 05:25:10 -0700166 RTC_DCHECK_LE(csrcs.size(), 0x0fu);
167 RTC_DCHECK_LE(kFixedHeaderSize + 4 * csrcs.size(), capacity());
168 payload_offset_ = kFixedHeaderSize + 4 * csrcs.size();
danilchap772bd8b2017-09-13 03:24:28 -0700169 WriteAt(0, (data()[0] & 0xF0) | rtc::dchecked_cast<uint8_t>(csrcs.size()));
danilchap1edb7ab2016-04-20 05:25:10 -0700170 size_t offset = kFixedHeaderSize;
171 for (uint32_t csrc : csrcs) {
172 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(offset), csrc);
173 offset += 4;
174 }
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200175 buffer_.SetSize(payload_offset_);
danilchap1edb7ab2016-04-20 05:25:10 -0700176}
177
Danil Chapovalov8769e172017-09-14 14:08:22 +0200178rtc::ArrayView<uint8_t> RtpPacket::AllocateRawExtension(int id, size_t length) {
Johannes Kron07ba2b92018-09-26 13:33:35 +0200179 RTC_DCHECK_GE(id, RtpExtension::kMinId);
Johannes Kron78cdde32018-10-05 10:00:46 +0200180 RTC_DCHECK_LE(id, RtpExtension::kMaxId);
danilchap653063f2017-04-03 06:16:30 -0700181 RTC_DCHECK_GE(length, 1);
Johannes Kron78cdde32018-10-05 10:00:46 +0200182 RTC_DCHECK_LE(length, RtpExtension::kMaxValueSize);
Johannes Kronc5744b82018-09-24 14:50:48 +0200183 const ExtensionInfo* extension_entry = FindExtensionInfo(id);
184 if (extension_entry != nullptr) {
danilchap653063f2017-04-03 06:16:30 -0700185 // Extension already reserved. Check if same length is used.
186 if (extension_entry->length == length)
187 return rtc::MakeArrayView(WriteAt(extension_entry->offset), length);
188
Johannes Kronc5744b82018-09-24 14:50:48 +0200189 RTC_LOG(LS_ERROR) << "Length mismatch for extension id " << id
Mirko Bonadei675513b2017-11-09 11:09:25 +0100190 << ": expected "
191 << static_cast<int>(extension_entry->length)
192 << ". received " << length;
danilchap653063f2017-04-03 06:16:30 -0700193 return nullptr;
194 }
195 if (payload_size_ > 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100196 RTC_LOG(LS_ERROR) << "Can't add new extension id " << id
197 << " after payload was set.";
danilchap653063f2017-04-03 06:16:30 -0700198 return nullptr;
199 }
200 if (padding_size_ > 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100201 RTC_LOG(LS_ERROR) << "Can't add new extension id " << id
202 << " after padding was set.";
danilchap653063f2017-04-03 06:16:30 -0700203 return nullptr;
204 }
205
Johannes Kron78cdde32018-10-05 10:00:46 +0200206 const size_t num_csrc = data()[0] & 0x0F;
207 const size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
208 // Determine if two-byte header is required for the extension based on id and
209 // length. Please note that a length of 0 also requires two-byte header
210 // extension. See RFC8285 Section 4.2-4.3.
211 const bool two_byte_header_required =
212 id > RtpExtension::kOneByteHeaderExtensionMaxId ||
213 length > RtpExtension::kOneByteHeaderExtensionMaxValueSize || length == 0;
Johannes Kron9581bc42018-10-23 10:17:39 +0200214 RTC_CHECK(!two_byte_header_required || extensions_.ExtmapAllowMixed());
Johannes Kron78cdde32018-10-05 10:00:46 +0200215
216 uint16_t profile_id;
217 if (extensions_size_ > 0) {
218 profile_id =
219 ByteReader<uint16_t>::ReadBigEndian(data() + extensions_offset - 4);
220 if (profile_id == kOneByteExtensionProfileId && two_byte_header_required) {
221 // Is buffer size big enough to fit promotion and new data field?
222 // The header extension will grow with one byte per already allocated
223 // extension + the size of the extension that is about to be allocated.
224 size_t expected_new_extensions_size =
225 extensions_size_ + extension_entries_.size() +
226 kTwoByteExtensionHeaderLength + length;
227 if (extensions_offset + expected_new_extensions_size > capacity()) {
228 RTC_LOG(LS_ERROR)
229 << "Extension cannot be registered: Not enough space left in "
230 "buffer to change to two-byte header extension and add new "
231 "extension.";
232 return nullptr;
233 }
234 // Promote already written data to two-byte header format.
235 PromoteToTwoByteHeaderExtension();
236 profile_id = kTwoByteExtensionProfileId;
237 }
238 } else {
239 // Profile specific ID, set to OneByteExtensionHeader unless
240 // TwoByteExtensionHeader is required.
241 profile_id = two_byte_header_required ? kTwoByteExtensionProfileId
242 : kOneByteExtensionProfileId;
243 }
244
245 const size_t extension_header_size = profile_id == kOneByteExtensionProfileId
246 ? kOneByteExtensionHeaderLength
247 : kTwoByteExtensionHeaderLength;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200248 size_t new_extensions_size =
Johannes Kron78cdde32018-10-05 10:00:46 +0200249 extensions_size_ + extension_header_size + length;
danilchap653063f2017-04-03 06:16:30 -0700250 if (extensions_offset + new_extensions_size > capacity()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100251 RTC_LOG(LS_ERROR)
danilchap653063f2017-04-03 06:16:30 -0700252 << "Extension cannot be registered: Not enough space left in buffer.";
253 return nullptr;
254 }
255
256 // All checks passed, write down the extension headers.
257 if (extensions_size_ == 0) {
258 RTC_DCHECK_EQ(payload_offset_, kFixedHeaderSize + (num_csrc * 4));
259 WriteAt(0, data()[0] | 0x10); // Set extension bit.
danilchap653063f2017-04-03 06:16:30 -0700260 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
Johannes Kron78cdde32018-10-05 10:00:46 +0200261 profile_id);
danilchap653063f2017-04-03 06:16:30 -0700262 }
263
Johannes Kron78cdde32018-10-05 10:00:46 +0200264 if (profile_id == kOneByteExtensionProfileId) {
265 uint8_t one_byte_header = rtc::dchecked_cast<uint8_t>(id) << 4;
266 one_byte_header |= rtc::dchecked_cast<uint8_t>(length - 1);
267 WriteAt(extensions_offset + extensions_size_, one_byte_header);
268 } else {
269 // TwoByteHeaderExtension.
270 uint8_t extension_id = rtc::dchecked_cast<uint8_t>(id);
271 WriteAt(extensions_offset + extensions_size_, extension_id);
272 uint8_t extension_length = rtc::dchecked_cast<uint8_t>(length);
273 WriteAt(extensions_offset + extensions_size_ + 1, extension_length);
274 }
danilchap653063f2017-04-03 06:16:30 -0700275
Johannes Kronc5744b82018-09-24 14:50:48 +0200276 const uint16_t extension_info_offset = rtc::dchecked_cast<uint16_t>(
Johannes Kron78cdde32018-10-05 10:00:46 +0200277 extensions_offset + extensions_size_ + extension_header_size);
Johannes Kronc5744b82018-09-24 14:50:48 +0200278 const uint8_t extension_info_length = rtc::dchecked_cast<uint8_t>(length);
279 extension_entries_.emplace_back(id, extension_info_length,
280 extension_info_offset);
Johannes Kron78cdde32018-10-05 10:00:46 +0200281
Danil Chapovalov61405bc2018-02-13 13:55:30 +0100282 extensions_size_ = new_extensions_size;
danilchap653063f2017-04-03 06:16:30 -0700283
Johannes Kron78cdde32018-10-05 10:00:46 +0200284 uint16_t extensions_size_padded =
285 SetExtensionLengthMaybeAddZeroPadding(extensions_offset);
286 payload_offset_ = extensions_offset + extensions_size_padded;
287 buffer_.SetSize(payload_offset_);
288 return rtc::MakeArrayView(WriteAt(extension_info_offset),
289 extension_info_length);
290}
291
292void RtpPacket::PromoteToTwoByteHeaderExtension() {
293 size_t num_csrc = data()[0] & 0x0F;
294 size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
295
296 RTC_CHECK_GT(extension_entries_.size(), 0);
297 RTC_CHECK_EQ(payload_size_, 0);
298 RTC_CHECK_EQ(kOneByteExtensionProfileId, ByteReader<uint16_t>::ReadBigEndian(
299 data() + extensions_offset - 4));
300 // Rewrite data.
301 // Each extension adds one to the offset. The write-read delta for the last
302 // extension is therefore the same as the number of extension entries.
303 size_t write_read_delta = extension_entries_.size();
304 for (auto extension_entry = extension_entries_.rbegin();
305 extension_entry != extension_entries_.rend(); ++extension_entry) {
306 size_t read_index = extension_entry->offset;
307 size_t write_index = read_index + write_read_delta;
308 // Update offset.
309 extension_entry->offset = rtc::dchecked_cast<uint16_t>(write_index);
310 // Copy data. Use memmove since read/write regions may overlap.
311 memmove(WriteAt(write_index), data() + read_index, extension_entry->length);
312 // Rewrite id and length.
313 WriteAt(--write_index, extension_entry->length);
314 WriteAt(--write_index, extension_entry->id);
315 --write_read_delta;
316 }
317
318 // Update profile header, extensions length, and zero padding.
319 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
320 kTwoByteExtensionProfileId);
321 extensions_size_ += extension_entries_.size();
322 uint16_t extensions_size_padded =
323 SetExtensionLengthMaybeAddZeroPadding(extensions_offset);
324 payload_offset_ = extensions_offset + extensions_size_padded;
325 buffer_.SetSize(payload_offset_);
326}
327
328uint16_t RtpPacket::SetExtensionLengthMaybeAddZeroPadding(
329 size_t extensions_offset) {
danilchap653063f2017-04-03 06:16:30 -0700330 // Update header length field.
Danil Chapovalov61405bc2018-02-13 13:55:30 +0100331 uint16_t extensions_words = rtc::dchecked_cast<uint16_t>(
332 (extensions_size_ + 3) / 4); // Wrap up to 32bit.
danilchap653063f2017-04-03 06:16:30 -0700333 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 2),
334 extensions_words);
335 // Fill extension padding place with zeroes.
336 size_t extension_padding_size = 4 * extensions_words - extensions_size_;
337 memset(WriteAt(extensions_offset + extensions_size_), 0,
338 extension_padding_size);
Johannes Kron78cdde32018-10-05 10:00:46 +0200339 return 4 * extensions_words;
danilchap653063f2017-04-03 06:16:30 -0700340}
341
Danil Chapovalov8769e172017-09-14 14:08:22 +0200342uint8_t* RtpPacket::AllocatePayload(size_t size_bytes) {
danilchap07a01b32017-03-29 07:33:13 -0700343 // Reset payload size to 0. If CopyOnWrite buffer_ was shared, this will cause
344 // reallocation and memcpy. Keeping just header reduces memcpy size.
345 SetPayloadSize(0);
346 return SetPayloadSize(size_bytes);
347}
348
Danil Chapovalov8769e172017-09-14 14:08:22 +0200349uint8_t* RtpPacket::SetPayloadSize(size_t size_bytes) {
kwibergaf476c72016-11-28 15:21:39 -0800350 RTC_DCHECK_EQ(padding_size_, 0);
danilchap1edb7ab2016-04-20 05:25:10 -0700351 if (payload_offset_ + size_bytes > capacity()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100352 RTC_LOG(LS_WARNING) << "Cannot set payload, not enough space in buffer.";
danilchap1edb7ab2016-04-20 05:25:10 -0700353 return nullptr;
354 }
355 payload_size_ = size_bytes;
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200356 buffer_.SetSize(payload_offset_ + payload_size_);
danilchap1edb7ab2016-04-20 05:25:10 -0700357 return WriteAt(payload_offset_);
358}
359
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200360bool RtpPacket::SetPadding(size_t padding_bytes) {
361 if (payload_offset_ + payload_size_ + padding_bytes > capacity()) {
362 RTC_LOG(LS_WARNING) << "Cannot set padding size " << padding_bytes
363 << ", only "
Mirko Bonadei675513b2017-11-09 11:09:25 +0100364 << (capacity() - payload_offset_ - payload_size_)
365 << " bytes left in buffer.";
danilchap1edb7ab2016-04-20 05:25:10 -0700366 return false;
367 }
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200368 padding_size_ = rtc::dchecked_cast<uint8_t>(padding_bytes);
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200369 buffer_.SetSize(payload_offset_ + payload_size_ + padding_size_);
danilchap1edb7ab2016-04-20 05:25:10 -0700370 if (padding_size_ > 0) {
371 size_t padding_offset = payload_offset_ + payload_size_;
372 size_t padding_end = padding_offset + padding_size_;
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200373 memset(WriteAt(padding_offset), 0, padding_size_ - 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700374 WriteAt(padding_end - 1, padding_size_);
375 WriteAt(0, data()[0] | 0x20); // Set padding bit.
376 } else {
377 WriteAt(0, data()[0] & ~0x20); // Clear padding bit.
378 }
379 return true;
380}
381
Danil Chapovalov8769e172017-09-14 14:08:22 +0200382void RtpPacket::Clear() {
danilchap1edb7ab2016-04-20 05:25:10 -0700383 marker_ = false;
384 payload_type_ = 0;
385 sequence_number_ = 0;
386 timestamp_ = 0;
387 ssrc_ = 0;
388 payload_offset_ = kFixedHeaderSize;
389 payload_size_ = 0;
390 padding_size_ = 0;
danilchap1edb7ab2016-04-20 05:25:10 -0700391 extensions_size_ = 0;
Johannes Kronc5744b82018-09-24 14:50:48 +0200392 extension_entries_.clear();
danilchap1edb7ab2016-04-20 05:25:10 -0700393
394 memset(WriteAt(0), 0, kFixedHeaderSize);
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200395 buffer_.SetSize(kFixedHeaderSize);
danilchap1edb7ab2016-04-20 05:25:10 -0700396 WriteAt(0, kRtpVersion << 6);
397}
398
Danil Chapovalov8769e172017-09-14 14:08:22 +0200399bool RtpPacket::ParseBuffer(const uint8_t* buffer, size_t size) {
danilchap1edb7ab2016-04-20 05:25:10 -0700400 if (size < kFixedHeaderSize) {
401 return false;
402 }
403 const uint8_t version = buffer[0] >> 6;
404 if (version != kRtpVersion) {
405 return false;
406 }
407 const bool has_padding = (buffer[0] & 0x20) != 0;
408 const bool has_extension = (buffer[0] & 0x10) != 0;
409 const uint8_t number_of_crcs = buffer[0] & 0x0f;
410 marker_ = (buffer[1] & 0x80) != 0;
411 payload_type_ = buffer[1] & 0x7f;
412
413 sequence_number_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]);
414 timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
415 ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
416 if (size < kFixedHeaderSize + number_of_crcs * 4) {
417 return false;
418 }
419 payload_offset_ = kFixedHeaderSize + number_of_crcs * 4;
420
421 if (has_padding) {
422 padding_size_ = buffer[size - 1];
423 if (padding_size_ == 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100424 RTC_LOG(LS_WARNING) << "Padding was set, but padding size is zero";
danilchap1edb7ab2016-04-20 05:25:10 -0700425 return false;
426 }
427 } else {
428 padding_size_ = 0;
429 }
430
danilchap1edb7ab2016-04-20 05:25:10 -0700431 extensions_size_ = 0;
Johannes Kron6ea77192018-09-24 17:19:52 +0200432 extension_entries_.clear();
danilchap1edb7ab2016-04-20 05:25:10 -0700433 if (has_extension) {
434 /* RTP header extension, RFC 3550.
435 0 1 2 3
436 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
437 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
438 | defined by profile | length |
439 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 | header extension |
441 | .... |
442 */
443 size_t extension_offset = payload_offset_ + 4;
444 if (extension_offset > size) {
445 return false;
446 }
447 uint16_t profile =
448 ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_]);
449 size_t extensions_capacity =
450 ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_ + 2]);
451 extensions_capacity *= 4;
452 if (extension_offset + extensions_capacity > size) {
453 return false;
454 }
Johannes Kron07ba2b92018-09-26 13:33:35 +0200455 if (profile != kOneByteExtensionProfileId &&
456 profile != kTwoByteExtensionProfileId) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100457 RTC_LOG(LS_WARNING) << "Unsupported rtp extension " << profile;
danilchap1edb7ab2016-04-20 05:25:10 -0700458 } else {
Johannes Kron07ba2b92018-09-26 13:33:35 +0200459 size_t extension_header_length = profile == kOneByteExtensionProfileId
460 ? kOneByteExtensionHeaderLength
461 : kTwoByteExtensionHeaderLength;
462 constexpr uint8_t kPaddingByte = 0;
danilchap1edb7ab2016-04-20 05:25:10 -0700463 constexpr uint8_t kPaddingId = 0;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200464 constexpr uint8_t kOneByteHeaderExtensionReservedId = 15;
465 while (extensions_size_ + extension_header_length < extensions_capacity) {
466 if (buffer[extension_offset + extensions_size_] == kPaddingByte) {
danilchap1edb7ab2016-04-20 05:25:10 -0700467 extensions_size_++;
468 continue;
469 }
Johannes Kron07ba2b92018-09-26 13:33:35 +0200470 int id;
471 uint8_t length;
472 if (profile == kOneByteExtensionProfileId) {
473 id = buffer[extension_offset + extensions_size_] >> 4;
474 length = 1 + (buffer[extension_offset + extensions_size_] & 0xf);
475 if (id == kOneByteHeaderExtensionReservedId ||
476 (id == kPaddingId && length != 1)) {
477 break;
478 }
479 } else {
480 id = buffer[extension_offset + extensions_size_];
481 length = buffer[extension_offset + extensions_size_ + 1];
482 }
483
484 if (extensions_size_ + extension_header_length + length >
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200485 extensions_capacity) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100486 RTC_LOG(LS_WARNING) << "Oversized rtp header extension.";
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200487 break;
danilchap1edb7ab2016-04-20 05:25:10 -0700488 }
danilchap70f39a32016-12-16 05:48:18 -0800489
Johannes Kronc5744b82018-09-24 14:50:48 +0200490 ExtensionInfo& extension_info = FindOrCreateExtensionInfo(id);
491 if (extension_info.length != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100492 RTC_LOG(LS_VERBOSE)
493 << "Duplicate rtp header extension id " << id << ". Overwriting.";
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200494 }
danilchap70f39a32016-12-16 05:48:18 -0800495
Danil Chapovalovc2dd59c2018-02-06 11:29:35 +0100496 size_t offset =
Johannes Kron07ba2b92018-09-26 13:33:35 +0200497 extension_offset + extensions_size_ + extension_header_length;
Danil Chapovalovc2dd59c2018-02-06 11:29:35 +0100498 if (!rtc::IsValueInRangeForNumericType<uint16_t>(offset)) {
499 RTC_DLOG(LS_WARNING) << "Oversized rtp header extension.";
500 break;
501 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200502 extension_info.offset = static_cast<uint16_t>(offset);
503 extension_info.length = length;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200504 extensions_size_ += extension_header_length + length;
danilchap1edb7ab2016-04-20 05:25:10 -0700505 }
506 }
507 payload_offset_ = extension_offset + extensions_capacity;
508 }
509
510 if (payload_offset_ + padding_size_ > size) {
511 return false;
512 }
513 payload_size_ = size - payload_offset_ - padding_size_;
514 return true;
515}
516
Johannes Kronc5744b82018-09-24 14:50:48 +0200517const RtpPacket::ExtensionInfo* RtpPacket::FindExtensionInfo(int id) const {
danilchap70f39a32016-12-16 05:48:18 -0800518 for (const ExtensionInfo& extension : extension_entries_) {
Johannes Kronc5744b82018-09-24 14:50:48 +0200519 if (extension.id == id) {
520 return &extension;
danilchap1edb7ab2016-04-20 05:25:10 -0700521 }
522 }
danilchap978504e2017-04-06 01:03:53 -0700523 return nullptr;
danilchap1edb7ab2016-04-20 05:25:10 -0700524}
525
Johannes Kronc5744b82018-09-24 14:50:48 +0200526RtpPacket::ExtensionInfo& RtpPacket::FindOrCreateExtensionInfo(int id) {
527 for (ExtensionInfo& extension : extension_entries_) {
528 if (extension.id == id) {
529 return extension;
danilchap70f39a32016-12-16 05:48:18 -0800530 }
danilchap1edb7ab2016-04-20 05:25:10 -0700531 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200532 extension_entries_.emplace_back(id);
533 return extension_entries_.back();
534}
535
536rtc::ArrayView<const uint8_t> RtpPacket::FindExtension(
537 ExtensionType type) const {
538 uint8_t id = extensions_.GetId(type);
539 if (id == ExtensionManager::kInvalidId) {
540 // Extension not registered.
541 return nullptr;
542 }
543 ExtensionInfo const* extension_info = FindExtensionInfo(id);
544 if (extension_info == nullptr) {
545 return nullptr;
546 }
547 return rtc::MakeArrayView(data() + extension_info->offset,
548 extension_info->length);
549}
550
551rtc::ArrayView<uint8_t> RtpPacket::AllocateExtension(ExtensionType type,
552 size_t length) {
Johannes Kron78cdde32018-10-05 10:00:46 +0200553 // TODO(webrtc:7990): Add support for empty extensions (length==0).
554 if (length == 0 || length > RtpExtension::kMaxValueSize ||
Johannes Kron9581bc42018-10-23 10:17:39 +0200555 (!extensions_.ExtmapAllowMixed() &&
Johannes Kron78cdde32018-10-05 10:00:46 +0200556 length > RtpExtension::kOneByteHeaderExtensionMaxValueSize)) {
557 return nullptr;
558 }
559
Johannes Kronc5744b82018-09-24 14:50:48 +0200560 uint8_t id = extensions_.GetId(type);
561 if (id == ExtensionManager::kInvalidId) {
562 // Extension not registered.
563 return nullptr;
564 }
Johannes Kron9581bc42018-10-23 10:17:39 +0200565 if (!extensions_.ExtmapAllowMixed() &&
Johannes Kron78cdde32018-10-05 10:00:46 +0200566 id > RtpExtension::kOneByteHeaderExtensionMaxId) {
567 return nullptr;
568 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200569 return AllocateRawExtension(id, length);
danilchap1edb7ab2016-04-20 05:25:10 -0700570}
571
danilchap1edb7ab2016-04-20 05:25:10 -0700572} // namespace webrtc