blob: 1c336e299646ee63537009a97e7f630e88495352 [file] [log] [blame]
deadbeefe814a0d2017-02-25 18:15:09 -08001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "ortc/rtpparametersconversion.h"
deadbeefe814a0d2017-02-25 18:15:09 -080012
13#include <set>
14#include <sstream>
15#include <utility>
16
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "media/base/rtputils.h"
deadbeefe814a0d2017-02-25 18:15:09 -080018
19namespace webrtc {
20
21RTCErrorOr<cricket::FeedbackParam> ToCricketFeedbackParam(
22 const RtcpFeedback& feedback) {
23 switch (feedback.type) {
24 case RtcpFeedbackType::CCM:
25 if (!feedback.message_type) {
26 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
27 "Missing message type in CCM RtcpFeedback.");
28 } else if (*feedback.message_type != RtcpFeedbackMessageType::FIR) {
29 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
30 "Invalid message type in CCM RtcpFeedback.");
31 }
32 return cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
33 cricket::kRtcpFbCcmParamFir);
34 case RtcpFeedbackType::NACK:
35 if (!feedback.message_type) {
36 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
37 "Missing message type in NACK RtcpFeedback.");
38 }
39 switch (*feedback.message_type) {
40 case RtcpFeedbackMessageType::GENERIC_NACK:
41 return cricket::FeedbackParam(cricket::kRtcpFbParamNack);
42 case RtcpFeedbackMessageType::PLI:
43 return cricket::FeedbackParam(cricket::kRtcpFbParamNack,
44 cricket::kRtcpFbNackParamPli);
45 default:
46 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
47 "Invalid message type in NACK RtcpFeedback.");
48 }
49 case RtcpFeedbackType::REMB:
50 if (feedback.message_type) {
51 LOG_AND_RETURN_ERROR(
52 RTCErrorType::INVALID_PARAMETER,
53 "Didn't expect message type in REMB RtcpFeedback.");
54 }
55 return cricket::FeedbackParam(cricket::kRtcpFbParamRemb);
56 case RtcpFeedbackType::TRANSPORT_CC:
57 if (feedback.message_type) {
58 LOG_AND_RETURN_ERROR(
59 RTCErrorType::INVALID_PARAMETER,
60 "Didn't expect message type in transport-cc RtcpFeedback.");
61 }
62 return cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc);
63 }
64 // Not reached; avoids compile warning.
65 FATAL();
66}
67
68template <typename C>
69static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
70 C* cricket_codec);
71
72template <>
73RTCError ToCricketCodecTypeSpecific<cricket::AudioCodec>(
74 const RtpCodecParameters& codec,
75 cricket::AudioCodec* cricket_codec) {
76 if (codec.kind != cricket::MEDIA_TYPE_AUDIO) {
77 LOG_AND_RETURN_ERROR(
78 RTCErrorType::INVALID_PARAMETER,
79 "Can't use video codec with audio sender or receiver.");
80 }
81 if (!codec.num_channels) {
82 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
83 "Missing number of channels for audio codec.");
84 }
85 if (*codec.num_channels <= 0) {
86 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
87 "Number of channels must be positive.");
88 }
89 cricket_codec->channels = *codec.num_channels;
90 if (!codec.clock_rate) {
91 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
92 "Missing codec clock rate.");
93 }
94 if (*codec.clock_rate <= 0) {
95 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
96 "Clock rate must be positive.");
97 }
98 cricket_codec->clockrate = *codec.clock_rate;
99 return RTCError::OK();
100}
101
102// Video codecs don't use num_channels or clock_rate, but they should at least
103// be validated to ensure the application isn't trying to do something it
104// doesn't intend to.
105template <>
106RTCError ToCricketCodecTypeSpecific<cricket::VideoCodec>(
107 const RtpCodecParameters& codec,
108 cricket::VideoCodec*) {
109 if (codec.kind != cricket::MEDIA_TYPE_VIDEO) {
110 LOG_AND_RETURN_ERROR(
111 RTCErrorType::INVALID_PARAMETER,
112 "Can't use audio codec with video sender or receiver.");
113 }
114 if (codec.num_channels) {
115 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
116 "Video codec shouldn't have num_channels.");
117 }
118 if (!codec.clock_rate) {
119 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
120 "Missing codec clock rate.");
121 }
122 if (*codec.clock_rate != cricket::kVideoCodecClockrate) {
123 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
124 "Video clock rate must be 90000.");
125 }
126 return RTCError::OK();
127}
128
129template <typename C>
130RTCErrorOr<C> ToCricketCodec(const RtpCodecParameters& codec) {
131 C cricket_codec;
132 // Start with audio/video specific conversion.
133 RTCError err = ToCricketCodecTypeSpecific(codec, &cricket_codec);
134 if (!err.ok()) {
135 return std::move(err);
136 }
137 cricket_codec.name = codec.name;
138 if (!cricket::IsValidRtpPayloadType(codec.payload_type)) {
139 std::ostringstream oss;
140 oss << "Invalid payload type: " << codec.payload_type;
141 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, oss.str());
142 }
143 cricket_codec.id = codec.payload_type;
144 for (const RtcpFeedback& feedback : codec.rtcp_feedback) {
145 auto result = ToCricketFeedbackParam(feedback);
146 if (!result.ok()) {
147 return result.MoveError();
148 }
149 cricket_codec.AddFeedbackParam(result.MoveValue());
150 }
151 cricket_codec.params.insert(codec.parameters.begin(), codec.parameters.end());
152 return std::move(cricket_codec);
153}
154
155template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
156 const RtpCodecParameters& codec);
157template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
158 const RtpCodecParameters& codec);
159
160template <typename C>
161RTCErrorOr<std::vector<C>> ToCricketCodecs(
162 const std::vector<RtpCodecParameters>& codecs) {
163 std::vector<C> cricket_codecs;
164 std::set<int> seen_payload_types;
165 for (const RtpCodecParameters& codec : codecs) {
166 auto result = ToCricketCodec<C>(codec);
167 if (!result.ok()) {
168 return result.MoveError();
169 }
170 if (!seen_payload_types.insert(codec.payload_type).second) {
171 std::ostringstream oss;
172 oss << "Duplicate payload type: " << codec.payload_type;
173 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
174 }
175 cricket_codecs.push_back(result.MoveValue());
176 }
177 return std::move(cricket_codecs);
178}
179
180template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
181 cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
182
183template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
184 cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
185
186RTCErrorOr<cricket::RtpHeaderExtensions> ToCricketRtpHeaderExtensions(
187 const std::vector<RtpHeaderExtensionParameters>& extensions) {
188 cricket::RtpHeaderExtensions cricket_extensions;
189 std::ostringstream err_writer;
190 std::set<int> seen_header_extension_ids;
191 for (const RtpHeaderExtensionParameters& extension : extensions) {
192 if (extension.id < RtpHeaderExtensionParameters::kMinId ||
193 extension.id > RtpHeaderExtensionParameters::kMaxId) {
194 err_writer << "Invalid header extension id: " << extension.id;
195 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, err_writer.str());
196 }
197 if (!seen_header_extension_ids.insert(extension.id).second) {
198 err_writer << "Duplicate header extension id: " << extension.id;
199 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, err_writer.str());
200 }
201 cricket_extensions.push_back(extension);
202 }
203 return std::move(cricket_extensions);
204}
205
206RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
207 const std::vector<RtpEncodingParameters>& encodings) {
208 if (encodings.size() > 1u) {
209 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
210 "ORTC API implementation doesn't currently "
211 "support simulcast or layered encodings.");
212 } else if (encodings.empty()) {
213 return cricket::StreamParamsVec();
214 }
215 cricket::StreamParamsVec cricket_streams;
216 const RtpEncodingParameters& encoding = encodings[0];
217 if (encoding.rtx && encoding.rtx->ssrc && !encoding.ssrc) {
218 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
219 "Setting an RTX SSRC explicitly while leaving the "
220 "primary SSRC unset is not currently supported.");
221 }
222 if (encoding.ssrc) {
223 cricket::StreamParams stream_params;
224 stream_params.add_ssrc(*encoding.ssrc);
225 if (encoding.rtx && encoding.rtx->ssrc) {
226 stream_params.AddFidSsrc(*encoding.ssrc, *encoding.rtx->ssrc);
227 }
228 cricket_streams.push_back(std::move(stream_params));
229 }
230 return std::move(cricket_streams);
231}
232
233rtc::Optional<RtcpFeedback> ToRtcpFeedback(
234 const cricket::FeedbackParam& cricket_feedback) {
235 if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
236 if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100237 return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
deadbeefe814a0d2017-02-25 18:15:09 -0800238 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100239 RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
240 << cricket_feedback.param();
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100241 return rtc::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800242 }
243 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
244 if (cricket_feedback.param().empty()) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100245 return RtcpFeedback(RtcpFeedbackType::NACK,
246 RtcpFeedbackMessageType::GENERIC_NACK);
deadbeefe814a0d2017-02-25 18:15:09 -0800247 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100248 return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
deadbeefe814a0d2017-02-25 18:15:09 -0800249 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100250 RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
251 << cricket_feedback.param();
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100252 return rtc::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800253 }
254 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
255 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100256 RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
257 << cricket_feedback.param();
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100258 return rtc::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800259 } else {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100260 return RtcpFeedback(RtcpFeedbackType::REMB);
deadbeefe814a0d2017-02-25 18:15:09 -0800261 }
262 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
263 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100264 RTC_LOG(LS_WARNING)
deadbeefe814a0d2017-02-25 18:15:09 -0800265 << "Unsupported parameter for transport-cc RTCP feedback: "
266 << cricket_feedback.param();
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100267 return rtc::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800268 } else {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100269 return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
deadbeefe814a0d2017-02-25 18:15:09 -0800270 }
271 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100272 RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
273 << cricket_feedback.id();
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100274 return rtc::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800275}
276
zhihuang24366392017-03-08 17:15:06 -0800277std::vector<RtpEncodingParameters> ToRtpEncodings(
278 const cricket::StreamParamsVec& stream_params) {
279 std::vector<RtpEncodingParameters> rtp_encodings;
280 for (const cricket::StreamParams& stream_param : stream_params) {
281 RtpEncodingParameters rtp_encoding;
282 rtp_encoding.ssrc.emplace(stream_param.first_ssrc());
283 uint32_t rtx_ssrc = 0;
284 if (stream_param.GetFidSsrc(stream_param.first_ssrc(), &rtx_ssrc)) {
285 RtpRtxParameters rtx_param(rtx_ssrc);
286 rtp_encoding.rtx.emplace(rtx_param);
287 }
288 rtp_encodings.push_back(std::move(rtp_encoding));
289 }
290 return rtp_encodings;
291}
292
deadbeefe814a0d2017-02-25 18:15:09 -0800293template <typename C>
294cricket::MediaType KindOfCodec();
295
296template <>
297cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
298 return cricket::MEDIA_TYPE_AUDIO;
299}
300
301template <>
302cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
303 return cricket::MEDIA_TYPE_VIDEO;
304}
305
306template <typename C>
307static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
308 RtpCodecCapability* codec);
309
310template <>
311void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
312 const cricket::AudioCodec& cricket_codec,
313 RtpCodecCapability* codec) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100314 codec->num_channels = static_cast<int>(cricket_codec.channels);
deadbeefe814a0d2017-02-25 18:15:09 -0800315}
316
317template <>
318void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
319 const cricket::VideoCodec& cricket_codec,
320 RtpCodecCapability* codec) {}
321
322template <typename C>
323RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
324 RtpCodecCapability codec;
325 codec.name = cricket_codec.name;
326 codec.kind = KindOfCodec<C>();
327 codec.clock_rate.emplace(cricket_codec.clockrate);
328 codec.preferred_payload_type.emplace(cricket_codec.id);
329 for (const cricket::FeedbackParam& cricket_feedback :
330 cricket_codec.feedback_params.params()) {
331 rtc::Optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
332 if (feedback) {
Danil Chapovalov57ff2732018-03-28 11:25:15 +0200333 codec.rtcp_feedback.push_back(feedback.value());
deadbeefe814a0d2017-02-25 18:15:09 -0800334 }
335 }
336 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
337 codec.parameters.insert(cricket_codec.params.begin(),
338 cricket_codec.params.end());
339 return codec;
340}
341
342template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
343 const cricket::AudioCodec& cricket_codec);
344template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
345 const cricket::VideoCodec& cricket_codec);
346
zhihuang24366392017-03-08 17:15:06 -0800347template <typename C>
348static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
349 RtpCodecParameters* codec);
350template <>
351void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
352 const cricket::AudioCodec& cricket_codec,
353 RtpCodecParameters* codec) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100354 codec->num_channels = static_cast<int>(cricket_codec.channels);
zhihuang24366392017-03-08 17:15:06 -0800355}
356
357template <>
358void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
359 const cricket::VideoCodec& cricket_codec,
360 RtpCodecParameters* codec) {}
361
362template <typename C>
363RtpCodecParameters ToRtpCodecParameters(const C& cricket_codec) {
364 RtpCodecParameters codec_param;
365 codec_param.name = cricket_codec.name;
366 codec_param.kind = KindOfCodec<C>();
367 codec_param.clock_rate.emplace(cricket_codec.clockrate);
368 codec_param.payload_type = cricket_codec.id;
369 for (const cricket::FeedbackParam& cricket_feedback :
370 cricket_codec.feedback_params.params()) {
371 rtc::Optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
372 if (feedback) {
Danil Chapovalov57ff2732018-03-28 11:25:15 +0200373 codec_param.rtcp_feedback.push_back(feedback.value());
zhihuang24366392017-03-08 17:15:06 -0800374 }
375 }
376 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
377 codec_param.parameters.insert(cricket_codec.params.begin(),
378 cricket_codec.params.end());
379 return codec_param;
380}
381
382template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
383 const cricket::AudioCodec& cricket_codec);
384template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
385 const cricket::VideoCodec& cricket_codec);
386
deadbeefe814a0d2017-02-25 18:15:09 -0800387template <class C>
388RtpCapabilities ToRtpCapabilities(
389 const std::vector<C>& cricket_codecs,
390 const cricket::RtpHeaderExtensions& cricket_extensions) {
391 RtpCapabilities capabilities;
392 bool have_red = false;
393 bool have_ulpfec = false;
394 bool have_flexfec = false;
395 for (const C& cricket_codec : cricket_codecs) {
396 if (cricket_codec.name == cricket::kRedCodecName) {
397 have_red = true;
398 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
399 have_ulpfec = true;
400 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
401 have_flexfec = true;
402 }
403 capabilities.codecs.push_back(ToRtpCodecCapability(cricket_codec));
404 }
405 for (const RtpExtension& cricket_extension : cricket_extensions) {
406 capabilities.header_extensions.emplace_back(cricket_extension.uri,
407 cricket_extension.id);
408 }
409 if (have_red) {
410 capabilities.fec.push_back(FecMechanism::RED);
411 }
412 if (have_red && have_ulpfec) {
413 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
414 }
415 if (have_flexfec) {
416 capabilities.fec.push_back(FecMechanism::FLEXFEC);
417 }
418 return capabilities;
419}
420
421template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
422 const std::vector<cricket::AudioCodec>& cricket_codecs,
423 const cricket::RtpHeaderExtensions& cricket_extensions);
424template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
425 const std::vector<cricket::VideoCodec>& cricket_codecs,
426 const cricket::RtpHeaderExtensions& cricket_extensions);
427
zhihuang24366392017-03-08 17:15:06 -0800428template <class C>
429RtpParameters ToRtpParameters(
430 const std::vector<C>& cricket_codecs,
431 const cricket::RtpHeaderExtensions& cricket_extensions,
432 const cricket::StreamParamsVec& stream_params) {
433 RtpParameters rtp_parameters;
434 for (const C& cricket_codec : cricket_codecs) {
435 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
436 }
437 for (const RtpExtension& cricket_extension : cricket_extensions) {
438 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
439 cricket_extension.id);
440 }
441 rtp_parameters.encodings = ToRtpEncodings(stream_params);
442 return rtp_parameters;
443}
444
445template RtpParameters ToRtpParameters<cricket::AudioCodec>(
446 const std::vector<cricket::AudioCodec>& cricket_codecs,
447 const cricket::RtpHeaderExtensions& cricket_extensions,
448 const cricket::StreamParamsVec& stream_params);
449template RtpParameters ToRtpParameters<cricket::VideoCodec>(
450 const std::vector<cricket::VideoCodec>& cricket_codecs,
451 const cricket::RtpHeaderExtensions& cricket_extensions,
452 const cricket::StreamParamsVec& stream_params);
453
deadbeefe814a0d2017-02-25 18:15:09 -0800454} // namespace webrtc