1 /*
2 * Copyright 2017 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
11 #include "pc/rtp_parameters_conversion.h"
12
13 #include <cstdint>
14 #include <set>
15 #include <string>
16 #include <type_traits>
17 #include <utility>
18
19 #include "api/array_view.h"
20 #include "api/media_types.h"
21 #include "media/base/media_constants.h"
22 #include "media/base/rtp_utils.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/logging.h"
25 #include "rtc_base/strings/string_builder.h"
26
27 namespace webrtc {
28
ToCricketFeedbackParam(const RtcpFeedback & feedback)29 RTCErrorOr<cricket::FeedbackParam> ToCricketFeedbackParam(
30 const RtcpFeedback& feedback) {
31 switch (feedback.type) {
32 case RtcpFeedbackType::CCM:
33 if (!feedback.message_type) {
34 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
35 "Missing message type in CCM RtcpFeedback.");
36 } else if (*feedback.message_type != RtcpFeedbackMessageType::FIR) {
37 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
38 "Invalid message type in CCM RtcpFeedback.");
39 }
40 return cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
41 cricket::kRtcpFbCcmParamFir);
42 case RtcpFeedbackType::LNTF:
43 if (feedback.message_type) {
44 LOG_AND_RETURN_ERROR(
45 RTCErrorType::INVALID_PARAMETER,
46 "Didn't expect message type in LNTF RtcpFeedback.");
47 }
48 return cricket::FeedbackParam(cricket::kRtcpFbParamLntf);
49 case RtcpFeedbackType::NACK:
50 if (!feedback.message_type) {
51 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
52 "Missing message type in NACK RtcpFeedback.");
53 }
54 switch (*feedback.message_type) {
55 case RtcpFeedbackMessageType::GENERIC_NACK:
56 return cricket::FeedbackParam(cricket::kRtcpFbParamNack);
57 case RtcpFeedbackMessageType::PLI:
58 return cricket::FeedbackParam(cricket::kRtcpFbParamNack,
59 cricket::kRtcpFbNackParamPli);
60 default:
61 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
62 "Invalid message type in NACK RtcpFeedback.");
63 }
64 case RtcpFeedbackType::REMB:
65 if (feedback.message_type) {
66 LOG_AND_RETURN_ERROR(
67 RTCErrorType::INVALID_PARAMETER,
68 "Didn't expect message type in REMB RtcpFeedback.");
69 }
70 return cricket::FeedbackParam(cricket::kRtcpFbParamRemb);
71 case RtcpFeedbackType::TRANSPORT_CC:
72 if (feedback.message_type) {
73 LOG_AND_RETURN_ERROR(
74 RTCErrorType::INVALID_PARAMETER,
75 "Didn't expect message type in transport-cc RtcpFeedback.");
76 }
77 return cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc);
78 }
79 RTC_CHECK_NOTREACHED();
80 }
81
82 template <typename C>
83 static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
84 C* cricket_codec);
85
86 template <>
ToCricketCodecTypeSpecific(const RtpCodecParameters & codec,cricket::AudioCodec * cricket_codec)87 RTCError ToCricketCodecTypeSpecific<cricket::AudioCodec>(
88 const RtpCodecParameters& codec,
89 cricket::AudioCodec* cricket_codec) {
90 if (codec.kind != cricket::MEDIA_TYPE_AUDIO) {
91 LOG_AND_RETURN_ERROR(
92 RTCErrorType::INVALID_PARAMETER,
93 "Can't use video codec with audio sender or receiver.");
94 }
95 if (!codec.num_channels) {
96 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
97 "Missing number of channels for audio codec.");
98 }
99 if (*codec.num_channels <= 0) {
100 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
101 "Number of channels must be positive.");
102 }
103 cricket_codec->channels = *codec.num_channels;
104 if (!codec.clock_rate) {
105 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
106 "Missing codec clock rate.");
107 }
108 if (*codec.clock_rate <= 0) {
109 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
110 "Clock rate must be positive.");
111 }
112 cricket_codec->clockrate = *codec.clock_rate;
113 return RTCError::OK();
114 }
115
116 // Video codecs don't use num_channels or clock_rate, but they should at least
117 // be validated to ensure the application isn't trying to do something it
118 // doesn't intend to.
119 template <>
ToCricketCodecTypeSpecific(const RtpCodecParameters & codec,cricket::VideoCodec *)120 RTCError ToCricketCodecTypeSpecific<cricket::VideoCodec>(
121 const RtpCodecParameters& codec,
122 cricket::VideoCodec*) {
123 if (codec.kind != cricket::MEDIA_TYPE_VIDEO) {
124 LOG_AND_RETURN_ERROR(
125 RTCErrorType::INVALID_PARAMETER,
126 "Can't use audio codec with video sender or receiver.");
127 }
128 if (codec.num_channels) {
129 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
130 "Video codec shouldn't have num_channels.");
131 }
132 if (!codec.clock_rate) {
133 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
134 "Missing codec clock rate.");
135 }
136 if (*codec.clock_rate != cricket::kVideoCodecClockrate) {
137 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
138 "Video clock rate must be 90000.");
139 }
140 return RTCError::OK();
141 }
142
143 template <typename C>
ToCricketCodec(const RtpCodecParameters & codec)144 RTCErrorOr<C> ToCricketCodec(const RtpCodecParameters& codec) {
145 C cricket_codec;
146 // Start with audio/video specific conversion.
147 RTCError err = ToCricketCodecTypeSpecific(codec, &cricket_codec);
148 if (!err.ok()) {
149 return std::move(err);
150 }
151 cricket_codec.name = codec.name;
152 if (!cricket::IsValidRtpPayloadType(codec.payload_type)) {
153 char buf[40];
154 rtc::SimpleStringBuilder sb(buf);
155 sb << "Invalid payload type: " << codec.payload_type;
156 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, sb.str());
157 }
158 cricket_codec.id = codec.payload_type;
159 for (const RtcpFeedback& feedback : codec.rtcp_feedback) {
160 auto result = ToCricketFeedbackParam(feedback);
161 if (!result.ok()) {
162 return result.MoveError();
163 }
164 cricket_codec.AddFeedbackParam(result.MoveValue());
165 }
166 cricket_codec.params = codec.parameters;
167 return std::move(cricket_codec);
168 }
169
170 template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
171 const RtpCodecParameters& codec);
172 template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
173 const RtpCodecParameters& codec);
174
175 template <typename C>
ToCricketCodecs(const std::vector<RtpCodecParameters> & codecs)176 RTCErrorOr<std::vector<C>> ToCricketCodecs(
177 const std::vector<RtpCodecParameters>& codecs) {
178 std::vector<C> cricket_codecs;
179 std::set<int> seen_payload_types;
180 for (const RtpCodecParameters& codec : codecs) {
181 auto result = ToCricketCodec<C>(codec);
182 if (!result.ok()) {
183 return result.MoveError();
184 }
185 if (!seen_payload_types.insert(codec.payload_type).second) {
186 char buf[40];
187 rtc::SimpleStringBuilder sb(buf);
188 sb << "Duplicate payload type: " << codec.payload_type;
189 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, sb.str());
190 }
191 cricket_codecs.push_back(result.MoveValue());
192 }
193 return std::move(cricket_codecs);
194 }
195
196 template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
197 cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
198
199 template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
200 cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
201
ToCricketStreamParamsVec(const std::vector<RtpEncodingParameters> & encodings)202 RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
203 const std::vector<RtpEncodingParameters>& encodings) {
204 if (encodings.size() > 1u) {
205 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
206 "ORTC API implementation doesn't currently "
207 "support simulcast or layered encodings.");
208 } else if (encodings.empty()) {
209 return cricket::StreamParamsVec();
210 }
211 cricket::StreamParamsVec cricket_streams;
212 const RtpEncodingParameters& encoding = encodings[0];
213 if (encoding.ssrc) {
214 cricket::StreamParams stream_params;
215 stream_params.add_ssrc(*encoding.ssrc);
216 cricket_streams.push_back(std::move(stream_params));
217 }
218 return std::move(cricket_streams);
219 }
220
ToRtcpFeedback(const cricket::FeedbackParam & cricket_feedback)221 absl::optional<RtcpFeedback> ToRtcpFeedback(
222 const cricket::FeedbackParam& cricket_feedback) {
223 if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
224 if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
225 return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
226 } else {
227 RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
228 << cricket_feedback.param();
229 return absl::nullopt;
230 }
231 } else if (cricket_feedback.id() == cricket::kRtcpFbParamLntf) {
232 if (cricket_feedback.param().empty()) {
233 return RtcpFeedback(RtcpFeedbackType::LNTF);
234 } else {
235 RTC_LOG(LS_WARNING) << "Unsupported parameter for LNTF RTCP feedback: "
236 << cricket_feedback.param();
237 return absl::nullopt;
238 }
239 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
240 if (cricket_feedback.param().empty()) {
241 return RtcpFeedback(RtcpFeedbackType::NACK,
242 RtcpFeedbackMessageType::GENERIC_NACK);
243 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
244 return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
245 } else {
246 RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
247 << cricket_feedback.param();
248 return absl::nullopt;
249 }
250 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
251 if (!cricket_feedback.param().empty()) {
252 RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
253 << cricket_feedback.param();
254 return absl::nullopt;
255 } else {
256 return RtcpFeedback(RtcpFeedbackType::REMB);
257 }
258 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
259 if (!cricket_feedback.param().empty()) {
260 RTC_LOG(LS_WARNING)
261 << "Unsupported parameter for transport-cc RTCP feedback: "
262 << cricket_feedback.param();
263 return absl::nullopt;
264 } else {
265 return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
266 }
267 }
268 RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
269 << cricket_feedback.id();
270 return absl::nullopt;
271 }
272
ToRtpEncodings(const cricket::StreamParamsVec & stream_params)273 std::vector<RtpEncodingParameters> ToRtpEncodings(
274 const cricket::StreamParamsVec& stream_params) {
275 std::vector<RtpEncodingParameters> rtp_encodings;
276 for (const cricket::StreamParams& stream_param : stream_params) {
277 RtpEncodingParameters rtp_encoding;
278 rtp_encoding.ssrc.emplace(stream_param.first_ssrc());
279 rtp_encodings.push_back(std::move(rtp_encoding));
280 }
281 return rtp_encodings;
282 }
283
284 template <typename C>
285 cricket::MediaType KindOfCodec();
286
287 template <>
KindOfCodec()288 cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
289 return cricket::MEDIA_TYPE_AUDIO;
290 }
291
292 template <>
KindOfCodec()293 cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
294 return cricket::MEDIA_TYPE_VIDEO;
295 }
296
297 template <typename C>
298 static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
299 RtpCodecCapability* codec);
300
301 template <>
ToRtpCodecCapabilityTypeSpecific(const cricket::AudioCodec & cricket_codec,RtpCodecCapability * codec)302 void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
303 const cricket::AudioCodec& cricket_codec,
304 RtpCodecCapability* codec) {
305 codec->num_channels = static_cast<int>(cricket_codec.channels);
306 }
307
308 template <>
ToRtpCodecCapabilityTypeSpecific(const cricket::VideoCodec & cricket_codec,RtpCodecCapability * codec)309 void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
310 const cricket::VideoCodec& cricket_codec,
311 RtpCodecCapability* codec) {
312 if (cricket_codec.scalability_modes.empty() ||
313 (cricket_codec.scalability_modes.size() == 1 &&
314 cricket_codec.scalability_modes[0] == ScalabilityMode::kL1T1)) {
315 // https://w3c.github.io/webrtc-svc/#dom-rtcrtpcodeccapability-scalabilitymodes
316 // If a codec does not support encoding of scalability modes other than
317 // "L1T1", then the scalabilityModes member is not provided.
318 return;
319 }
320
321 codec->scalability_modes = cricket_codec.scalability_modes;
322 }
323
324 template <typename C>
ToRtpCodecCapability(const C & cricket_codec)325 RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
326 RtpCodecCapability codec;
327 codec.name = cricket_codec.name;
328 codec.kind = KindOfCodec<C>();
329 codec.clock_rate.emplace(cricket_codec.clockrate);
330 codec.preferred_payload_type.emplace(cricket_codec.id);
331 for (const cricket::FeedbackParam& cricket_feedback :
332 cricket_codec.feedback_params.params()) {
333 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
334 if (feedback) {
335 codec.rtcp_feedback.push_back(feedback.value());
336 }
337 }
338 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
339 codec.parameters.insert(cricket_codec.params.begin(),
340 cricket_codec.params.end());
341 return codec;
342 }
343
344 template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
345 const cricket::AudioCodec& cricket_codec);
346 template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
347 const cricket::VideoCodec& cricket_codec);
348
349 template <typename C>
350 static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
351 RtpCodecParameters* codec);
352 template <>
ToRtpCodecParametersTypeSpecific(const cricket::AudioCodec & cricket_codec,RtpCodecParameters * codec)353 void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
354 const cricket::AudioCodec& cricket_codec,
355 RtpCodecParameters* codec) {
356 codec->num_channels = static_cast<int>(cricket_codec.channels);
357 }
358
359 template <>
ToRtpCodecParametersTypeSpecific(const cricket::VideoCodec & cricket_codec,RtpCodecParameters * codec)360 void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
361 const cricket::VideoCodec& cricket_codec,
362 RtpCodecParameters* codec) {}
363
364 template <typename C>
ToRtpCodecParameters(const C & cricket_codec)365 RtpCodecParameters ToRtpCodecParameters(const C& cricket_codec) {
366 RtpCodecParameters codec_param;
367 codec_param.name = cricket_codec.name;
368 codec_param.kind = KindOfCodec<C>();
369 codec_param.clock_rate.emplace(cricket_codec.clockrate);
370 codec_param.payload_type = cricket_codec.id;
371 for (const cricket::FeedbackParam& cricket_feedback :
372 cricket_codec.feedback_params.params()) {
373 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
374 if (feedback) {
375 codec_param.rtcp_feedback.push_back(feedback.value());
376 }
377 }
378 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
379 codec_param.parameters = cricket_codec.params;
380 return codec_param;
381 }
382
383 template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
384 const cricket::AudioCodec& cricket_codec);
385 template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
386 const cricket::VideoCodec& cricket_codec);
387
388 template <class C>
ToRtpCapabilities(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions)389 RtpCapabilities ToRtpCapabilities(
390 const std::vector<C>& cricket_codecs,
391 const cricket::RtpHeaderExtensions& cricket_extensions) {
392 RtpCapabilities capabilities;
393 bool have_red = false;
394 bool have_ulpfec = false;
395 bool have_flexfec = false;
396 bool have_rtx = false;
397 for (const C& cricket_codec : cricket_codecs) {
398 if (cricket_codec.name == cricket::kRedCodecName) {
399 have_red = true;
400 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
401 have_ulpfec = true;
402 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
403 have_flexfec = true;
404 } else if (cricket_codec.name == cricket::kRtxCodecName) {
405 if (have_rtx) {
406 // There should only be one RTX codec entry
407 continue;
408 }
409 have_rtx = true;
410 }
411 auto codec_capability = ToRtpCodecCapability(cricket_codec);
412 if (cricket_codec.name == cricket::kRtxCodecName) {
413 // RTX codec should not have any parameter
414 codec_capability.parameters.clear();
415 }
416 capabilities.codecs.push_back(codec_capability);
417 }
418 for (const RtpExtension& cricket_extension : cricket_extensions) {
419 capabilities.header_extensions.emplace_back(cricket_extension.uri,
420 cricket_extension.id);
421 }
422 if (have_red) {
423 capabilities.fec.push_back(FecMechanism::RED);
424 }
425 if (have_red && have_ulpfec) {
426 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
427 }
428 if (have_flexfec) {
429 capabilities.fec.push_back(FecMechanism::FLEXFEC);
430 }
431 return capabilities;
432 }
433
434 template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
435 const std::vector<cricket::AudioCodec>& cricket_codecs,
436 const cricket::RtpHeaderExtensions& cricket_extensions);
437 template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
438 const std::vector<cricket::VideoCodec>& cricket_codecs,
439 const cricket::RtpHeaderExtensions& cricket_extensions);
440
441 template <class C>
ToRtpParameters(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions,const cricket::StreamParamsVec & stream_params)442 RtpParameters ToRtpParameters(
443 const std::vector<C>& cricket_codecs,
444 const cricket::RtpHeaderExtensions& cricket_extensions,
445 const cricket::StreamParamsVec& stream_params) {
446 RtpParameters rtp_parameters;
447 for (const C& cricket_codec : cricket_codecs) {
448 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
449 }
450 for (const RtpExtension& cricket_extension : cricket_extensions) {
451 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
452 cricket_extension.id);
453 }
454 rtp_parameters.encodings = ToRtpEncodings(stream_params);
455 return rtp_parameters;
456 }
457
458 template RtpParameters ToRtpParameters<cricket::AudioCodec>(
459 const std::vector<cricket::AudioCodec>& cricket_codecs,
460 const cricket::RtpHeaderExtensions& cricket_extensions,
461 const cricket::StreamParamsVec& stream_params);
462 template RtpParameters ToRtpParameters<cricket::VideoCodec>(
463 const std::vector<cricket::VideoCodec>& cricket_codecs,
464 const cricket::RtpHeaderExtensions& cricket_extensions,
465 const cricket::StreamParamsVec& stream_params);
466
467 } // namespace webrtc
468