blob: e531a886c68e2dfb24940329b954050819e466d1 [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_header_extensions.h"
danilchap1edb7ab2016-04-20 05:25:10 -070012
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <string.h>
Johannes Kronad1d9f02018-11-09 11:12:36 +010014#include <cmath>
Johannes Kronc13f4be2018-12-12 09:52:53 +010015#include <limits>
Yves Gerey988cc082018-10-23 12:03:01 +020016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "modules/rtp_rtcp/include/rtp_cvo.h"
18#include "modules/rtp_rtcp/source/byte_io.h"
Yves Gerey988cc082018-10-23 12:03:01 +020019// TODO(bug:9855) Move kNoSpatialIdx from vp9_globals.h to common_constants
20#include "modules/video_coding/codecs/interface/common_constants.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/checks.h"
danilchap1edb7ab2016-04-20 05:25:10 -070022
23namespace webrtc {
24// Absolute send time in RTP streams.
25//
26// The absolute send time is signaled to the receiver in-band using the
Johannes Kron07ba2b92018-09-26 13:33:35 +020027// general mechanism for RTP header extensions [RFC8285]. The payload
danilchap1edb7ab2016-04-20 05:25:10 -070028// of this extension (the transmitted value) is a 24-bit unsigned integer
29// containing the sender's current time in seconds as a fixed point number
30// with 18 bits fractional part.
31//
32// The form of the absolute send time extension block:
33//
34// 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// | ID | len=2 | absolute send time |
38// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
danilchape2a01772016-10-28 07:08:58 -070039constexpr RTPExtensionType AbsoluteSendTime::kId;
40constexpr uint8_t AbsoluteSendTime::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -070041constexpr const char AbsoluteSendTime::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -070042
danilchap978504e2017-04-06 01:03:53 -070043bool AbsoluteSendTime::Parse(rtc::ArrayView<const uint8_t> data,
44 uint32_t* time_24bits) {
45 if (data.size() != 3)
46 return false;
47 *time_24bits = ByteReader<uint32_t, 3>::ReadBigEndian(data.data());
danilchap1edb7ab2016-04-20 05:25:10 -070048 return true;
49}
50
Danil Chapovalov9bf31582018-06-18 13:48:20 +020051bool AbsoluteSendTime::Write(rtc::ArrayView<uint8_t> data,
52 uint32_t time_24bits) {
53 RTC_DCHECK_EQ(data.size(), 3);
Danil Chapovalovf3ba6482017-06-12 15:43:55 +020054 RTC_DCHECK_LE(time_24bits, 0x00FFFFFF);
Danil Chapovalov9bf31582018-06-18 13:48:20 +020055 ByteWriter<uint32_t, 3>::WriteBigEndian(data.data(), time_24bits);
danilchap1edb7ab2016-04-20 05:25:10 -070056 return true;
57}
58
59// An RTP Header Extension for Client-to-Mixer Audio Level Indication
60//
61// https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
62//
63// The form of the audio level extension block:
64//
65// 0 1
66// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
67// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68// | ID | len=0 |V| level |
69// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70//
danilchape2a01772016-10-28 07:08:58 -070071constexpr RTPExtensionType AudioLevel::kId;
72constexpr uint8_t AudioLevel::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -070073constexpr const char AudioLevel::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -070074
danilchap978504e2017-04-06 01:03:53 -070075bool AudioLevel::Parse(rtc::ArrayView<const uint8_t> data,
danilchap1edb7ab2016-04-20 05:25:10 -070076 bool* voice_activity,
77 uint8_t* audio_level) {
danilchap978504e2017-04-06 01:03:53 -070078 if (data.size() != 1)
79 return false;
danilchap1edb7ab2016-04-20 05:25:10 -070080 *voice_activity = (data[0] & 0x80) != 0;
81 *audio_level = data[0] & 0x7F;
82 return true;
83}
84
Danil Chapovalov9bf31582018-06-18 13:48:20 +020085bool AudioLevel::Write(rtc::ArrayView<uint8_t> data,
danilchap1edb7ab2016-04-20 05:25:10 -070086 bool voice_activity,
87 uint8_t audio_level) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +020088 RTC_DCHECK_EQ(data.size(), 1);
danilchap1edb7ab2016-04-20 05:25:10 -070089 RTC_CHECK_LE(audio_level, 0x7f);
90 data[0] = (voice_activity ? 0x80 : 0x00) | audio_level;
91 return true;
92}
93
94// From RFC 5450: Transmission Time Offsets in RTP Streams.
95//
96// The transmission time is signaled to the receiver in-band using the
Johannes Kron07ba2b92018-09-26 13:33:35 +020097// general mechanism for RTP header extensions [RFC8285]. The payload
danilchap1edb7ab2016-04-20 05:25:10 -070098// of this extension (the transmitted value) is a 24-bit signed integer.
99// When added to the RTP timestamp of the packet, it represents the
100// "effective" RTP transmission time of the packet, on the RTP
101// timescale.
102//
103// The form of the transmission offset extension block:
104//
105// 0 1 2 3
106// 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
107// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
108// | ID | len=2 | transmission offset |
109// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
danilchape2a01772016-10-28 07:08:58 -0700110constexpr RTPExtensionType TransmissionOffset::kId;
111constexpr uint8_t TransmissionOffset::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700112constexpr const char TransmissionOffset::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -0700113
danilchap978504e2017-04-06 01:03:53 -0700114bool TransmissionOffset::Parse(rtc::ArrayView<const uint8_t> data,
115 int32_t* rtp_time) {
116 if (data.size() != 3)
117 return false;
118 *rtp_time = ByteReader<int32_t, 3>::ReadBigEndian(data.data());
danilchap1edb7ab2016-04-20 05:25:10 -0700119 return true;
120}
121
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200122bool TransmissionOffset::Write(rtc::ArrayView<uint8_t> data, int32_t rtp_time) {
123 RTC_DCHECK_EQ(data.size(), 3);
Danil Chapovalov31e4e802016-08-03 18:27:40 +0200124 RTC_DCHECK_LE(rtp_time, 0x00ffffff);
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200125 ByteWriter<int32_t, 3>::WriteBigEndian(data.data(), rtp_time);
danilchap1edb7ab2016-04-20 05:25:10 -0700126 return true;
127}
128
Johannes Kron54047be2019-02-21 14:09:20 +0000129// TransportSequenceNumber
130//
danilchap1edb7ab2016-04-20 05:25:10 -0700131// 0 1 2
132// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
133// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kron54047be2019-02-21 14:09:20 +0000134// | ID | L=1 |transport-wide sequence number |
danilchap1edb7ab2016-04-20 05:25:10 -0700135// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
danilchape2a01772016-10-28 07:08:58 -0700136constexpr RTPExtensionType TransportSequenceNumber::kId;
137constexpr uint8_t TransportSequenceNumber::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700138constexpr const char TransportSequenceNumber::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -0700139
danilchap978504e2017-04-06 01:03:53 -0700140bool TransportSequenceNumber::Parse(rtc::ArrayView<const uint8_t> data,
Johannes Kron54047be2019-02-21 14:09:20 +0000141 uint16_t* transport_sequence_number) {
142 if (data.size() != kValueSizeBytes)
danilchap978504e2017-04-06 01:03:53 -0700143 return false;
Johannes Kron54047be2019-02-21 14:09:20 +0000144 *transport_sequence_number = ByteReader<uint16_t>::ReadBigEndian(data.data());
danilchap1edb7ab2016-04-20 05:25:10 -0700145 return true;
146}
147
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200148bool TransportSequenceNumber::Write(rtc::ArrayView<uint8_t> data,
Johannes Kron54047be2019-02-21 14:09:20 +0000149 uint16_t transport_sequence_number) {
150 RTC_DCHECK_EQ(data.size(), ValueSize(transport_sequence_number));
151 ByteWriter<uint16_t>::WriteBigEndian(data.data(), transport_sequence_number);
152 return true;
153}
154
155// TransportSequenceNumberV2
156//
157// In addition to the format used for TransportSequencNumber, V2 also supports
158// the following packet format where two extra bytes are used to specify that
159// the sender requests immediate feedback.
160// 0 1 2 3
161// 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
162// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
163// | ID | L=3 |transport-wide sequence number |T| seq count |
164// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
165// |seq count cont.|
166// +-+-+-+-+-+-+-+-+
167//
168// The bit |T| determines whether the feedback should include timing information
169// or not and |seq_count| determines how many additional packets the feedback
170// packet should cover.
171constexpr RTPExtensionType TransportSequenceNumberV2::kId;
172constexpr uint8_t TransportSequenceNumberV2::kValueSizeBytesWithFeedbackRequest;
173constexpr const char TransportSequenceNumberV2::kUri[];
174constexpr uint16_t TransportSequenceNumberV2::kIncludeTimestampsBit;
175
176bool TransportSequenceNumberV2::Parse(
177 rtc::ArrayView<const uint8_t> data,
178 uint16_t* transport_sequence_number,
179 absl::optional<FeedbackRequest>* feedback_request) {
180 if (data.size() != TransportSequenceNumber::kValueSizeBytes &&
181 data.size() != kValueSizeBytesWithFeedbackRequest)
182 return false;
183
184 *transport_sequence_number = ByteReader<uint16_t>::ReadBigEndian(data.data());
185
186 if (data.size() == kValueSizeBytesWithFeedbackRequest) {
187 uint16_t feedback_request_raw =
188 ByteReader<uint16_t>::ReadBigEndian(data.data() + 2);
189 bool include_timestamps =
190 (feedback_request_raw & kIncludeTimestampsBit) != 0;
191 uint16_t sequence_count = feedback_request_raw & ~kIncludeTimestampsBit;
192 *feedback_request = {include_timestamps, sequence_count};
193 } else {
194 *feedback_request = absl::nullopt;
195 }
196 return true;
197}
198
199bool TransportSequenceNumberV2::Write(
200 rtc::ArrayView<uint8_t> data,
201 uint16_t transport_sequence_number,
202 const absl::optional<FeedbackRequest>& feedback_request) {
203 RTC_DCHECK_EQ(data.size(),
204 ValueSize(transport_sequence_number, feedback_request));
205
206 ByteWriter<uint16_t>::WriteBigEndian(data.data(), transport_sequence_number);
207
208 if (feedback_request) {
209 RTC_DCHECK_GE(feedback_request->sequence_count, 0);
210 RTC_DCHECK_LT(feedback_request->sequence_count, kIncludeTimestampsBit);
211 uint16_t feedback_request_raw =
212 feedback_request->sequence_count |
213 (feedback_request->include_timestamps ? kIncludeTimestampsBit : 0);
214 ByteWriter<uint16_t>::WriteBigEndian(data.data() + 2, feedback_request_raw);
215 }
danilchap1edb7ab2016-04-20 05:25:10 -0700216 return true;
217}
218
219// Coordination of Video Orientation in RTP streams.
220//
221// Coordination of Video Orientation consists in signaling of the current
222// orientation of the image captured on the sender side to the receiver for
223// appropriate rendering and displaying.
224//
225// 0 1
226// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
227// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228// | ID | len=0 |0 0 0 0 C F R R|
229// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
danilchape2a01772016-10-28 07:08:58 -0700230constexpr RTPExtensionType VideoOrientation::kId;
231constexpr uint8_t VideoOrientation::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700232constexpr const char VideoOrientation::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -0700233
danilchap978504e2017-04-06 01:03:53 -0700234bool VideoOrientation::Parse(rtc::ArrayView<const uint8_t> data,
235 VideoRotation* rotation) {
236 if (data.size() != 1)
237 return false;
magjed71eb61c2016-09-08 03:24:58 -0700238 *rotation = ConvertCVOByteToVideoRotation(data[0]);
danilchap1edb7ab2016-04-20 05:25:10 -0700239 return true;
240}
241
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200242bool VideoOrientation::Write(rtc::ArrayView<uint8_t> data,
243 VideoRotation rotation) {
244 RTC_DCHECK_EQ(data.size(), 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700245 data[0] = ConvertVideoRotationToCVOByte(rotation);
246 return true;
247}
248
danilchap978504e2017-04-06 01:03:53 -0700249bool VideoOrientation::Parse(rtc::ArrayView<const uint8_t> data,
250 uint8_t* value) {
251 if (data.size() != 1)
252 return false;
danilchap1edb7ab2016-04-20 05:25:10 -0700253 *value = data[0];
254 return true;
255}
256
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200257bool VideoOrientation::Write(rtc::ArrayView<uint8_t> data, uint8_t value) {
258 RTC_DCHECK_EQ(data.size(), 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700259 data[0] = value;
260 return true;
261}
Danil Chapovalov08b03512016-09-07 15:08:13 +0200262
263// 0 1 2 3
264// 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
265// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
266// | ID | len=2 | MIN delay | MAX delay |
267// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
268constexpr RTPExtensionType PlayoutDelayLimits::kId;
269constexpr uint8_t PlayoutDelayLimits::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700270constexpr const char PlayoutDelayLimits::kUri[];
Danil Chapovalov08b03512016-09-07 15:08:13 +0200271
danilchap978504e2017-04-06 01:03:53 -0700272bool PlayoutDelayLimits::Parse(rtc::ArrayView<const uint8_t> data,
Danil Chapovalov08b03512016-09-07 15:08:13 +0200273 PlayoutDelay* playout_delay) {
274 RTC_DCHECK(playout_delay);
danilchap978504e2017-04-06 01:03:53 -0700275 if (data.size() != 3)
276 return false;
277 uint32_t raw = ByteReader<uint32_t, 3>::ReadBigEndian(data.data());
Danil Chapovalov08b03512016-09-07 15:08:13 +0200278 uint16_t min_raw = (raw >> 12);
279 uint16_t max_raw = (raw & 0xfff);
280 if (min_raw > max_raw)
281 return false;
282 playout_delay->min_ms = min_raw * kGranularityMs;
283 playout_delay->max_ms = max_raw * kGranularityMs;
284 return true;
285}
286
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200287bool PlayoutDelayLimits::Write(rtc::ArrayView<uint8_t> data,
Danil Chapovalov08b03512016-09-07 15:08:13 +0200288 const PlayoutDelay& playout_delay) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200289 RTC_DCHECK_EQ(data.size(), 3);
Danil Chapovalov08b03512016-09-07 15:08:13 +0200290 RTC_DCHECK_LE(0, playout_delay.min_ms);
291 RTC_DCHECK_LE(playout_delay.min_ms, playout_delay.max_ms);
292 RTC_DCHECK_LE(playout_delay.max_ms, kMaxMs);
293 // Convert MS to value to be sent on extension header.
294 uint32_t min_delay = playout_delay.min_ms / kGranularityMs;
295 uint32_t max_delay = playout_delay.max_ms / kGranularityMs;
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200296 ByteWriter<uint32_t, 3>::WriteBigEndian(data.data(),
297 (min_delay << 12) | max_delay);
Danil Chapovalov08b03512016-09-07 15:08:13 +0200298 return true;
299}
300
ilnik00d802b2017-04-11 10:34:31 -0700301// Video Content Type.
302//
303// E.g. default video or screenshare.
304//
305// 0 1
306// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
307// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
308// | ID | len=0 | Content type |
309// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
310constexpr RTPExtensionType VideoContentTypeExtension::kId;
311constexpr uint8_t VideoContentTypeExtension::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700312constexpr const char VideoContentTypeExtension::kUri[];
ilnik00d802b2017-04-11 10:34:31 -0700313
314bool VideoContentTypeExtension::Parse(rtc::ArrayView<const uint8_t> data,
315 VideoContentType* content_type) {
316 if (data.size() == 1 &&
ilnik6d5b4d62017-08-30 03:32:14 -0700317 videocontenttypehelpers::IsValidContentType(data[0])) {
ilnik00d802b2017-04-11 10:34:31 -0700318 *content_type = static_cast<VideoContentType>(data[0]);
319 return true;
320 }
321 return false;
322}
323
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200324bool VideoContentTypeExtension::Write(rtc::ArrayView<uint8_t> data,
ilnik00d802b2017-04-11 10:34:31 -0700325 VideoContentType content_type) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200326 RTC_DCHECK_EQ(data.size(), 1);
ilnik00d802b2017-04-11 10:34:31 -0700327 data[0] = static_cast<uint8_t>(content_type);
328 return true;
329}
330
ilnik04f4d122017-06-19 07:18:55 -0700331// Video Timing.
332// 6 timestamps in milliseconds counted from capture time stored in rtp header:
333// encode start/finish, packetization complete, pacer exit and reserved for
sprangba050a62017-08-18 02:51:12 -0700334// modification by the network modification. |flags| is a bitmask and has the
335// following allowed values:
336// 0 = Valid data, but no flags available (backwards compatibility)
337// 1 = Frame marked as timing frame due to cyclic timer.
338// 2 = Frame marked as timing frame due to size being outside limit.
339// 255 = Invalid. The whole timing frame extension should be ignored.
340//
ilnik04f4d122017-06-19 07:18:55 -0700341// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100342// 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
343// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
344// | ID | len=12| flags | encode start ms delta |
345// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
346// | encode finish ms delta | packetizer finish ms delta |
347// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
348// | pacer exit ms delta | network timestamp ms delta |
349// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
sprangba050a62017-08-18 02:51:12 -0700350// | network2 timestamp ms delta |
351// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ilnik04f4d122017-06-19 07:18:55 -0700352
353constexpr RTPExtensionType VideoTimingExtension::kId;
354constexpr uint8_t VideoTimingExtension::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700355constexpr const char VideoTimingExtension::kUri[];
ilnik04f4d122017-06-19 07:18:55 -0700356
357bool VideoTimingExtension::Parse(rtc::ArrayView<const uint8_t> data,
ilnik2edc6842017-07-06 03:06:50 -0700358 VideoSendTiming* timing) {
ilnik04f4d122017-06-19 07:18:55 -0700359 RTC_DCHECK(timing);
sprangba050a62017-08-18 02:51:12 -0700360 // TODO(sprang): Deprecate support for old wire format.
361 ptrdiff_t off = 0;
362 switch (data.size()) {
363 case kValueSizeBytes - 1:
364 timing->flags = 0;
365 off = 1; // Old wire format without the flags field.
366 break;
367 case kValueSizeBytes:
368 timing->flags = ByteReader<uint8_t>::ReadBigEndian(data.data());
369 break;
370 default:
371 return false;
372 }
373
374 timing->encode_start_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
375 data.data() + VideoSendTiming::kEncodeStartDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700376 timing->encode_finish_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700377 data.data() + VideoSendTiming::kEncodeFinishDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700378 timing->packetization_finish_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700379 data.data() + VideoSendTiming::kPacketizationFinishDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700380 timing->pacer_exit_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700381 data.data() + VideoSendTiming::kPacerExitDeltaOffset - off);
Danil Chapovalov996eb9e2017-10-30 17:14:41 +0100382 timing->network_timestamp_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700383 data.data() + VideoSendTiming::kNetworkTimestampDeltaOffset - off);
Danil Chapovalov996eb9e2017-10-30 17:14:41 +0100384 timing->network2_timestamp_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700385 data.data() + VideoSendTiming::kNetwork2TimestampDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700386 return true;
387}
388
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200389bool VideoTimingExtension::Write(rtc::ArrayView<uint8_t> data,
390 const VideoSendTiming& timing) {
391 RTC_DCHECK_EQ(data.size(), 1 + 2 * 6);
392 ByteWriter<uint8_t>::WriteBigEndian(
393 data.data() + VideoSendTiming::kFlagsOffset, timing.flags);
ilnik04f4d122017-06-19 07:18:55 -0700394 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200395 data.data() + VideoSendTiming::kEncodeStartDeltaOffset,
sprangba050a62017-08-18 02:51:12 -0700396 timing.encode_start_delta_ms);
397 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200398 data.data() + VideoSendTiming::kEncodeFinishDeltaOffset,
ilnik04f4d122017-06-19 07:18:55 -0700399 timing.encode_finish_delta_ms);
400 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200401 data.data() + VideoSendTiming::kPacketizationFinishDeltaOffset,
ilnik04f4d122017-06-19 07:18:55 -0700402 timing.packetization_finish_delta_ms);
403 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200404 data.data() + VideoSendTiming::kPacerExitDeltaOffset,
ilnik2edc6842017-07-06 03:06:50 -0700405 timing.pacer_exit_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700406 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200407 data.data() + VideoSendTiming::kNetworkTimestampDeltaOffset,
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100408 timing.network_timestamp_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700409 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200410 data.data() + VideoSendTiming::kNetwork2TimestampDeltaOffset,
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100411 timing.network2_timestamp_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700412 return true;
413}
414
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200415bool VideoTimingExtension::Write(rtc::ArrayView<uint8_t> data,
ilnik04f4d122017-06-19 07:18:55 -0700416 uint16_t time_delta_ms,
sprangba050a62017-08-18 02:51:12 -0700417 uint8_t offset) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200418 RTC_DCHECK_GE(data.size(), offset + 2);
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100419 RTC_DCHECK_LE(offset, kValueSizeBytes - sizeof(uint16_t));
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200420 ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset, time_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700421 return true;
422}
423
Johnny Leee0c8b232018-09-11 16:50:49 -0400424// Frame Marking.
425//
426// Meta-information about an RTP stream outside the encrypted media payload,
427// useful for an RTP switch to do codec-agnostic selective forwarding
428// without decrypting the payload.
429//
430// For non-scalable streams:
431// 0 1
432// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
433// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434// | ID | L = 0 |S|E|I|D|0 0 0 0|
435// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436//
437// For scalable streams:
438// 0 1 2 3
439// 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
440// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
441// | ID | L = 2 |S|E|I|D|B| TID | LID | TL0PICIDX |
442// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
443
444constexpr RTPExtensionType FrameMarkingExtension::kId;
445constexpr const char FrameMarkingExtension::kUri[];
446
447bool FrameMarkingExtension::IsScalable(uint8_t temporal_id, uint8_t layer_id) {
448 return temporal_id != kNoTemporalIdx || layer_id != kNoSpatialIdx;
449}
450
451bool FrameMarkingExtension::Parse(rtc::ArrayView<const uint8_t> data,
452 FrameMarking* frame_marking) {
453 RTC_DCHECK(frame_marking);
454
455 if (data.size() != 1 && data.size() != 3)
456 return false;
457
458 frame_marking->start_of_frame = (data[0] & 0x80) != 0;
459 frame_marking->end_of_frame = (data[0] & 0x40) != 0;
460 frame_marking->independent_frame = (data[0] & 0x20) != 0;
461 frame_marking->discardable_frame = (data[0] & 0x10) != 0;
462
463 if (data.size() == 3) {
464 frame_marking->base_layer_sync = (data[0] & 0x08) != 0;
465 frame_marking->temporal_id = data[0] & 0x7;
466 frame_marking->layer_id = data[1];
467 frame_marking->tl0_pic_idx = data[2];
468 } else {
469 // non-scalable
470 frame_marking->base_layer_sync = false;
471 frame_marking->temporal_id = kNoTemporalIdx;
472 frame_marking->layer_id = kNoSpatialIdx;
473 frame_marking->tl0_pic_idx = 0;
474 }
475 return true;
476}
477
478size_t FrameMarkingExtension::ValueSize(const FrameMarking& frame_marking) {
479 if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id))
480 return 3;
481 else
482 return 1;
483}
484
485bool FrameMarkingExtension::Write(rtc::ArrayView<uint8_t> data,
486 const FrameMarking& frame_marking) {
487 RTC_DCHECK_GE(data.size(), 1);
488 RTC_CHECK_LE(frame_marking.temporal_id, 0x07);
489 data[0] = frame_marking.start_of_frame ? 0x80 : 0x00;
490 data[0] |= frame_marking.end_of_frame ? 0x40 : 0x00;
491 data[0] |= frame_marking.independent_frame ? 0x20 : 0x00;
492 data[0] |= frame_marking.discardable_frame ? 0x10 : 0x00;
493
494 if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id)) {
495 RTC_DCHECK_EQ(data.size(), 3);
496 data[0] |= frame_marking.base_layer_sync ? 0x08 : 0x00;
497 data[0] |= frame_marking.temporal_id & 0x07;
498 data[1] = frame_marking.layer_id;
499 data[2] = frame_marking.tl0_pic_idx;
500 }
501 return true;
502}
503
Johannes Kron09d65882018-11-27 14:36:41 +0100504// Color space including HDR metadata as an optional field.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100505//
Johannes Krond0b69a82018-12-03 14:18:53 +0100506// RTP header extension to carry color space information and optionally HDR
507// metadata. The float values in the HDR metadata struct are upscaled by a
508// static factor and transmitted as unsigned integers.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100509//
Johannes Krond0b69a82018-12-03 14:18:53 +0100510// Data layout of color space with HDR metadata (two-byte RTP header extension)
Johannes Kronad1d9f02018-11-09 11:12:36 +0100511// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100512// 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
513// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100514// | ID | length=28 | primaries | transfer |
Johannes Krond0b69a82018-12-03 14:18:53 +0100515// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100516// | matrix |range+chr.sit. | luminance_max |
Johannes Krond0b69a82018-12-03 14:18:53 +0100517// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100518// | luminance_min | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100519// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100520// |primary_r.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100521// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100522// |primary_g.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100523// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100524// |primary_b.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100525// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100526// |white.x and .y | max_content_light_level |
Johannes Krond0b69a82018-12-03 14:18:53 +0100527// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100528// | max_frame_average_light_level |
529// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kron09d65882018-11-27 14:36:41 +0100530//
Johannes Krond0b69a82018-12-03 14:18:53 +0100531// Data layout of color space w/o HDR metadata (one-byte RTP header extension)
Johannes Kron09d65882018-11-27 14:36:41 +0100532// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100533// 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
534// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100535// | ID | L = 3 | primaries | transfer | matrix |
Johannes Krond0b69a82018-12-03 14:18:53 +0100536// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100537// |range+chr.sit. |
Johannes Krond0b69a82018-12-03 14:18:53 +0100538// +-+-+-+-+-+-+-+-+
Johannes Kronad1d9f02018-11-09 11:12:36 +0100539
Johannes Kron09d65882018-11-27 14:36:41 +0100540constexpr RTPExtensionType ColorSpaceExtension::kId;
541constexpr uint8_t ColorSpaceExtension::kValueSizeBytes;
542constexpr const char ColorSpaceExtension::kUri[];
543
544bool ColorSpaceExtension::Parse(rtc::ArrayView<const uint8_t> data,
545 ColorSpace* color_space) {
546 RTC_DCHECK(color_space);
547 if (data.size() != kValueSizeBytes &&
548 data.size() != kValueSizeBytesWithoutHdrMetadata)
Johannes Kronad1d9f02018-11-09 11:12:36 +0100549 return false;
550
551 size_t offset = 0;
Johannes Kron09d65882018-11-27 14:36:41 +0100552 // Read color space information.
Johannes Kronc13f4be2018-12-12 09:52:53 +0100553 if (!color_space->set_primaries_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100554 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100555 if (!color_space->set_transfer_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100556 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100557 if (!color_space->set_matrix_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100558 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100559
560 uint8_t range_and_chroma_siting = data[offset++];
561 if (!color_space->set_range_from_uint8((range_and_chroma_siting >> 4) & 0x03))
562 return false;
563 if (!color_space->set_chroma_siting_horizontal_from_uint8(
564 (range_and_chroma_siting >> 2) & 0x03))
565 return false;
566 if (!color_space->set_chroma_siting_vertical_from_uint8(
567 range_and_chroma_siting & 0x03))
Johannes Kron09d65882018-11-27 14:36:41 +0100568 return false;
569
570 // Read HDR metadata if it exists, otherwise clear it.
571 if (data.size() == kValueSizeBytesWithoutHdrMetadata) {
572 color_space->set_hdr_metadata(nullptr);
573 } else {
574 HdrMetadata hdr_metadata;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100575 offset += ParseHdrMetadata(data.subview(offset), &hdr_metadata);
576 if (!hdr_metadata.Validate())
577 return false;
Johannes Kron09d65882018-11-27 14:36:41 +0100578 color_space->set_hdr_metadata(&hdr_metadata);
579 }
580 RTC_DCHECK_EQ(ValueSize(*color_space), offset);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100581 return true;
582}
583
Johannes Kron09d65882018-11-27 14:36:41 +0100584bool ColorSpaceExtension::Write(rtc::ArrayView<uint8_t> data,
585 const ColorSpace& color_space) {
Johannes Krond0b69a82018-12-03 14:18:53 +0100586 RTC_DCHECK_EQ(data.size(), ValueSize(color_space));
Johannes Kronad1d9f02018-11-09 11:12:36 +0100587 size_t offset = 0;
Johannes Kron09d65882018-11-27 14:36:41 +0100588 // Write color space information.
Johannes Kronc13f4be2018-12-12 09:52:53 +0100589 data[offset++] = static_cast<uint8_t>(color_space.primaries());
590 data[offset++] = static_cast<uint8_t>(color_space.transfer());
591 data[offset++] = static_cast<uint8_t>(color_space.matrix());
592 data[offset++] = CombineRangeAndChromaSiting(
593 color_space.range(), color_space.chroma_siting_horizontal(),
594 color_space.chroma_siting_vertical());
Johannes Kronad1d9f02018-11-09 11:12:36 +0100595
Johannes Kron09d65882018-11-27 14:36:41 +0100596 // Write HDR metadata if it exists.
597 if (color_space.hdr_metadata()) {
Johannes Kronc13f4be2018-12-12 09:52:53 +0100598 offset +=
599 WriteHdrMetadata(data.subview(offset), *color_space.hdr_metadata());
Johannes Kron09d65882018-11-27 14:36:41 +0100600 }
601 RTC_DCHECK_EQ(ValueSize(color_space), offset);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100602 return true;
603}
604
Johannes Kronc13f4be2018-12-12 09:52:53 +0100605// Combines range and chroma siting into one byte with the following bit layout:
606// bits 0-1 Chroma siting vertical.
607// 2-3 Chroma siting horizontal.
608// 4-5 Range.
609// 6-7 Unused.
610uint8_t ColorSpaceExtension::CombineRangeAndChromaSiting(
611 ColorSpace::RangeID range,
612 ColorSpace::ChromaSiting chroma_siting_horizontal,
613 ColorSpace::ChromaSiting chroma_siting_vertical) {
614 RTC_DCHECK_LE(static_cast<uint8_t>(range), 3);
615 RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_horizontal), 3);
616 RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_vertical), 3);
617 return (static_cast<uint8_t>(range) << 4) |
618 (static_cast<uint8_t>(chroma_siting_horizontal) << 2) |
619 static_cast<uint8_t>(chroma_siting_vertical);
620}
621
622size_t ColorSpaceExtension::ParseHdrMetadata(rtc::ArrayView<const uint8_t> data,
623 HdrMetadata* hdr_metadata) {
624 RTC_DCHECK_EQ(data.size(),
625 kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
626 size_t offset = 0;
627 offset += ParseLuminance(data.data() + offset,
628 &hdr_metadata->mastering_metadata.luminance_max,
629 kLuminanceMaxDenominator);
630 offset += ParseLuminance(data.data() + offset,
631 &hdr_metadata->mastering_metadata.luminance_min,
632 kLuminanceMinDenominator);
633 offset += ParseChromaticity(data.data() + offset,
634 &hdr_metadata->mastering_metadata.primary_r);
635 offset += ParseChromaticity(data.data() + offset,
636 &hdr_metadata->mastering_metadata.primary_g);
637 offset += ParseChromaticity(data.data() + offset,
638 &hdr_metadata->mastering_metadata.primary_b);
639 offset += ParseChromaticity(data.data() + offset,
640 &hdr_metadata->mastering_metadata.white_point);
641 hdr_metadata->max_content_light_level =
642 ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
643 offset += 2;
644 hdr_metadata->max_frame_average_light_level =
645 ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
646 offset += 2;
647 return offset;
648}
649
Johannes Kron09d65882018-11-27 14:36:41 +0100650size_t ColorSpaceExtension::ParseChromaticity(
Johannes Kronad1d9f02018-11-09 11:12:36 +0100651 const uint8_t* data,
652 HdrMasteringMetadata::Chromaticity* p) {
653 uint16_t chromaticity_x_scaled = ByteReader<uint16_t>::ReadBigEndian(data);
654 uint16_t chromaticity_y_scaled =
655 ByteReader<uint16_t>::ReadBigEndian(data + 2);
656 p->x = static_cast<float>(chromaticity_x_scaled) / kChromaticityDenominator;
657 p->y = static_cast<float>(chromaticity_y_scaled) / kChromaticityDenominator;
658 return 4; // Return number of bytes read.
659}
660
Johannes Kron09d65882018-11-27 14:36:41 +0100661size_t ColorSpaceExtension::ParseLuminance(const uint8_t* data,
662 float* f,
663 int denominator) {
Johannes Kronc13f4be2018-12-12 09:52:53 +0100664 uint16_t luminance_scaled = ByteReader<uint16_t>::ReadBigEndian(data);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100665 *f = static_cast<float>(luminance_scaled) / denominator;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100666 return 2; // Return number of bytes read.
667}
668
669size_t ColorSpaceExtension::WriteHdrMetadata(rtc::ArrayView<uint8_t> data,
670 const HdrMetadata& hdr_metadata) {
671 RTC_DCHECK_EQ(data.size(),
672 kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
673 RTC_DCHECK(hdr_metadata.Validate());
674 size_t offset = 0;
675 offset += WriteLuminance(data.data() + offset,
676 hdr_metadata.mastering_metadata.luminance_max,
677 kLuminanceMaxDenominator);
678 offset += WriteLuminance(data.data() + offset,
679 hdr_metadata.mastering_metadata.luminance_min,
680 kLuminanceMinDenominator);
681 offset += WriteChromaticity(data.data() + offset,
682 hdr_metadata.mastering_metadata.primary_r);
683 offset += WriteChromaticity(data.data() + offset,
684 hdr_metadata.mastering_metadata.primary_g);
685 offset += WriteChromaticity(data.data() + offset,
686 hdr_metadata.mastering_metadata.primary_b);
687 offset += WriteChromaticity(data.data() + offset,
688 hdr_metadata.mastering_metadata.white_point);
689
690 ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset,
691 hdr_metadata.max_content_light_level);
692 offset += 2;
693 ByteWriter<uint16_t>::WriteBigEndian(
694 data.data() + offset, hdr_metadata.max_frame_average_light_level);
695 offset += 2;
696 return offset;
Johannes Kronad1d9f02018-11-09 11:12:36 +0100697}
698
Johannes Kron09d65882018-11-27 14:36:41 +0100699size_t ColorSpaceExtension::WriteChromaticity(
Johannes Kronad1d9f02018-11-09 11:12:36 +0100700 uint8_t* data,
701 const HdrMasteringMetadata::Chromaticity& p) {
702 RTC_DCHECK_GE(p.x, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100703 RTC_DCHECK_LE(p.x, 1.0f);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100704 RTC_DCHECK_GE(p.y, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100705 RTC_DCHECK_LE(p.y, 1.0f);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100706 ByteWriter<uint16_t>::WriteBigEndian(
707 data, std::round(p.x * kChromaticityDenominator));
708 ByteWriter<uint16_t>::WriteBigEndian(
709 data + 2, std::round(p.y * kChromaticityDenominator));
710 return 4; // Return number of bytes written.
711}
712
Johannes Kron09d65882018-11-27 14:36:41 +0100713size_t ColorSpaceExtension::WriteLuminance(uint8_t* data,
714 float f,
715 int denominator) {
Johannes Kronad1d9f02018-11-09 11:12:36 +0100716 RTC_DCHECK_GE(f, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100717 float upscaled_value = f * denominator;
718 RTC_DCHECK_LE(upscaled_value, std::numeric_limits<uint16_t>::max());
719 ByteWriter<uint16_t>::WriteBigEndian(data, std::round(upscaled_value));
720 return 2; // Return number of bytes written.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100721}
722
Steve Antona3251dd2017-07-21 09:58:31 -0700723bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
724 StringRtpHeaderExtension* str) {
725 if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
726 return false;
727 str->Set(data);
728 RTC_DCHECK(!str->empty());
729 return true;
730}
731
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200732bool BaseRtpStringExtension::Write(rtc::ArrayView<uint8_t> data,
Steve Antona3251dd2017-07-21 09:58:31 -0700733 const StringRtpHeaderExtension& str) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200734 RTC_DCHECK_EQ(data.size(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700735 RTC_DCHECK_GE(str.size(), 1);
736 RTC_DCHECK_LE(str.size(), StringRtpHeaderExtension::kMaxSize);
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200737 memcpy(data.data(), str.data(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700738 return true;
739}
740
741bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
742 std::string* str) {
743 if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
744 return false;
745 const char* cstr = reinterpret_cast<const char*>(data.data());
746 // If there is a \0 character in the middle of the |data|, treat it as end
747 // of the string. Well-formed string extensions shouldn't contain it.
748 str->assign(cstr, strnlen(cstr, data.size()));
749 RTC_DCHECK(!str->empty());
750 return true;
751}
752
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200753bool BaseRtpStringExtension::Write(rtc::ArrayView<uint8_t> data,
754 const std::string& str) {
Johannes Kron78cdde32018-10-05 10:00:46 +0200755 if (str.size() > StringRtpHeaderExtension::kMaxSize) {
756 return false;
757 }
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200758 RTC_DCHECK_EQ(data.size(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700759 RTC_DCHECK_GE(str.size(), 1);
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200760 memcpy(data.data(), str.data(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700761 return true;
762}
763
764// Constant declarations for string RTP header extension types.
765
danilchapef8d7732017-04-19 02:59:48 -0700766constexpr RTPExtensionType RtpStreamId::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700767constexpr const char RtpStreamId::kUri[];
danilchapef8d7732017-04-19 02:59:48 -0700768
danilchapef8d7732017-04-19 02:59:48 -0700769constexpr RTPExtensionType RepairedRtpStreamId::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700770constexpr const char RepairedRtpStreamId::kUri[];
danilchapef8d7732017-04-19 02:59:48 -0700771
Steve Antona3251dd2017-07-21 09:58:31 -0700772constexpr RTPExtensionType RtpMid::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700773constexpr const char RtpMid::kUri[];
erikvargae6b16192017-05-11 02:36:32 -0700774
danilchap1edb7ab2016-04-20 05:25:10 -0700775} // namespace webrtc