blob: 2463cefe58d0e356b27a8e967ca9384e14557a3f [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
Yves Gerey3e707812018-11-28 15:47:4913#include <cstdint>
deadbeefe814a0d2017-02-26 02:15:0914#include <set>
Yves Gerey3e707812018-11-28 15:47:4915#include <string>
Harald Alvestrandc24a2182022-02-23 13:44:5916#include <type_traits>
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"
Florent Castellid0b8e8e2023-06-04 23:22:3621#include "api/rtc_error.h"
Florent Castelli8c4b9ea2023-06-02 16:06:5722#include "media/base/codec.h"
Steve Anton10542f22019-01-11 17:11:0023#include "media/base/media_constants.h"
24#include "media/base/rtp_utils.h"
Yves Gerey3e707812018-11-28 15:47:4925#include "rtc_base/checks.h"
26#include "rtc_base/logging.h"
27#include "rtc_base/strings/string_builder.h"
deadbeefe814a0d2017-02-26 02:15:0928
29namespace webrtc {
30
31RTCErrorOr<cricket::FeedbackParam> ToCricketFeedbackParam(
32 const RtcpFeedback& feedback) {
33 switch (feedback.type) {
34 case RtcpFeedbackType::CCM:
35 if (!feedback.message_type) {
36 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
37 "Missing message type in CCM RtcpFeedback.");
38 } else if (*feedback.message_type != RtcpFeedbackMessageType::FIR) {
39 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
40 "Invalid message type in CCM RtcpFeedback.");
41 }
42 return cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
43 cricket::kRtcpFbCcmParamFir);
Elad Alonfadb1812019-05-24 11:40:0244 case RtcpFeedbackType::LNTF:
45 if (feedback.message_type) {
46 LOG_AND_RETURN_ERROR(
47 RTCErrorType::INVALID_PARAMETER,
48 "Didn't expect message type in LNTF RtcpFeedback.");
49 }
50 return cricket::FeedbackParam(cricket::kRtcpFbParamLntf);
deadbeefe814a0d2017-02-26 02:15:0951 case RtcpFeedbackType::NACK:
52 if (!feedback.message_type) {
53 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
54 "Missing message type in NACK RtcpFeedback.");
55 }
56 switch (*feedback.message_type) {
57 case RtcpFeedbackMessageType::GENERIC_NACK:
58 return cricket::FeedbackParam(cricket::kRtcpFbParamNack);
59 case RtcpFeedbackMessageType::PLI:
60 return cricket::FeedbackParam(cricket::kRtcpFbParamNack,
61 cricket::kRtcpFbNackParamPli);
62 default:
63 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
64 "Invalid message type in NACK RtcpFeedback.");
65 }
66 case RtcpFeedbackType::REMB:
67 if (feedback.message_type) {
68 LOG_AND_RETURN_ERROR(
69 RTCErrorType::INVALID_PARAMETER,
70 "Didn't expect message type in REMB RtcpFeedback.");
71 }
72 return cricket::FeedbackParam(cricket::kRtcpFbParamRemb);
73 case RtcpFeedbackType::TRANSPORT_CC:
74 if (feedback.message_type) {
75 LOG_AND_RETURN_ERROR(
76 RTCErrorType::INVALID_PARAMETER,
77 "Didn't expect message type in transport-cc RtcpFeedback.");
78 }
79 return cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc);
80 }
Karl Wibergc95b9392020-11-07 23:49:3781 RTC_CHECK_NOTREACHED();
deadbeefe814a0d2017-02-26 02:15:0982}
83
Florent Castellid0b8e8e2023-06-04 23:22:3684RTCErrorOr<cricket::Codec> ToCricketCodec(const RtpCodecParameters& codec) {
85 switch (codec.kind) {
86 case cricket::MEDIA_TYPE_AUDIO:
87 if (codec.kind != cricket::MEDIA_TYPE_AUDIO) {
88 LOG_AND_RETURN_ERROR(
89 RTCErrorType::INVALID_PARAMETER,
90 "Can't use video codec with audio sender or receiver.");
91 }
92 if (!codec.num_channels) {
93 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
94 "Missing number of channels for audio codec.");
95 }
96 if (*codec.num_channels <= 0) {
97 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
98 "Number of channels must be positive.");
99 }
100 if (!codec.clock_rate) {
101 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
102 "Missing codec clock rate.");
103 }
104 if (*codec.clock_rate <= 0) {
105 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
106 "Clock rate must be positive.");
107 }
108 break;
109 case cricket::MEDIA_TYPE_VIDEO:
110 if (codec.kind != cricket::MEDIA_TYPE_VIDEO) {
111 LOG_AND_RETURN_ERROR(
112 RTCErrorType::INVALID_PARAMETER,
113 "Can't use audio codec with video sender or receiver.");
114 }
115 if (codec.num_channels) {
116 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
117 "Video codec shouldn't have num_channels.");
118 }
119 if (!codec.clock_rate) {
120 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
121 "Missing codec clock rate.");
122 }
123 if (*codec.clock_rate != cricket::kVideoCodecClockrate) {
124 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
125 "Video clock rate must be 90000.");
126 }
127 break;
128 default:
129 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
130 "Unknown codec type");
131 }
deadbeefe814a0d2017-02-26 02:15:09132
deadbeefe814a0d2017-02-26 02:15:09133 if (!cricket::IsValidRtpPayloadType(codec.payload_type)) {
Florent Castelli72b751a2018-06-28 12:09:33134 char buf[40];
135 rtc::SimpleStringBuilder sb(buf);
136 sb << "Invalid payload type: " << codec.payload_type;
137 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, sb.str());
deadbeefe814a0d2017-02-26 02:15:09138 }
Florent Castellid0b8e8e2023-06-04 23:22:36139
140 cricket::Codec cricket_codec = [&]() {
141 if (codec.kind == cricket::MEDIA_TYPE_AUDIO) {
142 return cricket::CreateAudioCodec(codec.payload_type, codec.name,
143 *codec.clock_rate, *codec.num_channels);
144 }
145 RTC_DCHECK(codec.kind == cricket::MEDIA_TYPE_VIDEO);
146 return cricket::CreateVideoCodec(codec.payload_type, codec.name);
147 }();
148
deadbeefe814a0d2017-02-26 02:15:09149 for (const RtcpFeedback& feedback : codec.rtcp_feedback) {
150 auto result = ToCricketFeedbackParam(feedback);
151 if (!result.ok()) {
152 return result.MoveError();
153 }
154 cricket_codec.AddFeedbackParam(result.MoveValue());
155 }
Johannes Kron72d69152020-02-10 13:05:55156 cricket_codec.params = codec.parameters;
deadbeefe814a0d2017-02-26 02:15:09157 return std::move(cricket_codec);
158}
159
Florent Castellid0b8e8e2023-06-04 23:22:36160RTCErrorOr<std::vector<cricket::Codec>> ToCricketCodecs(
deadbeefe814a0d2017-02-26 02:15:09161 const std::vector<RtpCodecParameters>& codecs) {
Florent Castellid0b8e8e2023-06-04 23:22:36162 std::vector<cricket::Codec> cricket_codecs;
deadbeefe814a0d2017-02-26 02:15:09163 std::set<int> seen_payload_types;
164 for (const RtpCodecParameters& codec : codecs) {
Florent Castellid0b8e8e2023-06-04 23:22:36165 auto result = ToCricketCodec(codec);
deadbeefe814a0d2017-02-26 02:15:09166 if (!result.ok()) {
167 return result.MoveError();
168 }
169 if (!seen_payload_types.insert(codec.payload_type).second) {
Florent Castelli72b751a2018-06-28 12:09:33170 char buf[40];
171 rtc::SimpleStringBuilder sb(buf);
172 sb << "Duplicate payload type: " << codec.payload_type;
173 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, sb.str());
deadbeefe814a0d2017-02-26 02:15:09174 }
175 cricket_codecs.push_back(result.MoveValue());
176 }
177 return std::move(cricket_codecs);
178}
179
deadbeefe814a0d2017-02-26 02:15:09180RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
181 const std::vector<RtpEncodingParameters>& encodings) {
182 if (encodings.size() > 1u) {
183 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
184 "ORTC API implementation doesn't currently "
185 "support simulcast or layered encodings.");
186 } else if (encodings.empty()) {
187 return cricket::StreamParamsVec();
188 }
189 cricket::StreamParamsVec cricket_streams;
190 const RtpEncodingParameters& encoding = encodings[0];
deadbeefe814a0d2017-02-26 02:15:09191 if (encoding.ssrc) {
192 cricket::StreamParams stream_params;
193 stream_params.add_ssrc(*encoding.ssrc);
deadbeefe814a0d2017-02-26 02:15:09194 cricket_streams.push_back(std::move(stream_params));
195 }
196 return std::move(cricket_streams);
197}
198
Danil Chapovalov00c718362018-06-15 13:58:38199absl::optional<RtcpFeedback> ToRtcpFeedback(
deadbeefe814a0d2017-02-26 02:15:09200 const cricket::FeedbackParam& cricket_feedback) {
201 if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
202 if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
Oskar Sundbomff610bd2017-11-16 09:57:44203 return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
deadbeefe814a0d2017-02-26 02:15:09204 } else {
Mirko Bonadei675513b2017-11-09 10:09:25205 RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
206 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38207 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09208 }
Elad Alonfadb1812019-05-24 11:40:02209 } else if (cricket_feedback.id() == cricket::kRtcpFbParamLntf) {
210 if (cricket_feedback.param().empty()) {
211 return RtcpFeedback(RtcpFeedbackType::LNTF);
212 } else {
213 RTC_LOG(LS_WARNING) << "Unsupported parameter for LNTF RTCP feedback: "
214 << cricket_feedback.param();
215 return absl::nullopt;
216 }
deadbeefe814a0d2017-02-26 02:15:09217 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
218 if (cricket_feedback.param().empty()) {
Oskar Sundbomff610bd2017-11-16 09:57:44219 return RtcpFeedback(RtcpFeedbackType::NACK,
220 RtcpFeedbackMessageType::GENERIC_NACK);
deadbeefe814a0d2017-02-26 02:15:09221 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
Oskar Sundbomff610bd2017-11-16 09:57:44222 return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
deadbeefe814a0d2017-02-26 02:15:09223 } else {
Mirko Bonadei675513b2017-11-09 10:09:25224 RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
225 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38226 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09227 }
228 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
229 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 10:09:25230 RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
231 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38232 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09233 } else {
Oskar Sundbomff610bd2017-11-16 09:57:44234 return RtcpFeedback(RtcpFeedbackType::REMB);
deadbeefe814a0d2017-02-26 02:15:09235 }
236 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
237 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 10:09:25238 RTC_LOG(LS_WARNING)
deadbeefe814a0d2017-02-26 02:15:09239 << "Unsupported parameter for transport-cc RTCP feedback: "
240 << cricket_feedback.param();
Danil Chapovalov00c718362018-06-15 13:58:38241 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09242 } else {
Oskar Sundbomff610bd2017-11-16 09:57:44243 return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
deadbeefe814a0d2017-02-26 02:15:09244 }
245 }
Mirko Bonadei675513b2017-11-09 10:09:25246 RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
247 << cricket_feedback.id();
Danil Chapovalov00c718362018-06-15 13:58:38248 return absl::nullopt;
deadbeefe814a0d2017-02-26 02:15:09249}
250
zhihuang24366392017-03-09 01:15:06251std::vector<RtpEncodingParameters> ToRtpEncodings(
252 const cricket::StreamParamsVec& stream_params) {
253 std::vector<RtpEncodingParameters> rtp_encodings;
254 for (const cricket::StreamParams& stream_param : stream_params) {
255 RtpEncodingParameters rtp_encoding;
256 rtp_encoding.ssrc.emplace(stream_param.first_ssrc());
zhihuang24366392017-03-09 01:15:06257 rtp_encodings.push_back(std::move(rtp_encoding));
258 }
259 return rtp_encodings;
260}
261
Florent Castellid0b8e8e2023-06-04 23:22:36262RtpCodecCapability ToRtpCodecCapability(const cricket::Codec& cricket_codec) {
deadbeefe814a0d2017-02-26 02:15:09263 RtpCodecCapability codec;
264 codec.name = cricket_codec.name;
Florent Castellid0b8e8e2023-06-04 23:22:36265 codec.kind = cricket_codec.type == cricket::Codec::Type::kAudio
266 ? cricket::MEDIA_TYPE_AUDIO
267 : cricket::MEDIA_TYPE_VIDEO;
deadbeefe814a0d2017-02-26 02:15:09268 codec.clock_rate.emplace(cricket_codec.clockrate);
269 codec.preferred_payload_type.emplace(cricket_codec.id);
270 for (const cricket::FeedbackParam& cricket_feedback :
271 cricket_codec.feedback_params.params()) {
Danil Chapovalov00c718362018-06-15 13:58:38272 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
deadbeefe814a0d2017-02-26 02:15:09273 if (feedback) {
Danil Chapovalov57ff2732018-03-28 09:25:15274 codec.rtcp_feedback.push_back(feedback.value());
deadbeefe814a0d2017-02-26 02:15:09275 }
276 }
Florent Castellid0b8e8e2023-06-04 23:22:36277 switch (cricket_codec.type) {
278 case cricket::Codec::Type::kAudio:
279 codec.num_channels = static_cast<int>(cricket_codec.channels);
280 break;
281 case cricket::Codec::Type::kVideo:
282 codec.scalability_modes = cricket_codec.scalability_modes;
283 break;
284 }
deadbeefe814a0d2017-02-26 02:15:09285 codec.parameters.insert(cricket_codec.params.begin(),
286 cricket_codec.params.end());
287 return codec;
288}
289
Florent Castellid0b8e8e2023-06-04 23:22:36290RtpCodecParameters ToRtpCodecParameters(const cricket::Codec& cricket_codec) {
zhihuang24366392017-03-09 01:15:06291 RtpCodecParameters codec_param;
292 codec_param.name = cricket_codec.name;
Florent Castellid0b8e8e2023-06-04 23:22:36293 codec_param.kind = cricket_codec.type == cricket::Codec::Type::kAudio
294 ? cricket::MEDIA_TYPE_AUDIO
295 : cricket::MEDIA_TYPE_VIDEO;
zhihuang24366392017-03-09 01:15:06296 codec_param.clock_rate.emplace(cricket_codec.clockrate);
297 codec_param.payload_type = cricket_codec.id;
298 for (const cricket::FeedbackParam& cricket_feedback :
299 cricket_codec.feedback_params.params()) {
Danil Chapovalov00c718362018-06-15 13:58:38300 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
zhihuang24366392017-03-09 01:15:06301 if (feedback) {
Danil Chapovalov57ff2732018-03-28 09:25:15302 codec_param.rtcp_feedback.push_back(feedback.value());
zhihuang24366392017-03-09 01:15:06303 }
304 }
Florent Castellid0b8e8e2023-06-04 23:22:36305 switch (cricket_codec.type) {
306 case cricket::Codec::Type::kAudio:
307 codec_param.num_channels = static_cast<int>(cricket_codec.channels);
308 break;
309 case cricket::Codec::Type::kVideo:
310 // Nothing to do.
311 break;
312 }
Johannes Kron72d69152020-02-10 13:05:55313 codec_param.parameters = cricket_codec.params;
zhihuang24366392017-03-09 01:15:06314 return codec_param;
315}
316
deadbeefe814a0d2017-02-26 02:15:09317RtpCapabilities ToRtpCapabilities(
Florent Castellid0b8e8e2023-06-04 23:22:36318 const std::vector<cricket::Codec>& cricket_codecs,
deadbeefe814a0d2017-02-26 02:15:09319 const cricket::RtpHeaderExtensions& cricket_extensions) {
320 RtpCapabilities capabilities;
321 bool have_red = false;
322 bool have_ulpfec = false;
323 bool have_flexfec = false;
Florent Castelli5473a452018-11-06 16:27:21324 bool have_rtx = false;
Florent Castellid0b8e8e2023-06-04 23:22:36325 for (const cricket::Codec& cricket_codec : cricket_codecs) {
deadbeefe814a0d2017-02-26 02:15:09326 if (cricket_codec.name == cricket::kRedCodecName) {
327 have_red = true;
328 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
329 have_ulpfec = true;
330 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
331 have_flexfec = true;
Florent Castelli5473a452018-11-06 16:27:21332 } else if (cricket_codec.name == cricket::kRtxCodecName) {
333 if (have_rtx) {
334 // There should only be one RTX codec entry
335 continue;
336 }
337 have_rtx = true;
deadbeefe814a0d2017-02-26 02:15:09338 }
Florent Castelli5473a452018-11-06 16:27:21339 auto codec_capability = ToRtpCodecCapability(cricket_codec);
340 if (cricket_codec.name == cricket::kRtxCodecName) {
341 // RTX codec should not have any parameter
342 codec_capability.parameters.clear();
343 }
344 capabilities.codecs.push_back(codec_capability);
deadbeefe814a0d2017-02-26 02:15:09345 }
346 for (const RtpExtension& cricket_extension : cricket_extensions) {
347 capabilities.header_extensions.emplace_back(cricket_extension.uri,
348 cricket_extension.id);
349 }
350 if (have_red) {
351 capabilities.fec.push_back(FecMechanism::RED);
352 }
353 if (have_red && have_ulpfec) {
354 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
355 }
356 if (have_flexfec) {
357 capabilities.fec.push_back(FecMechanism::FLEXFEC);
358 }
359 return capabilities;
360}
361
zhihuang24366392017-03-09 01:15:06362RtpParameters ToRtpParameters(
Florent Castellid0b8e8e2023-06-04 23:22:36363 const std::vector<cricket::Codec>& cricket_codecs,
zhihuang24366392017-03-09 01:15:06364 const cricket::RtpHeaderExtensions& cricket_extensions,
365 const cricket::StreamParamsVec& stream_params) {
366 RtpParameters rtp_parameters;
Florent Castellid0b8e8e2023-06-04 23:22:36367 for (const cricket::Codec& cricket_codec : cricket_codecs) {
zhihuang24366392017-03-09 01:15:06368 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
369 }
370 for (const RtpExtension& cricket_extension : cricket_extensions) {
371 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
372 cricket_extension.id);
373 }
374 rtp_parameters.encodings = ToRtpEncodings(stream_params);
375 return rtp_parameters;
376}
377
deadbeefe814a0d2017-02-26 02:15:09378} // namespace webrtc