blob: 904500337aca75eb3be6a53d8f2511e94a7f310b [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
Johannes Kron0da25a12019-03-06 09:34:13 +0100169// or not and |seq_count| determines how many packets the feedback packet should
170// cover including the current packet. If |seq_count| is zero no feedback is
171// requested.
Johannes Kron54047be2019-02-21 14:09:20 +0000172constexpr RTPExtensionType TransportSequenceNumberV2::kId;
Johannes Kron0da25a12019-03-06 09:34:13 +0100173constexpr uint8_t TransportSequenceNumberV2::kValueSizeBytes;
174constexpr uint8_t
175 TransportSequenceNumberV2::kValueSizeBytesWithoutFeedbackRequest;
Johannes Kron54047be2019-02-21 14:09:20 +0000176constexpr const char TransportSequenceNumberV2::kUri[];
177constexpr uint16_t TransportSequenceNumberV2::kIncludeTimestampsBit;
178
179bool TransportSequenceNumberV2::Parse(
180 rtc::ArrayView<const uint8_t> data,
181 uint16_t* transport_sequence_number,
182 absl::optional<FeedbackRequest>* feedback_request) {
Johannes Kron0da25a12019-03-06 09:34:13 +0100183 if (data.size() != kValueSizeBytes &&
184 data.size() != kValueSizeBytesWithoutFeedbackRequest)
Johannes Kron54047be2019-02-21 14:09:20 +0000185 return false;
186
187 *transport_sequence_number = ByteReader<uint16_t>::ReadBigEndian(data.data());
188
Johannes Kron0da25a12019-03-06 09:34:13 +0100189 *feedback_request = absl::nullopt;
190 if (data.size() == kValueSizeBytes) {
Johannes Kron54047be2019-02-21 14:09:20 +0000191 uint16_t feedback_request_raw =
192 ByteReader<uint16_t>::ReadBigEndian(data.data() + 2);
193 bool include_timestamps =
194 (feedback_request_raw & kIncludeTimestampsBit) != 0;
195 uint16_t sequence_count = feedback_request_raw & ~kIncludeTimestampsBit;
Johannes Kron0da25a12019-03-06 09:34:13 +0100196
197 // If |sequence_count| is zero no feedback is requested.
198 if (sequence_count != 0) {
199 *feedback_request = {include_timestamps, sequence_count};
200 }
Johannes Kron54047be2019-02-21 14:09:20 +0000201 }
202 return true;
203}
204
205bool TransportSequenceNumberV2::Write(
206 rtc::ArrayView<uint8_t> data,
207 uint16_t transport_sequence_number,
208 const absl::optional<FeedbackRequest>& feedback_request) {
209 RTC_DCHECK_EQ(data.size(),
210 ValueSize(transport_sequence_number, feedback_request));
211
212 ByteWriter<uint16_t>::WriteBigEndian(data.data(), transport_sequence_number);
213
214 if (feedback_request) {
215 RTC_DCHECK_GE(feedback_request->sequence_count, 0);
216 RTC_DCHECK_LT(feedback_request->sequence_count, kIncludeTimestampsBit);
217 uint16_t feedback_request_raw =
218 feedback_request->sequence_count |
219 (feedback_request->include_timestamps ? kIncludeTimestampsBit : 0);
220 ByteWriter<uint16_t>::WriteBigEndian(data.data() + 2, feedback_request_raw);
221 }
danilchap1edb7ab2016-04-20 05:25:10 -0700222 return true;
223}
224
225// Coordination of Video Orientation in RTP streams.
226//
227// Coordination of Video Orientation consists in signaling of the current
228// orientation of the image captured on the sender side to the receiver for
229// appropriate rendering and displaying.
230//
231// 0 1
232// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
233// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
234// | ID | len=0 |0 0 0 0 C F R R|
235// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
danilchape2a01772016-10-28 07:08:58 -0700236constexpr RTPExtensionType VideoOrientation::kId;
237constexpr uint8_t VideoOrientation::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700238constexpr const char VideoOrientation::kUri[];
danilchap1edb7ab2016-04-20 05:25:10 -0700239
danilchap978504e2017-04-06 01:03:53 -0700240bool VideoOrientation::Parse(rtc::ArrayView<const uint8_t> data,
241 VideoRotation* rotation) {
242 if (data.size() != 1)
243 return false;
magjed71eb61c2016-09-08 03:24:58 -0700244 *rotation = ConvertCVOByteToVideoRotation(data[0]);
danilchap1edb7ab2016-04-20 05:25:10 -0700245 return true;
246}
247
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200248bool VideoOrientation::Write(rtc::ArrayView<uint8_t> data,
249 VideoRotation rotation) {
250 RTC_DCHECK_EQ(data.size(), 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700251 data[0] = ConvertVideoRotationToCVOByte(rotation);
252 return true;
253}
254
danilchap978504e2017-04-06 01:03:53 -0700255bool VideoOrientation::Parse(rtc::ArrayView<const uint8_t> data,
256 uint8_t* value) {
257 if (data.size() != 1)
258 return false;
danilchap1edb7ab2016-04-20 05:25:10 -0700259 *value = data[0];
260 return true;
261}
262
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200263bool VideoOrientation::Write(rtc::ArrayView<uint8_t> data, uint8_t value) {
264 RTC_DCHECK_EQ(data.size(), 1);
danilchap1edb7ab2016-04-20 05:25:10 -0700265 data[0] = value;
266 return true;
267}
Danil Chapovalov08b03512016-09-07 15:08:13 +0200268
269// 0 1 2 3
270// 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
271// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
272// | ID | len=2 | MIN delay | MAX delay |
273// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
274constexpr RTPExtensionType PlayoutDelayLimits::kId;
275constexpr uint8_t PlayoutDelayLimits::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700276constexpr const char PlayoutDelayLimits::kUri[];
Danil Chapovalov08b03512016-09-07 15:08:13 +0200277
danilchap978504e2017-04-06 01:03:53 -0700278bool PlayoutDelayLimits::Parse(rtc::ArrayView<const uint8_t> data,
Danil Chapovalov08b03512016-09-07 15:08:13 +0200279 PlayoutDelay* playout_delay) {
280 RTC_DCHECK(playout_delay);
danilchap978504e2017-04-06 01:03:53 -0700281 if (data.size() != 3)
282 return false;
283 uint32_t raw = ByteReader<uint32_t, 3>::ReadBigEndian(data.data());
Danil Chapovalov08b03512016-09-07 15:08:13 +0200284 uint16_t min_raw = (raw >> 12);
285 uint16_t max_raw = (raw & 0xfff);
286 if (min_raw > max_raw)
287 return false;
288 playout_delay->min_ms = min_raw * kGranularityMs;
289 playout_delay->max_ms = max_raw * kGranularityMs;
290 return true;
291}
292
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200293bool PlayoutDelayLimits::Write(rtc::ArrayView<uint8_t> data,
Danil Chapovalov08b03512016-09-07 15:08:13 +0200294 const PlayoutDelay& playout_delay) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200295 RTC_DCHECK_EQ(data.size(), 3);
Danil Chapovalov08b03512016-09-07 15:08:13 +0200296 RTC_DCHECK_LE(0, playout_delay.min_ms);
297 RTC_DCHECK_LE(playout_delay.min_ms, playout_delay.max_ms);
298 RTC_DCHECK_LE(playout_delay.max_ms, kMaxMs);
299 // Convert MS to value to be sent on extension header.
300 uint32_t min_delay = playout_delay.min_ms / kGranularityMs;
301 uint32_t max_delay = playout_delay.max_ms / kGranularityMs;
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200302 ByteWriter<uint32_t, 3>::WriteBigEndian(data.data(),
303 (min_delay << 12) | max_delay);
Danil Chapovalov08b03512016-09-07 15:08:13 +0200304 return true;
305}
306
ilnik00d802b2017-04-11 10:34:31 -0700307// Video Content Type.
308//
309// E.g. default video or screenshare.
310//
311// 0 1
312// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
313// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
314// | ID | len=0 | Content type |
315// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
316constexpr RTPExtensionType VideoContentTypeExtension::kId;
317constexpr uint8_t VideoContentTypeExtension::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700318constexpr const char VideoContentTypeExtension::kUri[];
ilnik00d802b2017-04-11 10:34:31 -0700319
320bool VideoContentTypeExtension::Parse(rtc::ArrayView<const uint8_t> data,
321 VideoContentType* content_type) {
322 if (data.size() == 1 &&
ilnik6d5b4d62017-08-30 03:32:14 -0700323 videocontenttypehelpers::IsValidContentType(data[0])) {
ilnik00d802b2017-04-11 10:34:31 -0700324 *content_type = static_cast<VideoContentType>(data[0]);
325 return true;
326 }
327 return false;
328}
329
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200330bool VideoContentTypeExtension::Write(rtc::ArrayView<uint8_t> data,
ilnik00d802b2017-04-11 10:34:31 -0700331 VideoContentType content_type) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200332 RTC_DCHECK_EQ(data.size(), 1);
ilnik00d802b2017-04-11 10:34:31 -0700333 data[0] = static_cast<uint8_t>(content_type);
334 return true;
335}
336
ilnik04f4d122017-06-19 07:18:55 -0700337// Video Timing.
338// 6 timestamps in milliseconds counted from capture time stored in rtp header:
339// encode start/finish, packetization complete, pacer exit and reserved for
sprangba050a62017-08-18 02:51:12 -0700340// modification by the network modification. |flags| is a bitmask and has the
341// following allowed values:
342// 0 = Valid data, but no flags available (backwards compatibility)
343// 1 = Frame marked as timing frame due to cyclic timer.
344// 2 = Frame marked as timing frame due to size being outside limit.
345// 255 = Invalid. The whole timing frame extension should be ignored.
346//
ilnik04f4d122017-06-19 07:18:55 -0700347// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100348// 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
349// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
350// | ID | len=12| flags | encode start ms delta |
351// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352// | encode finish ms delta | packetizer finish ms delta |
353// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354// | pacer exit ms delta | network timestamp ms delta |
355// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
sprangba050a62017-08-18 02:51:12 -0700356// | network2 timestamp ms delta |
357// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ilnik04f4d122017-06-19 07:18:55 -0700358
359constexpr RTPExtensionType VideoTimingExtension::kId;
360constexpr uint8_t VideoTimingExtension::kValueSizeBytes;
Steve Antond14d9f72017-07-21 10:59:39 -0700361constexpr const char VideoTimingExtension::kUri[];
ilnik04f4d122017-06-19 07:18:55 -0700362
363bool VideoTimingExtension::Parse(rtc::ArrayView<const uint8_t> data,
ilnik2edc6842017-07-06 03:06:50 -0700364 VideoSendTiming* timing) {
ilnik04f4d122017-06-19 07:18:55 -0700365 RTC_DCHECK(timing);
sprangba050a62017-08-18 02:51:12 -0700366 // TODO(sprang): Deprecate support for old wire format.
367 ptrdiff_t off = 0;
368 switch (data.size()) {
369 case kValueSizeBytes - 1:
370 timing->flags = 0;
371 off = 1; // Old wire format without the flags field.
372 break;
373 case kValueSizeBytes:
374 timing->flags = ByteReader<uint8_t>::ReadBigEndian(data.data());
375 break;
376 default:
377 return false;
378 }
379
380 timing->encode_start_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
381 data.data() + VideoSendTiming::kEncodeStartDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700382 timing->encode_finish_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700383 data.data() + VideoSendTiming::kEncodeFinishDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700384 timing->packetization_finish_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700385 data.data() + VideoSendTiming::kPacketizationFinishDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700386 timing->pacer_exit_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700387 data.data() + VideoSendTiming::kPacerExitDeltaOffset - off);
Danil Chapovalov996eb9e2017-10-30 17:14:41 +0100388 timing->network_timestamp_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700389 data.data() + VideoSendTiming::kNetworkTimestampDeltaOffset - off);
Danil Chapovalov996eb9e2017-10-30 17:14:41 +0100390 timing->network2_timestamp_delta_ms = ByteReader<uint16_t>::ReadBigEndian(
sprangba050a62017-08-18 02:51:12 -0700391 data.data() + VideoSendTiming::kNetwork2TimestampDeltaOffset - off);
ilnik04f4d122017-06-19 07:18:55 -0700392 return true;
393}
394
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200395bool VideoTimingExtension::Write(rtc::ArrayView<uint8_t> data,
396 const VideoSendTiming& timing) {
397 RTC_DCHECK_EQ(data.size(), 1 + 2 * 6);
398 ByteWriter<uint8_t>::WriteBigEndian(
399 data.data() + VideoSendTiming::kFlagsOffset, timing.flags);
ilnik04f4d122017-06-19 07:18:55 -0700400 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200401 data.data() + VideoSendTiming::kEncodeStartDeltaOffset,
sprangba050a62017-08-18 02:51:12 -0700402 timing.encode_start_delta_ms);
403 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200404 data.data() + VideoSendTiming::kEncodeFinishDeltaOffset,
ilnik04f4d122017-06-19 07:18:55 -0700405 timing.encode_finish_delta_ms);
406 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200407 data.data() + VideoSendTiming::kPacketizationFinishDeltaOffset,
ilnik04f4d122017-06-19 07:18:55 -0700408 timing.packetization_finish_delta_ms);
409 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200410 data.data() + VideoSendTiming::kPacerExitDeltaOffset,
ilnik2edc6842017-07-06 03:06:50 -0700411 timing.pacer_exit_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700412 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200413 data.data() + VideoSendTiming::kNetworkTimestampDeltaOffset,
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100414 timing.network_timestamp_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700415 ByteWriter<uint16_t>::WriteBigEndian(
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200416 data.data() + VideoSendTiming::kNetwork2TimestampDeltaOffset,
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100417 timing.network2_timestamp_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700418 return true;
419}
420
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200421bool VideoTimingExtension::Write(rtc::ArrayView<uint8_t> data,
ilnik04f4d122017-06-19 07:18:55 -0700422 uint16_t time_delta_ms,
sprangba050a62017-08-18 02:51:12 -0700423 uint8_t offset) {
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200424 RTC_DCHECK_GE(data.size(), offset + 2);
Danil Chapovalovf0cc8142017-10-31 17:59:39 +0100425 RTC_DCHECK_LE(offset, kValueSizeBytes - sizeof(uint16_t));
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200426 ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset, time_delta_ms);
ilnik04f4d122017-06-19 07:18:55 -0700427 return true;
428}
429
Johnny Leee0c8b232018-09-11 16:50:49 -0400430// Frame Marking.
431//
432// Meta-information about an RTP stream outside the encrypted media payload,
433// useful for an RTP switch to do codec-agnostic selective forwarding
434// without decrypting the payload.
435//
436// For non-scalable streams:
437// 0 1
438// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
439// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440// | ID | L = 0 |S|E|I|D|0 0 0 0|
441// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
442//
443// For scalable streams:
444// 0 1 2 3
445// 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
446// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
447// | ID | L = 2 |S|E|I|D|B| TID | LID | TL0PICIDX |
448// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
449
450constexpr RTPExtensionType FrameMarkingExtension::kId;
451constexpr const char FrameMarkingExtension::kUri[];
452
453bool FrameMarkingExtension::IsScalable(uint8_t temporal_id, uint8_t layer_id) {
454 return temporal_id != kNoTemporalIdx || layer_id != kNoSpatialIdx;
455}
456
457bool FrameMarkingExtension::Parse(rtc::ArrayView<const uint8_t> data,
458 FrameMarking* frame_marking) {
459 RTC_DCHECK(frame_marking);
460
461 if (data.size() != 1 && data.size() != 3)
462 return false;
463
464 frame_marking->start_of_frame = (data[0] & 0x80) != 0;
465 frame_marking->end_of_frame = (data[0] & 0x40) != 0;
466 frame_marking->independent_frame = (data[0] & 0x20) != 0;
467 frame_marking->discardable_frame = (data[0] & 0x10) != 0;
468
469 if (data.size() == 3) {
470 frame_marking->base_layer_sync = (data[0] & 0x08) != 0;
471 frame_marking->temporal_id = data[0] & 0x7;
472 frame_marking->layer_id = data[1];
473 frame_marking->tl0_pic_idx = data[2];
474 } else {
475 // non-scalable
476 frame_marking->base_layer_sync = false;
477 frame_marking->temporal_id = kNoTemporalIdx;
478 frame_marking->layer_id = kNoSpatialIdx;
479 frame_marking->tl0_pic_idx = 0;
480 }
481 return true;
482}
483
484size_t FrameMarkingExtension::ValueSize(const FrameMarking& frame_marking) {
485 if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id))
486 return 3;
487 else
488 return 1;
489}
490
491bool FrameMarkingExtension::Write(rtc::ArrayView<uint8_t> data,
492 const FrameMarking& frame_marking) {
493 RTC_DCHECK_GE(data.size(), 1);
494 RTC_CHECK_LE(frame_marking.temporal_id, 0x07);
495 data[0] = frame_marking.start_of_frame ? 0x80 : 0x00;
496 data[0] |= frame_marking.end_of_frame ? 0x40 : 0x00;
497 data[0] |= frame_marking.independent_frame ? 0x20 : 0x00;
498 data[0] |= frame_marking.discardable_frame ? 0x10 : 0x00;
499
500 if (IsScalable(frame_marking.temporal_id, frame_marking.layer_id)) {
501 RTC_DCHECK_EQ(data.size(), 3);
502 data[0] |= frame_marking.base_layer_sync ? 0x08 : 0x00;
503 data[0] |= frame_marking.temporal_id & 0x07;
504 data[1] = frame_marking.layer_id;
505 data[2] = frame_marking.tl0_pic_idx;
506 }
507 return true;
508}
509
Johannes Kron09d65882018-11-27 14:36:41 +0100510// Color space including HDR metadata as an optional field.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100511//
Johannes Krond0b69a82018-12-03 14:18:53 +0100512// RTP header extension to carry color space information and optionally HDR
513// metadata. The float values in the HDR metadata struct are upscaled by a
514// static factor and transmitted as unsigned integers.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100515//
Johannes Krond0b69a82018-12-03 14:18:53 +0100516// Data layout of color space with HDR metadata (two-byte RTP header extension)
Johannes Kronad1d9f02018-11-09 11:12:36 +0100517// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100518// 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
519// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100520// | ID | length=28 | primaries | transfer |
Johannes Krond0b69a82018-12-03 14:18:53 +0100521// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100522// | matrix |range+chr.sit. | luminance_max |
Johannes Krond0b69a82018-12-03 14:18:53 +0100523// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100524// | luminance_min | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100525// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100526// |primary_r.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100527// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100528// |primary_g.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100529// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100530// |primary_b.x and .y | mastering_metadata.|
Johannes Krond0b69a82018-12-03 14:18:53 +0100531// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100532// |white.x and .y | max_content_light_level |
Johannes Krond0b69a82018-12-03 14:18:53 +0100533// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100534// | max_frame_average_light_level |
535// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kron09d65882018-11-27 14:36:41 +0100536//
Johannes Krond0b69a82018-12-03 14:18:53 +0100537// Data layout of color space w/o HDR metadata (one-byte RTP header extension)
Johannes Kron09d65882018-11-27 14:36:41 +0100538// 0 1 2 3
Johannes Krond0b69a82018-12-03 14:18:53 +0100539// 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
540// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100541// | ID | L = 3 | primaries | transfer | matrix |
Johannes Krond0b69a82018-12-03 14:18:53 +0100542// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Johannes Kronc13f4be2018-12-12 09:52:53 +0100543// |range+chr.sit. |
Johannes Krond0b69a82018-12-03 14:18:53 +0100544// +-+-+-+-+-+-+-+-+
Johannes Kronad1d9f02018-11-09 11:12:36 +0100545
Johannes Kron09d65882018-11-27 14:36:41 +0100546constexpr RTPExtensionType ColorSpaceExtension::kId;
547constexpr uint8_t ColorSpaceExtension::kValueSizeBytes;
548constexpr const char ColorSpaceExtension::kUri[];
549
550bool ColorSpaceExtension::Parse(rtc::ArrayView<const uint8_t> data,
551 ColorSpace* color_space) {
552 RTC_DCHECK(color_space);
553 if (data.size() != kValueSizeBytes &&
554 data.size() != kValueSizeBytesWithoutHdrMetadata)
Johannes Kronad1d9f02018-11-09 11:12:36 +0100555 return false;
556
557 size_t offset = 0;
Johannes Kron09d65882018-11-27 14:36:41 +0100558 // Read color space information.
Johannes Kronc13f4be2018-12-12 09:52:53 +0100559 if (!color_space->set_primaries_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100560 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100561 if (!color_space->set_transfer_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100562 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100563 if (!color_space->set_matrix_from_uint8(data[offset++]))
Johannes Kron09d65882018-11-27 14:36:41 +0100564 return false;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100565
566 uint8_t range_and_chroma_siting = data[offset++];
567 if (!color_space->set_range_from_uint8((range_and_chroma_siting >> 4) & 0x03))
568 return false;
569 if (!color_space->set_chroma_siting_horizontal_from_uint8(
570 (range_and_chroma_siting >> 2) & 0x03))
571 return false;
572 if (!color_space->set_chroma_siting_vertical_from_uint8(
573 range_and_chroma_siting & 0x03))
Johannes Kron09d65882018-11-27 14:36:41 +0100574 return false;
575
576 // Read HDR metadata if it exists, otherwise clear it.
577 if (data.size() == kValueSizeBytesWithoutHdrMetadata) {
578 color_space->set_hdr_metadata(nullptr);
579 } else {
580 HdrMetadata hdr_metadata;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100581 offset += ParseHdrMetadata(data.subview(offset), &hdr_metadata);
582 if (!hdr_metadata.Validate())
583 return false;
Johannes Kron09d65882018-11-27 14:36:41 +0100584 color_space->set_hdr_metadata(&hdr_metadata);
585 }
586 RTC_DCHECK_EQ(ValueSize(*color_space), offset);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100587 return true;
588}
589
Johannes Kron09d65882018-11-27 14:36:41 +0100590bool ColorSpaceExtension::Write(rtc::ArrayView<uint8_t> data,
591 const ColorSpace& color_space) {
Johannes Krond0b69a82018-12-03 14:18:53 +0100592 RTC_DCHECK_EQ(data.size(), ValueSize(color_space));
Johannes Kronad1d9f02018-11-09 11:12:36 +0100593 size_t offset = 0;
Johannes Kron09d65882018-11-27 14:36:41 +0100594 // Write color space information.
Johannes Kronc13f4be2018-12-12 09:52:53 +0100595 data[offset++] = static_cast<uint8_t>(color_space.primaries());
596 data[offset++] = static_cast<uint8_t>(color_space.transfer());
597 data[offset++] = static_cast<uint8_t>(color_space.matrix());
598 data[offset++] = CombineRangeAndChromaSiting(
599 color_space.range(), color_space.chroma_siting_horizontal(),
600 color_space.chroma_siting_vertical());
Johannes Kronad1d9f02018-11-09 11:12:36 +0100601
Johannes Kron09d65882018-11-27 14:36:41 +0100602 // Write HDR metadata if it exists.
603 if (color_space.hdr_metadata()) {
Johannes Kronc13f4be2018-12-12 09:52:53 +0100604 offset +=
605 WriteHdrMetadata(data.subview(offset), *color_space.hdr_metadata());
Johannes Kron09d65882018-11-27 14:36:41 +0100606 }
607 RTC_DCHECK_EQ(ValueSize(color_space), offset);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100608 return true;
609}
610
Johannes Kronc13f4be2018-12-12 09:52:53 +0100611// Combines range and chroma siting into one byte with the following bit layout:
612// bits 0-1 Chroma siting vertical.
613// 2-3 Chroma siting horizontal.
614// 4-5 Range.
615// 6-7 Unused.
616uint8_t ColorSpaceExtension::CombineRangeAndChromaSiting(
617 ColorSpace::RangeID range,
618 ColorSpace::ChromaSiting chroma_siting_horizontal,
619 ColorSpace::ChromaSiting chroma_siting_vertical) {
620 RTC_DCHECK_LE(static_cast<uint8_t>(range), 3);
621 RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_horizontal), 3);
622 RTC_DCHECK_LE(static_cast<uint8_t>(chroma_siting_vertical), 3);
623 return (static_cast<uint8_t>(range) << 4) |
624 (static_cast<uint8_t>(chroma_siting_horizontal) << 2) |
625 static_cast<uint8_t>(chroma_siting_vertical);
626}
627
628size_t ColorSpaceExtension::ParseHdrMetadata(rtc::ArrayView<const uint8_t> data,
629 HdrMetadata* hdr_metadata) {
630 RTC_DCHECK_EQ(data.size(),
631 kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
632 size_t offset = 0;
633 offset += ParseLuminance(data.data() + offset,
634 &hdr_metadata->mastering_metadata.luminance_max,
635 kLuminanceMaxDenominator);
636 offset += ParseLuminance(data.data() + offset,
637 &hdr_metadata->mastering_metadata.luminance_min,
638 kLuminanceMinDenominator);
639 offset += ParseChromaticity(data.data() + offset,
640 &hdr_metadata->mastering_metadata.primary_r);
641 offset += ParseChromaticity(data.data() + offset,
642 &hdr_metadata->mastering_metadata.primary_g);
643 offset += ParseChromaticity(data.data() + offset,
644 &hdr_metadata->mastering_metadata.primary_b);
645 offset += ParseChromaticity(data.data() + offset,
646 &hdr_metadata->mastering_metadata.white_point);
647 hdr_metadata->max_content_light_level =
648 ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
649 offset += 2;
650 hdr_metadata->max_frame_average_light_level =
651 ByteReader<uint16_t>::ReadBigEndian(data.data() + offset);
652 offset += 2;
653 return offset;
654}
655
Johannes Kron09d65882018-11-27 14:36:41 +0100656size_t ColorSpaceExtension::ParseChromaticity(
Johannes Kronad1d9f02018-11-09 11:12:36 +0100657 const uint8_t* data,
658 HdrMasteringMetadata::Chromaticity* p) {
659 uint16_t chromaticity_x_scaled = ByteReader<uint16_t>::ReadBigEndian(data);
660 uint16_t chromaticity_y_scaled =
661 ByteReader<uint16_t>::ReadBigEndian(data + 2);
662 p->x = static_cast<float>(chromaticity_x_scaled) / kChromaticityDenominator;
663 p->y = static_cast<float>(chromaticity_y_scaled) / kChromaticityDenominator;
664 return 4; // Return number of bytes read.
665}
666
Johannes Kron09d65882018-11-27 14:36:41 +0100667size_t ColorSpaceExtension::ParseLuminance(const uint8_t* data,
668 float* f,
669 int denominator) {
Johannes Kronc13f4be2018-12-12 09:52:53 +0100670 uint16_t luminance_scaled = ByteReader<uint16_t>::ReadBigEndian(data);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100671 *f = static_cast<float>(luminance_scaled) / denominator;
Johannes Kronc13f4be2018-12-12 09:52:53 +0100672 return 2; // Return number of bytes read.
673}
674
675size_t ColorSpaceExtension::WriteHdrMetadata(rtc::ArrayView<uint8_t> data,
676 const HdrMetadata& hdr_metadata) {
677 RTC_DCHECK_EQ(data.size(),
678 kValueSizeBytes - kValueSizeBytesWithoutHdrMetadata);
679 RTC_DCHECK(hdr_metadata.Validate());
680 size_t offset = 0;
681 offset += WriteLuminance(data.data() + offset,
682 hdr_metadata.mastering_metadata.luminance_max,
683 kLuminanceMaxDenominator);
684 offset += WriteLuminance(data.data() + offset,
685 hdr_metadata.mastering_metadata.luminance_min,
686 kLuminanceMinDenominator);
687 offset += WriteChromaticity(data.data() + offset,
688 hdr_metadata.mastering_metadata.primary_r);
689 offset += WriteChromaticity(data.data() + offset,
690 hdr_metadata.mastering_metadata.primary_g);
691 offset += WriteChromaticity(data.data() + offset,
692 hdr_metadata.mastering_metadata.primary_b);
693 offset += WriteChromaticity(data.data() + offset,
694 hdr_metadata.mastering_metadata.white_point);
695
696 ByteWriter<uint16_t>::WriteBigEndian(data.data() + offset,
697 hdr_metadata.max_content_light_level);
698 offset += 2;
699 ByteWriter<uint16_t>::WriteBigEndian(
700 data.data() + offset, hdr_metadata.max_frame_average_light_level);
701 offset += 2;
702 return offset;
Johannes Kronad1d9f02018-11-09 11:12:36 +0100703}
704
Johannes Kron09d65882018-11-27 14:36:41 +0100705size_t ColorSpaceExtension::WriteChromaticity(
Johannes Kronad1d9f02018-11-09 11:12:36 +0100706 uint8_t* data,
707 const HdrMasteringMetadata::Chromaticity& p) {
708 RTC_DCHECK_GE(p.x, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100709 RTC_DCHECK_LE(p.x, 1.0f);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100710 RTC_DCHECK_GE(p.y, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100711 RTC_DCHECK_LE(p.y, 1.0f);
Johannes Kronad1d9f02018-11-09 11:12:36 +0100712 ByteWriter<uint16_t>::WriteBigEndian(
713 data, std::round(p.x * kChromaticityDenominator));
714 ByteWriter<uint16_t>::WriteBigEndian(
715 data + 2, std::round(p.y * kChromaticityDenominator));
716 return 4; // Return number of bytes written.
717}
718
Johannes Kron09d65882018-11-27 14:36:41 +0100719size_t ColorSpaceExtension::WriteLuminance(uint8_t* data,
720 float f,
721 int denominator) {
Johannes Kronad1d9f02018-11-09 11:12:36 +0100722 RTC_DCHECK_GE(f, 0.0f);
Johannes Kronc13f4be2018-12-12 09:52:53 +0100723 float upscaled_value = f * denominator;
724 RTC_DCHECK_LE(upscaled_value, std::numeric_limits<uint16_t>::max());
725 ByteWriter<uint16_t>::WriteBigEndian(data, std::round(upscaled_value));
726 return 2; // Return number of bytes written.
Johannes Kronad1d9f02018-11-09 11:12:36 +0100727}
728
Steve Antona3251dd2017-07-21 09:58:31 -0700729bool BaseRtpStringExtension::Parse(rtc::ArrayView<const uint8_t> data,
Steve Antona3251dd2017-07-21 09:58:31 -0700730 std::string* str) {
731 if (data.empty() || data[0] == 0) // Valid string extension can't be empty.
732 return false;
733 const char* cstr = reinterpret_cast<const char*>(data.data());
734 // If there is a \0 character in the middle of the |data|, treat it as end
735 // of the string. Well-formed string extensions shouldn't contain it.
736 str->assign(cstr, strnlen(cstr, data.size()));
737 RTC_DCHECK(!str->empty());
738 return true;
739}
740
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200741bool BaseRtpStringExtension::Write(rtc::ArrayView<uint8_t> data,
742 const std::string& str) {
Niels Möllerd57efc12019-03-22 14:02:11 +0100743 if (str.size() > kMaxValueSizeBytes) {
Johannes Kron78cdde32018-10-05 10:00:46 +0200744 return false;
745 }
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200746 RTC_DCHECK_EQ(data.size(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700747 RTC_DCHECK_GE(str.size(), 1);
Danil Chapovalov9bf31582018-06-18 13:48:20 +0200748 memcpy(data.data(), str.data(), str.size());
Steve Antona3251dd2017-07-21 09:58:31 -0700749 return true;
750}
751
752// Constant declarations for string RTP header extension types.
753
danilchapef8d7732017-04-19 02:59:48 -0700754constexpr RTPExtensionType RtpStreamId::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700755constexpr const char RtpStreamId::kUri[];
danilchapef8d7732017-04-19 02:59:48 -0700756
danilchapef8d7732017-04-19 02:59:48 -0700757constexpr RTPExtensionType RepairedRtpStreamId::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700758constexpr const char RepairedRtpStreamId::kUri[];
danilchapef8d7732017-04-19 02:59:48 -0700759
Steve Antona3251dd2017-07-21 09:58:31 -0700760constexpr RTPExtensionType RtpMid::kId;
Steve Antond14d9f72017-07-21 10:59:39 -0700761constexpr const char RtpMid::kUri[];
erikvargae6b16192017-05-11 02:36:32 -0700762
danilchap1edb7ab2016-04-20 05:25:10 -0700763} // namespace webrtc