blob: 083c3b09c4bd6117eeca3b26472165f6f9f1c309 [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
11#include "webrtc/ortc/rtpparametersconversion.h"
12
13#include <set>
14#include <sstream>
15#include <utility>
16
17#include "webrtc/media/base/rtputils.h"
18
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) {
237 return rtc::Optional<RtcpFeedback>(
238 {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR});
239 } else {
240 LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
241 << cricket_feedback.param();
242 return rtc::Optional<RtcpFeedback>();
243 }
244 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
245 if (cricket_feedback.param().empty()) {
246 return rtc::Optional<RtcpFeedback>(
247 {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK});
248 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
249 return rtc::Optional<RtcpFeedback>(
250 {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI});
251 } else {
252 LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
253 << cricket_feedback.param();
254 return rtc::Optional<RtcpFeedback>();
255 }
256 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
257 if (!cricket_feedback.param().empty()) {
258 LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
259 << cricket_feedback.param();
260 return rtc::Optional<RtcpFeedback>();
261 } else {
262 return rtc::Optional<RtcpFeedback>(RtcpFeedback(RtcpFeedbackType::REMB));
263 }
264 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
265 if (!cricket_feedback.param().empty()) {
266 LOG(LS_WARNING)
267 << "Unsupported parameter for transport-cc RTCP feedback: "
268 << cricket_feedback.param();
269 return rtc::Optional<RtcpFeedback>();
270 } else {
271 return rtc::Optional<RtcpFeedback>(
272 RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC));
273 }
274 }
275 LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
276 << cricket_feedback.id();
277 return rtc::Optional<RtcpFeedback>();
278}
279
zhihuang24366392017-03-08 17:15:06 -0800280std::vector<RtpEncodingParameters> ToRtpEncodings(
281 const cricket::StreamParamsVec& stream_params) {
282 std::vector<RtpEncodingParameters> rtp_encodings;
283 for (const cricket::StreamParams& stream_param : stream_params) {
284 RtpEncodingParameters rtp_encoding;
285 rtp_encoding.ssrc.emplace(stream_param.first_ssrc());
286 uint32_t rtx_ssrc = 0;
287 if (stream_param.GetFidSsrc(stream_param.first_ssrc(), &rtx_ssrc)) {
288 RtpRtxParameters rtx_param(rtx_ssrc);
289 rtp_encoding.rtx.emplace(rtx_param);
290 }
291 rtp_encodings.push_back(std::move(rtp_encoding));
292 }
293 return rtp_encodings;
294}
295
deadbeefe814a0d2017-02-25 18:15:09 -0800296template <typename C>
297cricket::MediaType KindOfCodec();
298
299template <>
300cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
301 return cricket::MEDIA_TYPE_AUDIO;
302}
303
304template <>
305cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
306 return cricket::MEDIA_TYPE_VIDEO;
307}
308
309template <typename C>
310static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
311 RtpCodecCapability* codec);
312
313template <>
314void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
315 const cricket::AudioCodec& cricket_codec,
316 RtpCodecCapability* codec) {
317 codec->num_channels =
318 rtc::Optional<int>(static_cast<int>(cricket_codec.channels));
319}
320
321template <>
322void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
323 const cricket::VideoCodec& cricket_codec,
324 RtpCodecCapability* codec) {}
325
326template <typename C>
327RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
328 RtpCodecCapability codec;
329 codec.name = cricket_codec.name;
330 codec.kind = KindOfCodec<C>();
331 codec.clock_rate.emplace(cricket_codec.clockrate);
332 codec.preferred_payload_type.emplace(cricket_codec.id);
333 for (const cricket::FeedbackParam& cricket_feedback :
334 cricket_codec.feedback_params.params()) {
335 rtc::Optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
336 if (feedback) {
337 codec.rtcp_feedback.push_back(feedback.MoveValue());
338 }
339 }
340 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
341 codec.parameters.insert(cricket_codec.params.begin(),
342 cricket_codec.params.end());
343 return codec;
344}
345
346template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
347 const cricket::AudioCodec& cricket_codec);
348template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
349 const cricket::VideoCodec& cricket_codec);
350
zhihuang24366392017-03-08 17:15:06 -0800351template <typename C>
352static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
353 RtpCodecParameters* codec);
354template <>
355void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
356 const cricket::AudioCodec& cricket_codec,
357 RtpCodecParameters* codec) {
358 codec->num_channels =
359 rtc::Optional<int>(static_cast<int>(cricket_codec.channels));
360}
361
362template <>
363void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
364 const cricket::VideoCodec& cricket_codec,
365 RtpCodecParameters* codec) {}
366
367template <typename C>
368RtpCodecParameters ToRtpCodecParameters(const C& cricket_codec) {
369 RtpCodecParameters codec_param;
370 codec_param.name = cricket_codec.name;
371 codec_param.kind = KindOfCodec<C>();
372 codec_param.clock_rate.emplace(cricket_codec.clockrate);
373 codec_param.payload_type = cricket_codec.id;
374 for (const cricket::FeedbackParam& cricket_feedback :
375 cricket_codec.feedback_params.params()) {
376 rtc::Optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
377 if (feedback) {
378 codec_param.rtcp_feedback.push_back(feedback.MoveValue());
379 }
380 }
381 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
382 codec_param.parameters.insert(cricket_codec.params.begin(),
383 cricket_codec.params.end());
384 return codec_param;
385}
386
387template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
388 const cricket::AudioCodec& cricket_codec);
389template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
390 const cricket::VideoCodec& cricket_codec);
391
deadbeefe814a0d2017-02-25 18:15:09 -0800392template <class C>
393RtpCapabilities ToRtpCapabilities(
394 const std::vector<C>& cricket_codecs,
395 const cricket::RtpHeaderExtensions& cricket_extensions) {
396 RtpCapabilities capabilities;
397 bool have_red = false;
398 bool have_ulpfec = false;
399 bool have_flexfec = false;
400 for (const C& cricket_codec : cricket_codecs) {
401 if (cricket_codec.name == cricket::kRedCodecName) {
402 have_red = true;
403 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
404 have_ulpfec = true;
405 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
406 have_flexfec = true;
407 }
408 capabilities.codecs.push_back(ToRtpCodecCapability(cricket_codec));
409 }
410 for (const RtpExtension& cricket_extension : cricket_extensions) {
411 capabilities.header_extensions.emplace_back(cricket_extension.uri,
412 cricket_extension.id);
413 }
414 if (have_red) {
415 capabilities.fec.push_back(FecMechanism::RED);
416 }
417 if (have_red && have_ulpfec) {
418 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
419 }
420 if (have_flexfec) {
421 capabilities.fec.push_back(FecMechanism::FLEXFEC);
422 }
423 return capabilities;
424}
425
426template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
427 const std::vector<cricket::AudioCodec>& cricket_codecs,
428 const cricket::RtpHeaderExtensions& cricket_extensions);
429template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
430 const std::vector<cricket::VideoCodec>& cricket_codecs,
431 const cricket::RtpHeaderExtensions& cricket_extensions);
432
zhihuang24366392017-03-08 17:15:06 -0800433template <class C>
434RtpParameters ToRtpParameters(
435 const std::vector<C>& cricket_codecs,
436 const cricket::RtpHeaderExtensions& cricket_extensions,
437 const cricket::StreamParamsVec& stream_params) {
438 RtpParameters rtp_parameters;
439 for (const C& cricket_codec : cricket_codecs) {
440 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
441 }
442 for (const RtpExtension& cricket_extension : cricket_extensions) {
443 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
444 cricket_extension.id);
445 }
446 rtp_parameters.encodings = ToRtpEncodings(stream_params);
447 return rtp_parameters;
448}
449
450template RtpParameters ToRtpParameters<cricket::AudioCodec>(
451 const std::vector<cricket::AudioCodec>& cricket_codecs,
452 const cricket::RtpHeaderExtensions& cricket_extensions,
453 const cricket::StreamParamsVec& stream_params);
454template RtpParameters ToRtpParameters<cricket::VideoCodec>(
455 const std::vector<cricket::VideoCodec>& cricket_codecs,
456 const cricket::RtpHeaderExtensions& cricket_extensions,
457 const cricket::StreamParamsVec& stream_params);
458
deadbeefe814a0d2017-02-25 18:15:09 -0800459} // namespace webrtc