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 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
313 template <typename C>
ToRtpCodecCapability(const C & cricket_codec)314 RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
315 RtpCodecCapability codec;
316 codec.name = cricket_codec.name;
317 codec.kind = KindOfCodec<C>();
318 codec.clock_rate.emplace(cricket_codec.clockrate);
319 codec.preferred_payload_type.emplace(cricket_codec.id);
320 for (const cricket::FeedbackParam& cricket_feedback :
321 cricket_codec.feedback_params.params()) {
322 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
323 if (feedback) {
324 codec.rtcp_feedback.push_back(feedback.value());
325 }
326 }
327 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
328 codec.parameters.insert(cricket_codec.params.begin(),
329 cricket_codec.params.end());
330 return codec;
331 }
332
333 template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
334 const cricket::AudioCodec& cricket_codec);
335 template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
336 const cricket::VideoCodec& cricket_codec);
337
338 template <typename C>
339 static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
340 RtpCodecParameters* codec);
341 template <>
ToRtpCodecParametersTypeSpecific(const cricket::AudioCodec & cricket_codec,RtpCodecParameters * codec)342 void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
343 const cricket::AudioCodec& cricket_codec,
344 RtpCodecParameters* codec) {
345 codec->num_channels = static_cast<int>(cricket_codec.channels);
346 }
347
348 template <>
ToRtpCodecParametersTypeSpecific(const cricket::VideoCodec & cricket_codec,RtpCodecParameters * codec)349 void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
350 const cricket::VideoCodec& cricket_codec,
351 RtpCodecParameters* codec) {}
352
353 template <typename C>
ToRtpCodecParameters(const C & cricket_codec)354 RtpCodecParameters ToRtpCodecParameters(const C& cricket_codec) {
355 RtpCodecParameters codec_param;
356 codec_param.name = cricket_codec.name;
357 codec_param.kind = KindOfCodec<C>();
358 codec_param.clock_rate.emplace(cricket_codec.clockrate);
359 codec_param.payload_type = cricket_codec.id;
360 for (const cricket::FeedbackParam& cricket_feedback :
361 cricket_codec.feedback_params.params()) {
362 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
363 if (feedback) {
364 codec_param.rtcp_feedback.push_back(feedback.value());
365 }
366 }
367 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
368 codec_param.parameters = cricket_codec.params;
369 return codec_param;
370 }
371
372 template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
373 const cricket::AudioCodec& cricket_codec);
374 template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
375 const cricket::VideoCodec& cricket_codec);
376
377 template <class C>
ToRtpCapabilities(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions)378 RtpCapabilities ToRtpCapabilities(
379 const std::vector<C>& cricket_codecs,
380 const cricket::RtpHeaderExtensions& cricket_extensions) {
381 RtpCapabilities capabilities;
382 bool have_red = false;
383 bool have_ulpfec = false;
384 bool have_flexfec = false;
385 bool have_rtx = false;
386 for (const C& cricket_codec : cricket_codecs) {
387 if (cricket_codec.name == cricket::kRedCodecName) {
388 have_red = true;
389 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
390 have_ulpfec = true;
391 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
392 have_flexfec = true;
393 } else if (cricket_codec.name == cricket::kRtxCodecName) {
394 if (have_rtx) {
395 // There should only be one RTX codec entry
396 continue;
397 }
398 have_rtx = true;
399 }
400 auto codec_capability = ToRtpCodecCapability(cricket_codec);
401 if (cricket_codec.name == cricket::kRtxCodecName) {
402 // RTX codec should not have any parameter
403 codec_capability.parameters.clear();
404 }
405 capabilities.codecs.push_back(codec_capability);
406 }
407 for (const RtpExtension& cricket_extension : cricket_extensions) {
408 capabilities.header_extensions.emplace_back(cricket_extension.uri,
409 cricket_extension.id);
410 }
411 if (have_red) {
412 capabilities.fec.push_back(FecMechanism::RED);
413 }
414 if (have_red && have_ulpfec) {
415 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
416 }
417 if (have_flexfec) {
418 capabilities.fec.push_back(FecMechanism::FLEXFEC);
419 }
420 return capabilities;
421 }
422
423 template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
424 const std::vector<cricket::AudioCodec>& cricket_codecs,
425 const cricket::RtpHeaderExtensions& cricket_extensions);
426 template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
427 const std::vector<cricket::VideoCodec>& cricket_codecs,
428 const cricket::RtpHeaderExtensions& cricket_extensions);
429
430 template <class C>
ToRtpParameters(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions,const cricket::StreamParamsVec & stream_params)431 RtpParameters ToRtpParameters(
432 const std::vector<C>& cricket_codecs,
433 const cricket::RtpHeaderExtensions& cricket_extensions,
434 const cricket::StreamParamsVec& stream_params) {
435 RtpParameters rtp_parameters;
436 for (const C& cricket_codec : cricket_codecs) {
437 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
438 }
439 for (const RtpExtension& cricket_extension : cricket_extensions) {
440 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
441 cricket_extension.id);
442 }
443 rtp_parameters.encodings = ToRtpEncodings(stream_params);
444 return rtp_parameters;
445 }
446
447 template RtpParameters ToRtpParameters<cricket::AudioCodec>(
448 const std::vector<cricket::AudioCodec>& cricket_codecs,
449 const cricket::RtpHeaderExtensions& cricket_extensions,
450 const cricket::StreamParamsVec& stream_params);
451 template RtpParameters ToRtpParameters<cricket::VideoCodec>(
452 const std::vector<cricket::VideoCodec>& cricket_codecs,
453 const cricket::RtpHeaderExtensions& cricket_extensions,
454 const cricket::StreamParamsVec& stream_params);
455
456 } // namespace webrtc
457