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