blob: 8d3064ed93704b0499f98e503d7691463a952a12 [file] [log] [blame]
deadbeefe814a0d2017-02-26 02:15:091/*
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
Steve Anton10542f22019-01-11 17:11:0011#include "pc/rtp_parameters_conversion.h"
deadbeefe814a0d2017-02-26 02:15:0912
Harald Alvestrand5761e7b2021-01-29 14:45:0813#include <algorithm>
Yves Gerey3e707812018-11-28 15:47:4914#include <cstdint>
deadbeefe814a0d2017-02-26 02:15:0915#include <set>
Yves Gerey3e707812018-11-28 15:47:4916#include <string>
deadbeefe814a0d2017-02-26 02:15:0917#include <utility>
18
Yves Gerey3e707812018-11-28 15:47:4919#include "api/array_view.h"
Steve Anton10542f22019-01-11 17:11:0020#include "api/media_types.h"
21#include "media/base/media_constants.h"
22#include "media/base/rtp_utils.h"
Yves Gerey3e707812018-11-28 15:47:4923#include "rtc_base/checks.h"
24#include "rtc_base/logging.h"
25#include "rtc_base/strings/string_builder.h"
deadbeefe814a0d2017-02-26 02:15:0926
27namespace webrtc {
28
29RTCErrorOr<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);
Elad Alonfadb1812019-05-24 11:40:0242 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);
deadbeefe814a0d2017-02-26 02:15:0949 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 }
Karl Wibergc95b9392020-11-07 23:49:3779 RTC_CHECK_NOTREACHED();
deadbeefe814a0d2017-02-26 02:15:0980}
81
82template <typename C>
83static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
84 C* cricket_codec);
85
86template <>
87RTCError 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.
119template <>
120RTCError 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
143template <typename C>
144RTCErrorOr<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)) {
Florent Castelli72b751a2018-06-28 12:09:33153 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());
deadbeefe814a0d2017-02-26 02:15:09157 }
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 }
Johannes Kron72d69152020-02-10 13:05:55166 cricket_codec.params = codec.parameters;
deadbeefe814a0d2017-02-26 02:15:09167 return std::move(cricket_codec);
168}
169
170template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
171 const RtpCodecParameters& codec);
172template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
173 const RtpCodecParameters& codec);
174
175template <typename C>
176RTCErrorOr<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) {
Florent Castelli72b751a2018-06-28 12:09:33186 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());
deadbeefe814a0d2017-02-26 02:15:09190 }
191 cricket_codecs.push_back(result.MoveValue());
192 }
193 return std::move(cricket_codecs);
194}
195
196template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
197 cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
198
199template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
200 cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
201
deadbeefe814a0d2017-02-26 02:15:09202RTCErrorOr<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];
deadbeefe814a0d2017-02-26 02:15:09213 if (encoding.ssrc) {
214 cricket::StreamParams stream_params;
215 stream_params.add_ssrc(*encoding.ssrc);
deadbeefe814a0d2017-02-26 02:15:09216 cricket_streams.push_back(std::move(stream_params));
217 }
218 return std::move(cricket_streams);
219}
220
Danil Chapovalov00c718362018-06-15 13:58:38221absl::optional<RtcpFeedback> ToRtcpFeedback(
deadbeefe814a0d2017-02-26 02:15:09222 const cricket::FeedbackParam& cricket_feedback) {
223 if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
224 if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
Oskar Sundbomff610bd2017-11-16 09:57:44225 return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
deadbeefe814a0d2017-02-26 02:15:09226 } else {
Mirko Bonadei675513b2017-11-09 10:09:25227 RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
228 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38229 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09230 }
Elad Alonfadb1812019-05-24 11:40:02231 } 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 }
deadbeefe814a0d2017-02-26 02:15:09239 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
240 if (cricket_feedback.param().empty()) {
Oskar Sundbomff610bd2017-11-16 09:57:44241 return RtcpFeedback(RtcpFeedbackType::NACK,
242 RtcpFeedbackMessageType::GENERIC_NACK);
deadbeefe814a0d2017-02-26 02:15:09243 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
Oskar Sundbomff610bd2017-11-16 09:57:44244 return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
deadbeefe814a0d2017-02-26 02:15:09245 } else {
Mirko Bonadei675513b2017-11-09 10:09:25246 RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
247 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38248 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09249 }
250 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
251 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 10:09:25252 RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
253 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38254 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09255 } else {
Oskar Sundbomff610bd2017-11-16 09:57:44256 return RtcpFeedback(RtcpFeedbackType::REMB);
deadbeefe814a0d2017-02-26 02:15:09257 }
258 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
259 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 10:09:25260 RTC_LOG(LS_WARNING)
deadbeefe814a0d2017-02-26 02:15:09261 << "Unsupported parameter for transport-cc RTCP feedback: "
262 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38263 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09264 } else {
Oskar Sundbomff610bd2017-11-16 09:57:44265 return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
deadbeefe814a0d2017-02-26 02:15:09266 }
267 }
Mirko Bonadei675513b2017-11-09 10:09:25268 RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
269 << cricket_feedback.id();
Danil Chapovalov00c718362018-06-15 13:58:38270 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09271}
272
zhihuang24366392017-03-09 01:15:06273std::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());
zhihuang24366392017-03-09 01:15:06279 rtp_encodings.push_back(std::move(rtp_encoding));
280 }
281 return rtp_encodings;
282}
283
deadbeefe814a0d2017-02-26 02:15:09284template <typename C>
285cricket::MediaType KindOfCodec();
286
287template <>
288cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
289 return cricket::MEDIA_TYPE_AUDIO;
290}
291
292template <>
293cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
294 return cricket::MEDIA_TYPE_VIDEO;
295}
296
297template <typename C>
298static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
299 RtpCodecCapability* codec);
300
301template <>
302void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
303 const cricket::AudioCodec& cricket_codec,
304 RtpCodecCapability* codec) {
Oskar Sundbomff610bd2017-11-16 09:57:44305 codec->num_channels = static_cast<int>(cricket_codec.channels);
deadbeefe814a0d2017-02-26 02:15:09306}
307
308template <>
309void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
310 const cricket::VideoCodec& cricket_codec,
311 RtpCodecCapability* codec) {}
312
313template <typename C>
314RtpCodecCapability 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()) {
Danil Chapovalov00c718362018-06-15 13:58:38322 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
deadbeefe814a0d2017-02-26 02:15:09323 if (feedback) {
Danil Chapovalov57ff2732018-03-28 09:25:15324 codec.rtcp_feedback.push_back(feedback.value());
deadbeefe814a0d2017-02-26 02:15:09325 }
326 }
327 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
328 codec.parameters.insert(cricket_codec.params.begin(),
329 cricket_codec.params.end());
330 return codec;
331}
332
333template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
334 const cricket::AudioCodec& cricket_codec);
335template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
336 const cricket::VideoCodec& cricket_codec);
337
zhihuang24366392017-03-09 01:15:06338template <typename C>
339static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
340 RtpCodecParameters* codec);
341template <>
342void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
343 const cricket::AudioCodec& cricket_codec,
344 RtpCodecParameters* codec) {
Oskar Sundbomff610bd2017-11-16 09:57:44345 codec->num_channels = static_cast<int>(cricket_codec.channels);
zhihuang24366392017-03-09 01:15:06346}
347
348template <>
349void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
350 const cricket::VideoCodec& cricket_codec,
351 RtpCodecParameters* codec) {}
352
353template <typename C>
354RtpCodecParameters 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()) {
Danil Chapovalov00c718362018-06-15 13:58:38362 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
zhihuang24366392017-03-09 01:15:06363 if (feedback) {
Danil Chapovalov57ff2732018-03-28 09:25:15364 codec_param.rtcp_feedback.push_back(feedback.value());
zhihuang24366392017-03-09 01:15:06365 }
366 }
367 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
Johannes Kron72d69152020-02-10 13:05:55368 codec_param.parameters = cricket_codec.params;
zhihuang24366392017-03-09 01:15:06369 return codec_param;
370}
371
372template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
373 const cricket::AudioCodec& cricket_codec);
374template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
375 const cricket::VideoCodec& cricket_codec);
376
deadbeefe814a0d2017-02-26 02:15:09377template <class C>
378RtpCapabilities 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;
Florent Castelli5473a452018-11-06 16:27:21385 bool have_rtx = false;
deadbeefe814a0d2017-02-26 02:15:09386 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;
Florent Castelli5473a452018-11-06 16:27:21393 } 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;
deadbeefe814a0d2017-02-26 02:15:09399 }
Florent Castelli5473a452018-11-06 16:27:21400 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);
deadbeefe814a0d2017-02-26 02:15:09406 }
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
423template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
424 const std::vector<cricket::AudioCodec>& cricket_codecs,
425 const cricket::RtpHeaderExtensions& cricket_extensions);
426template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
427 const std::vector<cricket::VideoCodec>& cricket_codecs,
428 const cricket::RtpHeaderExtensions& cricket_extensions);
429
zhihuang24366392017-03-09 01:15:06430template <class C>
431RtpParameters 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
447template RtpParameters ToRtpParameters<cricket::AudioCodec>(
448 const std::vector<cricket::AudioCodec>& cricket_codecs,
449 const cricket::RtpHeaderExtensions& cricket_extensions,
450 const cricket::StreamParamsVec& stream_params);
451template 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
deadbeefe814a0d2017-02-26 02:15:09456} // namespace webrtc