blob: b07341d2f8a31ebd0ab7db2a1d15c417fcb9daef [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "modules/rtp_rtcp/source/byte_io.h"
18#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010020#include "rtc_base/numerics/safe_conversions.h"
danilchap1edb7ab2016-04-20 05:25:10 -070021
22namespace webrtc {
danilchap1edb7ab2016-04-20 05:25:10 -070023namespace {
24constexpr size_t kFixedHeaderSize = 12;
25constexpr uint8_t kRtpVersion = 2;
Johannes Kron07ba2b92018-09-26 13:33:35 +020026constexpr uint16_t kOneByteExtensionProfileId = 0xBEDE;
27constexpr uint16_t kTwoByteExtensionProfileId = 0x1000;
28constexpr size_t kOneByteExtensionHeaderLength = 1;
29constexpr size_t kTwoByteExtensionHeaderLength = 2;
danilchap1edb7ab2016-04-20 05:25:10 -070030constexpr size_t kDefaultPacketSize = 1500;
31} // namespace
danilchap653063f2017-04-03 06:16:30 -070032
danilchap1edb7ab2016-04-20 05:25:10 -070033// 0 1 2 3
34// 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
35// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36// |V=2|P|X| CC |M| PT | sequence number |
37// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38// | timestamp |
39// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40// | synchronization source (SSRC) identifier |
41// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
42// | Contributing source (CSRC) identifiers |
43// | .... |
44// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Johannes Kron07ba2b92018-09-26 13:33:35 +020045// | header eXtension profile id | length in 32bits |
danilchap1edb7ab2016-04-20 05:25:10 -070046// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47// | Extensions |
48// | .... |
49// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
50// | Payload |
51// | .... : padding... |
52// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53// | padding | Padding size |
54// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Danil Chapovalov8769e172017-09-14 14:08:22 +020055RtpPacket::RtpPacket() : RtpPacket(nullptr, kDefaultPacketSize) {}
danilchap70f39a32016-12-16 05:48:18 -080056
Danil Chapovalov8769e172017-09-14 14:08:22 +020057RtpPacket::RtpPacket(const ExtensionManager* extensions)
58 : RtpPacket(extensions, kDefaultPacketSize) {}
danilchap1edb7ab2016-04-20 05:25:10 -070059
Danil Chapovalov8769e172017-09-14 14:08:22 +020060RtpPacket::RtpPacket(const RtpPacket&) = default;
nisse76e62b02017-05-31 02:24:52 -070061
Danil Chapovalov8769e172017-09-14 14:08:22 +020062RtpPacket::RtpPacket(const ExtensionManager* extensions, size_t capacity)
Johannes Kron3dd473b2018-10-29 14:42:36 +010063 : extensions_(extensions ? *extensions : ExtensionManager()),
64 buffer_(capacity) {
danilchap1edb7ab2016-04-20 05:25:10 -070065 RTC_DCHECK_GE(capacity, kFixedHeaderSize);
66 Clear();
67}
68
Danil Chapovalov8769e172017-09-14 14:08:22 +020069RtpPacket::~RtpPacket() {}
danilchap1edb7ab2016-04-20 05:25:10 -070070
Danil Chapovalov8769e172017-09-14 14:08:22 +020071void RtpPacket::IdentifyExtensions(const ExtensionManager& extensions) {
Johannes Kronc5744b82018-09-24 14:50:48 +020072 extensions_ = extensions;
danilchap1edb7ab2016-04-20 05:25:10 -070073}
74
Danil Chapovalov8769e172017-09-14 14:08:22 +020075bool RtpPacket::Parse(const uint8_t* buffer, size_t buffer_size) {
danilchap1edb7ab2016-04-20 05:25:10 -070076 if (!ParseBuffer(buffer, buffer_size)) {
77 Clear();
78 return false;
79 }
danilchap1edb7ab2016-04-20 05:25:10 -070080 buffer_.SetData(buffer, buffer_size);
Danil Chapovalov31e4e802016-08-03 18:27:40 +020081 RTC_DCHECK_EQ(size(), buffer_size);
danilchap1edb7ab2016-04-20 05:25:10 -070082 return true;
83}
84
Danil Chapovalov8769e172017-09-14 14:08:22 +020085bool RtpPacket::Parse(rtc::ArrayView<const uint8_t> packet) {
brandtrb29e6522016-12-21 06:37:18 -080086 return Parse(packet.data(), packet.size());
87}
88
Danil Chapovalov8769e172017-09-14 14:08:22 +020089bool RtpPacket::Parse(rtc::CopyOnWriteBuffer buffer) {
Danil Chapovalov31e4e802016-08-03 18:27:40 +020090 if (!ParseBuffer(buffer.cdata(), buffer.size())) {
danilchap1edb7ab2016-04-20 05:25:10 -070091 Clear();
92 return false;
93 }
Danil Chapovalov31e4e802016-08-03 18:27:40 +020094 size_t buffer_size = buffer.size();
danilchap1edb7ab2016-04-20 05:25:10 -070095 buffer_ = std::move(buffer);
Danil Chapovalov31e4e802016-08-03 18:27:40 +020096 RTC_DCHECK_EQ(size(), buffer_size);
danilchap1edb7ab2016-04-20 05:25:10 -070097 return true;
98}
99
Danil Chapovalov8769e172017-09-14 14:08:22 +0200100std::vector<uint32_t> RtpPacket::Csrcs() const {
danilchap1edb7ab2016-04-20 05:25:10 -0700101 size_t num_csrc = data()[0] & 0x0F;
102 RTC_DCHECK_GE(capacity(), kFixedHeaderSize + num_csrc * 4);
103 std::vector<uint32_t> csrcs(num_csrc);
104 for (size_t i = 0; i < num_csrc; ++i) {
105 csrcs[i] =
106 ByteReader<uint32_t>::ReadBigEndian(&data()[kFixedHeaderSize + i * 4]);
107 }
108 return csrcs;
109}
110
Danil Chapovalov8769e172017-09-14 14:08:22 +0200111void RtpPacket::CopyHeaderFrom(const RtpPacket& packet) {
danilchap1edb7ab2016-04-20 05:25:10 -0700112 RTC_DCHECK_GE(capacity(), packet.headers_size());
113
114 marker_ = packet.marker_;
115 payload_type_ = packet.payload_type_;
116 sequence_number_ = packet.sequence_number_;
117 timestamp_ = packet.timestamp_;
118 ssrc_ = packet.ssrc_;
119 payload_offset_ = packet.payload_offset_;
Johannes Kronc5744b82018-09-24 14:50:48 +0200120 extensions_ = packet.extensions_;
121 extension_entries_ = packet.extension_entries_;
danilchap1edb7ab2016-04-20 05:25:10 -0700122 extensions_size_ = packet.extensions_size_;
123 buffer_.SetData(packet.data(), packet.headers_size());
124 // Reset payload and padding.
125 payload_size_ = 0;
126 padding_size_ = 0;
127}
128
Danil Chapovalov8769e172017-09-14 14:08:22 +0200129void RtpPacket::SetMarker(bool marker_bit) {
danilchap1edb7ab2016-04-20 05:25:10 -0700130 marker_ = marker_bit;
131 if (marker_) {
132 WriteAt(1, data()[1] | 0x80);
133 } else {
134 WriteAt(1, data()[1] & 0x7F);
135 }
136}
137
Danil Chapovalov8769e172017-09-14 14:08:22 +0200138void RtpPacket::SetPayloadType(uint8_t payload_type) {
danilchap1edb7ab2016-04-20 05:25:10 -0700139 RTC_DCHECK_LE(payload_type, 0x7Fu);
140 payload_type_ = payload_type;
141 WriteAt(1, (data()[1] & 0x80) | payload_type);
142}
143
Danil Chapovalov8769e172017-09-14 14:08:22 +0200144void RtpPacket::SetSequenceNumber(uint16_t seq_no) {
danilchap1edb7ab2016-04-20 05:25:10 -0700145 sequence_number_ = seq_no;
146 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(2), seq_no);
147}
148
Danil Chapovalov8769e172017-09-14 14:08:22 +0200149void RtpPacket::SetTimestamp(uint32_t timestamp) {
danilchap1edb7ab2016-04-20 05:25:10 -0700150 timestamp_ = timestamp;
151 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(4), timestamp);
152}
153
Danil Chapovalov8769e172017-09-14 14:08:22 +0200154void RtpPacket::SetSsrc(uint32_t ssrc) {
danilchap1edb7ab2016-04-20 05:25:10 -0700155 ssrc_ = ssrc;
156 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(8), ssrc);
157}
158
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +0200159void RtpPacket::CopyAndZeroMutableExtensions(
160 rtc::ArrayView<uint8_t> buffer) const {
161 RTC_CHECK_GE(buffer.size(), buffer_.size());
162 memcpy(buffer.data(), buffer_.cdata(), buffer_.size());
163 for (const ExtensionInfo& extension : extension_entries_) {
164 switch (extensions_.GetType(extension.id)) {
165 case RTPExtensionType::kRtpExtensionNone: {
166 RTC_LOG(LS_WARNING) << "Unidentified extension in the packet.";
167 break;
168 }
169 case RTPExtensionType::kRtpExtensionVideoTiming: {
170 // Nullify 3 last entries: packetization delay and 2 network timestamps.
171 // Each of them is 2 bytes.
172 memset(buffer.data() + extension.offset +
173 VideoSendTiming::kPacerExitDeltaOffset,
174 0, 6);
175 break;
176 }
177 case RTPExtensionType::kRtpExtensionTransportSequenceNumber:
178 case RTPExtensionType::kRtpExtensionTransportSequenceNumber02:
179 case RTPExtensionType::kRtpExtensionTransmissionTimeOffset:
180 case RTPExtensionType::kRtpExtensionAbsoluteSendTime: {
181 // Nullify whole extension, as it's filled in the pacer.
182 memset(buffer.data() + extension.offset, 0, extension.length);
183 break;
184 }
185 case RTPExtensionType::kRtpExtensionAudioLevel:
Chen Xingcd8a6e22019-07-01 10:56:51 +0200186 case RTPExtensionType::kRtpExtensionAbsoluteCaptureTime:
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +0200187 case RTPExtensionType::kRtpExtensionColorSpace:
188 case RTPExtensionType::kRtpExtensionFrameMarking:
189 case RTPExtensionType::kRtpExtensionGenericFrameDescriptor00:
190 case RTPExtensionType::kRtpExtensionGenericFrameDescriptor01:
Danil Chapovalov52e52422019-06-27 16:45:40 +0200191 case RTPExtensionType::kRtpExtensionGenericFrameDescriptor02:
Ilya Nikolaevskiy2d821c32019-06-26 14:39:36 +0200192 case RTPExtensionType::kRtpExtensionMid:
193 case RTPExtensionType::kRtpExtensionNumberOfExtensions:
194 case RTPExtensionType::kRtpExtensionPlayoutDelay:
195 case RTPExtensionType::kRtpExtensionRepairedRtpStreamId:
196 case RTPExtensionType::kRtpExtensionRtpStreamId:
197 case RTPExtensionType::kRtpExtensionVideoContentType:
198 case RTPExtensionType::kRtpExtensionVideoRotation: {
199 // Non-mutable extension. Don't change it.
200 break;
201 }
202 }
203 }
204}
205
Danil Chapovalov7d2df3f2018-08-14 17:18:07 +0200206void RtpPacket::SetCsrcs(rtc::ArrayView<const uint32_t> csrcs) {
danilchap70f39a32016-12-16 05:48:18 -0800207 RTC_DCHECK_EQ(extensions_size_, 0);
kwibergaf476c72016-11-28 15:21:39 -0800208 RTC_DCHECK_EQ(payload_size_, 0);
209 RTC_DCHECK_EQ(padding_size_, 0);
danilchap1edb7ab2016-04-20 05:25:10 -0700210 RTC_DCHECK_LE(csrcs.size(), 0x0fu);
211 RTC_DCHECK_LE(kFixedHeaderSize + 4 * csrcs.size(), capacity());
212 payload_offset_ = kFixedHeaderSize + 4 * csrcs.size();
danilchap772bd8b2017-09-13 03:24:28 -0700213 WriteAt(0, (data()[0] & 0xF0) | rtc::dchecked_cast<uint8_t>(csrcs.size()));
danilchap1edb7ab2016-04-20 05:25:10 -0700214 size_t offset = kFixedHeaderSize;
215 for (uint32_t csrc : csrcs) {
216 ByteWriter<uint32_t>::WriteBigEndian(WriteAt(offset), csrc);
217 offset += 4;
218 }
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200219 buffer_.SetSize(payload_offset_);
danilchap1edb7ab2016-04-20 05:25:10 -0700220}
221
Danil Chapovalov8769e172017-09-14 14:08:22 +0200222rtc::ArrayView<uint8_t> RtpPacket::AllocateRawExtension(int id, size_t length) {
Johannes Kron07ba2b92018-09-26 13:33:35 +0200223 RTC_DCHECK_GE(id, RtpExtension::kMinId);
Johannes Kron78cdde32018-10-05 10:00:46 +0200224 RTC_DCHECK_LE(id, RtpExtension::kMaxId);
danilchap653063f2017-04-03 06:16:30 -0700225 RTC_DCHECK_GE(length, 1);
Johannes Kron78cdde32018-10-05 10:00:46 +0200226 RTC_DCHECK_LE(length, RtpExtension::kMaxValueSize);
Johannes Kronc5744b82018-09-24 14:50:48 +0200227 const ExtensionInfo* extension_entry = FindExtensionInfo(id);
228 if (extension_entry != nullptr) {
danilchap653063f2017-04-03 06:16:30 -0700229 // Extension already reserved. Check if same length is used.
230 if (extension_entry->length == length)
231 return rtc::MakeArrayView(WriteAt(extension_entry->offset), length);
232
Johannes Kronc5744b82018-09-24 14:50:48 +0200233 RTC_LOG(LS_ERROR) << "Length mismatch for extension id " << id
Mirko Bonadei675513b2017-11-09 11:09:25 +0100234 << ": expected "
235 << static_cast<int>(extension_entry->length)
236 << ". received " << length;
danilchap653063f2017-04-03 06:16:30 -0700237 return nullptr;
238 }
239 if (payload_size_ > 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100240 RTC_LOG(LS_ERROR) << "Can't add new extension id " << id
241 << " after payload was set.";
danilchap653063f2017-04-03 06:16:30 -0700242 return nullptr;
243 }
244 if (padding_size_ > 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100245 RTC_LOG(LS_ERROR) << "Can't add new extension id " << id
246 << " after padding was set.";
danilchap653063f2017-04-03 06:16:30 -0700247 return nullptr;
248 }
249
Johannes Kron78cdde32018-10-05 10:00:46 +0200250 const size_t num_csrc = data()[0] & 0x0F;
251 const size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
252 // Determine if two-byte header is required for the extension based on id and
253 // length. Please note that a length of 0 also requires two-byte header
254 // extension. See RFC8285 Section 4.2-4.3.
255 const bool two_byte_header_required =
256 id > RtpExtension::kOneByteHeaderExtensionMaxId ||
257 length > RtpExtension::kOneByteHeaderExtensionMaxValueSize || length == 0;
Johannes Kron9581bc42018-10-23 10:17:39 +0200258 RTC_CHECK(!two_byte_header_required || extensions_.ExtmapAllowMixed());
Johannes Kron78cdde32018-10-05 10:00:46 +0200259
260 uint16_t profile_id;
261 if (extensions_size_ > 0) {
262 profile_id =
263 ByteReader<uint16_t>::ReadBigEndian(data() + extensions_offset - 4);
264 if (profile_id == kOneByteExtensionProfileId && two_byte_header_required) {
265 // Is buffer size big enough to fit promotion and new data field?
266 // The header extension will grow with one byte per already allocated
267 // extension + the size of the extension that is about to be allocated.
268 size_t expected_new_extensions_size =
269 extensions_size_ + extension_entries_.size() +
270 kTwoByteExtensionHeaderLength + length;
271 if (extensions_offset + expected_new_extensions_size > capacity()) {
272 RTC_LOG(LS_ERROR)
273 << "Extension cannot be registered: Not enough space left in "
274 "buffer to change to two-byte header extension and add new "
275 "extension.";
276 return nullptr;
277 }
278 // Promote already written data to two-byte header format.
279 PromoteToTwoByteHeaderExtension();
280 profile_id = kTwoByteExtensionProfileId;
281 }
282 } else {
283 // Profile specific ID, set to OneByteExtensionHeader unless
284 // TwoByteExtensionHeader is required.
285 profile_id = two_byte_header_required ? kTwoByteExtensionProfileId
286 : kOneByteExtensionProfileId;
287 }
288
289 const size_t extension_header_size = profile_id == kOneByteExtensionProfileId
290 ? kOneByteExtensionHeaderLength
291 : kTwoByteExtensionHeaderLength;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200292 size_t new_extensions_size =
Johannes Kron78cdde32018-10-05 10:00:46 +0200293 extensions_size_ + extension_header_size + length;
danilchap653063f2017-04-03 06:16:30 -0700294 if (extensions_offset + new_extensions_size > capacity()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100295 RTC_LOG(LS_ERROR)
danilchap653063f2017-04-03 06:16:30 -0700296 << "Extension cannot be registered: Not enough space left in buffer.";
297 return nullptr;
298 }
299
300 // All checks passed, write down the extension headers.
301 if (extensions_size_ == 0) {
302 RTC_DCHECK_EQ(payload_offset_, kFixedHeaderSize + (num_csrc * 4));
303 WriteAt(0, data()[0] | 0x10); // Set extension bit.
danilchap653063f2017-04-03 06:16:30 -0700304 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
Johannes Kron78cdde32018-10-05 10:00:46 +0200305 profile_id);
danilchap653063f2017-04-03 06:16:30 -0700306 }
307
Johannes Kron78cdde32018-10-05 10:00:46 +0200308 if (profile_id == kOneByteExtensionProfileId) {
309 uint8_t one_byte_header = rtc::dchecked_cast<uint8_t>(id) << 4;
310 one_byte_header |= rtc::dchecked_cast<uint8_t>(length - 1);
311 WriteAt(extensions_offset + extensions_size_, one_byte_header);
312 } else {
313 // TwoByteHeaderExtension.
314 uint8_t extension_id = rtc::dchecked_cast<uint8_t>(id);
315 WriteAt(extensions_offset + extensions_size_, extension_id);
316 uint8_t extension_length = rtc::dchecked_cast<uint8_t>(length);
317 WriteAt(extensions_offset + extensions_size_ + 1, extension_length);
318 }
danilchap653063f2017-04-03 06:16:30 -0700319
Johannes Kronc5744b82018-09-24 14:50:48 +0200320 const uint16_t extension_info_offset = rtc::dchecked_cast<uint16_t>(
Johannes Kron78cdde32018-10-05 10:00:46 +0200321 extensions_offset + extensions_size_ + extension_header_size);
Johannes Kronc5744b82018-09-24 14:50:48 +0200322 const uint8_t extension_info_length = rtc::dchecked_cast<uint8_t>(length);
323 extension_entries_.emplace_back(id, extension_info_length,
324 extension_info_offset);
Johannes Kron78cdde32018-10-05 10:00:46 +0200325
Danil Chapovalov61405bc2018-02-13 13:55:30 +0100326 extensions_size_ = new_extensions_size;
danilchap653063f2017-04-03 06:16:30 -0700327
Johannes Kron78cdde32018-10-05 10:00:46 +0200328 uint16_t extensions_size_padded =
329 SetExtensionLengthMaybeAddZeroPadding(extensions_offset);
330 payload_offset_ = extensions_offset + extensions_size_padded;
331 buffer_.SetSize(payload_offset_);
332 return rtc::MakeArrayView(WriteAt(extension_info_offset),
333 extension_info_length);
334}
335
336void RtpPacket::PromoteToTwoByteHeaderExtension() {
337 size_t num_csrc = data()[0] & 0x0F;
338 size_t extensions_offset = kFixedHeaderSize + (num_csrc * 4) + 4;
339
340 RTC_CHECK_GT(extension_entries_.size(), 0);
341 RTC_CHECK_EQ(payload_size_, 0);
342 RTC_CHECK_EQ(kOneByteExtensionProfileId, ByteReader<uint16_t>::ReadBigEndian(
343 data() + extensions_offset - 4));
344 // Rewrite data.
345 // Each extension adds one to the offset. The write-read delta for the last
346 // extension is therefore the same as the number of extension entries.
347 size_t write_read_delta = extension_entries_.size();
348 for (auto extension_entry = extension_entries_.rbegin();
349 extension_entry != extension_entries_.rend(); ++extension_entry) {
350 size_t read_index = extension_entry->offset;
351 size_t write_index = read_index + write_read_delta;
352 // Update offset.
353 extension_entry->offset = rtc::dchecked_cast<uint16_t>(write_index);
354 // Copy data. Use memmove since read/write regions may overlap.
355 memmove(WriteAt(write_index), data() + read_index, extension_entry->length);
356 // Rewrite id and length.
357 WriteAt(--write_index, extension_entry->length);
358 WriteAt(--write_index, extension_entry->id);
359 --write_read_delta;
360 }
361
362 // Update profile header, extensions length, and zero padding.
363 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 4),
364 kTwoByteExtensionProfileId);
365 extensions_size_ += extension_entries_.size();
366 uint16_t extensions_size_padded =
367 SetExtensionLengthMaybeAddZeroPadding(extensions_offset);
368 payload_offset_ = extensions_offset + extensions_size_padded;
369 buffer_.SetSize(payload_offset_);
370}
371
372uint16_t RtpPacket::SetExtensionLengthMaybeAddZeroPadding(
373 size_t extensions_offset) {
danilchap653063f2017-04-03 06:16:30 -0700374 // Update header length field.
Danil Chapovalov61405bc2018-02-13 13:55:30 +0100375 uint16_t extensions_words = rtc::dchecked_cast<uint16_t>(
376 (extensions_size_ + 3) / 4); // Wrap up to 32bit.
danilchap653063f2017-04-03 06:16:30 -0700377 ByteWriter<uint16_t>::WriteBigEndian(WriteAt(extensions_offset - 2),
378 extensions_words);
379 // Fill extension padding place with zeroes.
380 size_t extension_padding_size = 4 * extensions_words - extensions_size_;
381 memset(WriteAt(extensions_offset + extensions_size_), 0,
382 extension_padding_size);
Johannes Kron78cdde32018-10-05 10:00:46 +0200383 return 4 * extensions_words;
danilchap653063f2017-04-03 06:16:30 -0700384}
385
Danil Chapovalov8769e172017-09-14 14:08:22 +0200386uint8_t* RtpPacket::AllocatePayload(size_t size_bytes) {
danilchap07a01b32017-03-29 07:33:13 -0700387 // Reset payload size to 0. If CopyOnWrite buffer_ was shared, this will cause
388 // reallocation and memcpy. Keeping just header reduces memcpy size.
389 SetPayloadSize(0);
390 return SetPayloadSize(size_bytes);
391}
392
Danil Chapovalov8769e172017-09-14 14:08:22 +0200393uint8_t* RtpPacket::SetPayloadSize(size_t size_bytes) {
kwibergaf476c72016-11-28 15:21:39 -0800394 RTC_DCHECK_EQ(padding_size_, 0);
danilchap1edb7ab2016-04-20 05:25:10 -0700395 if (payload_offset_ + size_bytes > capacity()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100396 RTC_LOG(LS_WARNING) << "Cannot set payload, not enough space in buffer.";
danilchap1edb7ab2016-04-20 05:25:10 -0700397 return nullptr;
398 }
399 payload_size_ = size_bytes;
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200400 buffer_.SetSize(payload_offset_ + payload_size_);
danilchap1edb7ab2016-04-20 05:25:10 -0700401 return WriteAt(payload_offset_);
402}
403
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200404bool RtpPacket::SetPadding(size_t padding_bytes) {
405 if (payload_offset_ + payload_size_ + padding_bytes > capacity()) {
406 RTC_LOG(LS_WARNING) << "Cannot set padding size " << padding_bytes
407 << ", only "
Mirko Bonadei675513b2017-11-09 11:09:25 +0100408 << (capacity() - payload_offset_ - payload_size_)
409 << " bytes left in buffer.";
danilchap1edb7ab2016-04-20 05:25:10 -0700410 return false;
411 }
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200412 padding_size_ = rtc::dchecked_cast<uint8_t>(padding_bytes);
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200413 buffer_.SetSize(payload_offset_ + payload_size_ + padding_size_);
danilchap1edb7ab2016-04-20 05:25:10 -0700414 if (padding_size_ > 0) {
415 size_t padding_offset = payload_offset_ + payload_size_;
416 size_t padding_end = padding_offset + padding_size_;
Danil Chapovalovf7fcaf02018-10-10 14:56:01 +0200417 memset(WriteAt(padding_offset), 0, padding_size_ - 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700418 WriteAt(padding_end - 1, padding_size_);
419 WriteAt(0, data()[0] | 0x20); // Set padding bit.
420 } else {
421 WriteAt(0, data()[0] & ~0x20); // Clear padding bit.
422 }
423 return true;
424}
425
Danil Chapovalov8769e172017-09-14 14:08:22 +0200426void RtpPacket::Clear() {
danilchap1edb7ab2016-04-20 05:25:10 -0700427 marker_ = false;
428 payload_type_ = 0;
429 sequence_number_ = 0;
430 timestamp_ = 0;
431 ssrc_ = 0;
432 payload_offset_ = kFixedHeaderSize;
433 payload_size_ = 0;
434 padding_size_ = 0;
danilchap1edb7ab2016-04-20 05:25:10 -0700435 extensions_size_ = 0;
Johannes Kronc5744b82018-09-24 14:50:48 +0200436 extension_entries_.clear();
danilchap1edb7ab2016-04-20 05:25:10 -0700437
438 memset(WriteAt(0), 0, kFixedHeaderSize);
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200439 buffer_.SetSize(kFixedHeaderSize);
danilchap1edb7ab2016-04-20 05:25:10 -0700440 WriteAt(0, kRtpVersion << 6);
441}
442
Danil Chapovalov8769e172017-09-14 14:08:22 +0200443bool RtpPacket::ParseBuffer(const uint8_t* buffer, size_t size) {
danilchap1edb7ab2016-04-20 05:25:10 -0700444 if (size < kFixedHeaderSize) {
445 return false;
446 }
447 const uint8_t version = buffer[0] >> 6;
448 if (version != kRtpVersion) {
449 return false;
450 }
451 const bool has_padding = (buffer[0] & 0x20) != 0;
452 const bool has_extension = (buffer[0] & 0x10) != 0;
453 const uint8_t number_of_crcs = buffer[0] & 0x0f;
454 marker_ = (buffer[1] & 0x80) != 0;
455 payload_type_ = buffer[1] & 0x7f;
456
457 sequence_number_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]);
458 timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
459 ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
460 if (size < kFixedHeaderSize + number_of_crcs * 4) {
461 return false;
462 }
463 payload_offset_ = kFixedHeaderSize + number_of_crcs * 4;
464
465 if (has_padding) {
466 padding_size_ = buffer[size - 1];
467 if (padding_size_ == 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100468 RTC_LOG(LS_WARNING) << "Padding was set, but padding size is zero";
danilchap1edb7ab2016-04-20 05:25:10 -0700469 return false;
470 }
471 } else {
472 padding_size_ = 0;
473 }
474
danilchap1edb7ab2016-04-20 05:25:10 -0700475 extensions_size_ = 0;
Johannes Kron6ea77192018-09-24 17:19:52 +0200476 extension_entries_.clear();
danilchap1edb7ab2016-04-20 05:25:10 -0700477 if (has_extension) {
478 /* RTP header extension, RFC 3550.
479 0 1 2 3
480 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
481 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
482 | defined by profile | length |
483 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
484 | header extension |
485 | .... |
486 */
487 size_t extension_offset = payload_offset_ + 4;
488 if (extension_offset > size) {
489 return false;
490 }
491 uint16_t profile =
492 ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_]);
493 size_t extensions_capacity =
494 ByteReader<uint16_t>::ReadBigEndian(&buffer[payload_offset_ + 2]);
495 extensions_capacity *= 4;
496 if (extension_offset + extensions_capacity > size) {
497 return false;
498 }
Johannes Kron07ba2b92018-09-26 13:33:35 +0200499 if (profile != kOneByteExtensionProfileId &&
500 profile != kTwoByteExtensionProfileId) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100501 RTC_LOG(LS_WARNING) << "Unsupported rtp extension " << profile;
danilchap1edb7ab2016-04-20 05:25:10 -0700502 } else {
Johannes Kron07ba2b92018-09-26 13:33:35 +0200503 size_t extension_header_length = profile == kOneByteExtensionProfileId
504 ? kOneByteExtensionHeaderLength
505 : kTwoByteExtensionHeaderLength;
506 constexpr uint8_t kPaddingByte = 0;
danilchap1edb7ab2016-04-20 05:25:10 -0700507 constexpr uint8_t kPaddingId = 0;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200508 constexpr uint8_t kOneByteHeaderExtensionReservedId = 15;
509 while (extensions_size_ + extension_header_length < extensions_capacity) {
510 if (buffer[extension_offset + extensions_size_] == kPaddingByte) {
danilchap1edb7ab2016-04-20 05:25:10 -0700511 extensions_size_++;
512 continue;
513 }
Johannes Kron07ba2b92018-09-26 13:33:35 +0200514 int id;
515 uint8_t length;
516 if (profile == kOneByteExtensionProfileId) {
517 id = buffer[extension_offset + extensions_size_] >> 4;
518 length = 1 + (buffer[extension_offset + extensions_size_] & 0xf);
519 if (id == kOneByteHeaderExtensionReservedId ||
520 (id == kPaddingId && length != 1)) {
521 break;
522 }
523 } else {
524 id = buffer[extension_offset + extensions_size_];
525 length = buffer[extension_offset + extensions_size_ + 1];
526 }
527
528 if (extensions_size_ + extension_header_length + length >
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200529 extensions_capacity) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100530 RTC_LOG(LS_WARNING) << "Oversized rtp header extension.";
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200531 break;
danilchap1edb7ab2016-04-20 05:25:10 -0700532 }
danilchap70f39a32016-12-16 05:48:18 -0800533
Johannes Kronc5744b82018-09-24 14:50:48 +0200534 ExtensionInfo& extension_info = FindOrCreateExtensionInfo(id);
535 if (extension_info.length != 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100536 RTC_LOG(LS_VERBOSE)
537 << "Duplicate rtp header extension id " << id << ". Overwriting.";
Danil Chapovalova64a2fb2016-09-12 11:41:35 +0200538 }
danilchap70f39a32016-12-16 05:48:18 -0800539
Danil Chapovalovc2dd59c2018-02-06 11:29:35 +0100540 size_t offset =
Johannes Kron07ba2b92018-09-26 13:33:35 +0200541 extension_offset + extensions_size_ + extension_header_length;
Danil Chapovalovc2dd59c2018-02-06 11:29:35 +0100542 if (!rtc::IsValueInRangeForNumericType<uint16_t>(offset)) {
543 RTC_DLOG(LS_WARNING) << "Oversized rtp header extension.";
544 break;
545 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200546 extension_info.offset = static_cast<uint16_t>(offset);
547 extension_info.length = length;
Johannes Kron07ba2b92018-09-26 13:33:35 +0200548 extensions_size_ += extension_header_length + length;
danilchap1edb7ab2016-04-20 05:25:10 -0700549 }
550 }
551 payload_offset_ = extension_offset + extensions_capacity;
552 }
553
554 if (payload_offset_ + padding_size_ > size) {
555 return false;
556 }
557 payload_size_ = size - payload_offset_ - padding_size_;
558 return true;
559}
560
Johannes Kronc5744b82018-09-24 14:50:48 +0200561const RtpPacket::ExtensionInfo* RtpPacket::FindExtensionInfo(int id) const {
danilchap70f39a32016-12-16 05:48:18 -0800562 for (const ExtensionInfo& extension : extension_entries_) {
Johannes Kronc5744b82018-09-24 14:50:48 +0200563 if (extension.id == id) {
564 return &extension;
danilchap1edb7ab2016-04-20 05:25:10 -0700565 }
566 }
danilchap978504e2017-04-06 01:03:53 -0700567 return nullptr;
danilchap1edb7ab2016-04-20 05:25:10 -0700568}
569
Johannes Kronc5744b82018-09-24 14:50:48 +0200570RtpPacket::ExtensionInfo& RtpPacket::FindOrCreateExtensionInfo(int id) {
571 for (ExtensionInfo& extension : extension_entries_) {
572 if (extension.id == id) {
573 return extension;
danilchap70f39a32016-12-16 05:48:18 -0800574 }
danilchap1edb7ab2016-04-20 05:25:10 -0700575 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200576 extension_entries_.emplace_back(id);
577 return extension_entries_.back();
578}
579
580rtc::ArrayView<const uint8_t> RtpPacket::FindExtension(
581 ExtensionType type) const {
582 uint8_t id = extensions_.GetId(type);
583 if (id == ExtensionManager::kInvalidId) {
584 // Extension not registered.
585 return nullptr;
586 }
587 ExtensionInfo const* extension_info = FindExtensionInfo(id);
588 if (extension_info == nullptr) {
589 return nullptr;
590 }
591 return rtc::MakeArrayView(data() + extension_info->offset,
592 extension_info->length);
593}
594
595rtc::ArrayView<uint8_t> RtpPacket::AllocateExtension(ExtensionType type,
596 size_t length) {
Johannes Kron78cdde32018-10-05 10:00:46 +0200597 // TODO(webrtc:7990): Add support for empty extensions (length==0).
598 if (length == 0 || length > RtpExtension::kMaxValueSize ||
Johannes Kron9581bc42018-10-23 10:17:39 +0200599 (!extensions_.ExtmapAllowMixed() &&
Johannes Kron78cdde32018-10-05 10:00:46 +0200600 length > RtpExtension::kOneByteHeaderExtensionMaxValueSize)) {
601 return nullptr;
602 }
603
Johannes Kronc5744b82018-09-24 14:50:48 +0200604 uint8_t id = extensions_.GetId(type);
605 if (id == ExtensionManager::kInvalidId) {
606 // Extension not registered.
607 return nullptr;
608 }
Johannes Kron9581bc42018-10-23 10:17:39 +0200609 if (!extensions_.ExtmapAllowMixed() &&
Johannes Kron78cdde32018-10-05 10:00:46 +0200610 id > RtpExtension::kOneByteHeaderExtensionMaxId) {
611 return nullptr;
612 }
Johannes Kronc5744b82018-09-24 14:50:48 +0200613 return AllocateRawExtension(id, length);
danilchap1edb7ab2016-04-20 05:25:10 -0700614}
615
Amit Hilbuch77938e62018-12-21 09:23:38 -0800616bool RtpPacket::HasExtension(ExtensionType type) const {
617 // TODO(webrtc:7990): Add support for empty extensions (length==0).
618 return !FindExtension(type).empty();
619}
620
Erik Språng1d46f9c2019-07-02 21:24:47 +0200621bool RtpPacket::IsExtensionReserved(ExtensionType type) const {
622 uint8_t id = extensions_.GetId(type);
623 if (id == ExtensionManager::kInvalidId) {
624 // Extension not registered.
625 return false;
626 }
627 return FindExtensionInfo(id) != nullptr;
628}
629
danilchap1edb7ab2016-04-20 05:25:10 -0700630} // namespace webrtc