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   // Not reached; avoids compile warning.
80   FATAL();
81 }
82 
83 template <typename C>
84 static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
85                                            C* cricket_codec);
86 
87 template <>
ToCricketCodecTypeSpecific(const RtpCodecParameters & codec,cricket::AudioCodec * cricket_codec)88 RTCError ToCricketCodecTypeSpecific<cricket::AudioCodec>(
89     const RtpCodecParameters& codec,
90     cricket::AudioCodec* cricket_codec) {
91   if (codec.kind != cricket::MEDIA_TYPE_AUDIO) {
92     LOG_AND_RETURN_ERROR(
93         RTCErrorType::INVALID_PARAMETER,
94         "Can't use video codec with audio sender or receiver.");
95   }
96   if (!codec.num_channels) {
97     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
98                          "Missing number of channels for audio codec.");
99   }
100   if (*codec.num_channels <= 0) {
101     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
102                          "Number of channels must be positive.");
103   }
104   cricket_codec->channels = *codec.num_channels;
105   if (!codec.clock_rate) {
106     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
107                          "Missing codec clock rate.");
108   }
109   if (*codec.clock_rate <= 0) {
110     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
111                          "Clock rate must be positive.");
112   }
113   cricket_codec->clockrate = *codec.clock_rate;
114   return RTCError::OK();
115 }
116 
117 // Video codecs don't use num_channels or clock_rate, but they should at least
118 // be validated to ensure the application isn't trying to do something it
119 // doesn't intend to.
120 template <>
ToCricketCodecTypeSpecific(const RtpCodecParameters & codec,cricket::VideoCodec *)121 RTCError ToCricketCodecTypeSpecific<cricket::VideoCodec>(
122     const RtpCodecParameters& codec,
123     cricket::VideoCodec*) {
124   if (codec.kind != cricket::MEDIA_TYPE_VIDEO) {
125     LOG_AND_RETURN_ERROR(
126         RTCErrorType::INVALID_PARAMETER,
127         "Can't use audio codec with video sender or receiver.");
128   }
129   if (codec.num_channels) {
130     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
131                          "Video codec shouldn't have num_channels.");
132   }
133   if (!codec.clock_rate) {
134     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
135                          "Missing codec clock rate.");
136   }
137   if (*codec.clock_rate != cricket::kVideoCodecClockrate) {
138     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
139                          "Video clock rate must be 90000.");
140   }
141   return RTCError::OK();
142 }
143 
144 template <typename C>
ToCricketCodec(const RtpCodecParameters & codec)145 RTCErrorOr<C> ToCricketCodec(const RtpCodecParameters& codec) {
146   C cricket_codec;
147   // Start with audio/video specific conversion.
148   RTCError err = ToCricketCodecTypeSpecific(codec, &cricket_codec);
149   if (!err.ok()) {
150     return std::move(err);
151   }
152   cricket_codec.name = codec.name;
153   if (!cricket::IsValidRtpPayloadType(codec.payload_type)) {
154     char buf[40];
155     rtc::SimpleStringBuilder sb(buf);
156     sb << "Invalid payload type: " << codec.payload_type;
157     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, sb.str());
158   }
159   cricket_codec.id = codec.payload_type;
160   for (const RtcpFeedback& feedback : codec.rtcp_feedback) {
161     auto result = ToCricketFeedbackParam(feedback);
162     if (!result.ok()) {
163       return result.MoveError();
164     }
165     cricket_codec.AddFeedbackParam(result.MoveValue());
166   }
167   cricket_codec.params = codec.parameters;
168   return std::move(cricket_codec);
169 }
170 
171 template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
172     const RtpCodecParameters& codec);
173 template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
174     const RtpCodecParameters& codec);
175 
176 template <typename C>
ToCricketCodecs(const std::vector<RtpCodecParameters> & codecs)177 RTCErrorOr<std::vector<C>> ToCricketCodecs(
178     const std::vector<RtpCodecParameters>& codecs) {
179   std::vector<C> cricket_codecs;
180   std::set<int> seen_payload_types;
181   for (const RtpCodecParameters& codec : codecs) {
182     auto result = ToCricketCodec<C>(codec);
183     if (!result.ok()) {
184       return result.MoveError();
185     }
186     if (!seen_payload_types.insert(codec.payload_type).second) {
187       char buf[40];
188       rtc::SimpleStringBuilder sb(buf);
189       sb << "Duplicate payload type: " << codec.payload_type;
190       LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, sb.str());
191     }
192     cricket_codecs.push_back(result.MoveValue());
193   }
194   return std::move(cricket_codecs);
195 }
196 
197 template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
198     cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
199 
200 template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
201     cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
202 
ToCricketStreamParamsVec(const std::vector<RtpEncodingParameters> & encodings)203 RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
204     const std::vector<RtpEncodingParameters>& encodings) {
205   if (encodings.size() > 1u) {
206     LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
207                          "ORTC API implementation doesn't currently "
208                          "support simulcast or layered encodings.");
209   } else if (encodings.empty()) {
210     return cricket::StreamParamsVec();
211   }
212   cricket::StreamParamsVec cricket_streams;
213   const RtpEncodingParameters& encoding = encodings[0];
214   if (encoding.ssrc) {
215     cricket::StreamParams stream_params;
216     stream_params.add_ssrc(*encoding.ssrc);
217     cricket_streams.push_back(std::move(stream_params));
218   }
219   return std::move(cricket_streams);
220 }
221 
ToRtcpFeedback(const cricket::FeedbackParam & cricket_feedback)222 absl::optional<RtcpFeedback> ToRtcpFeedback(
223     const cricket::FeedbackParam& cricket_feedback) {
224   if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
225     if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
226       return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
227     } else {
228       RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
229                           << cricket_feedback.param();
230       return absl::nullopt;
231     }
232   } else if (cricket_feedback.id() == cricket::kRtcpFbParamLntf) {
233     if (cricket_feedback.param().empty()) {
234       return RtcpFeedback(RtcpFeedbackType::LNTF);
235     } else {
236       RTC_LOG(LS_WARNING) << "Unsupported parameter for LNTF RTCP feedback: "
237                           << cricket_feedback.param();
238       return absl::nullopt;
239     }
240   } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
241     if (cricket_feedback.param().empty()) {
242       return RtcpFeedback(RtcpFeedbackType::NACK,
243                           RtcpFeedbackMessageType::GENERIC_NACK);
244     } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
245       return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
246     } else {
247       RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
248                           << cricket_feedback.param();
249       return absl::nullopt;
250     }
251   } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
252     if (!cricket_feedback.param().empty()) {
253       RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
254                           << cricket_feedback.param();
255       return absl::nullopt;
256     } else {
257       return RtcpFeedback(RtcpFeedbackType::REMB);
258     }
259   } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
260     if (!cricket_feedback.param().empty()) {
261       RTC_LOG(LS_WARNING)
262           << "Unsupported parameter for transport-cc RTCP feedback: "
263           << cricket_feedback.param();
264       return absl::nullopt;
265     } else {
266       return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
267     }
268   }
269   RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
270                       << cricket_feedback.id();
271   return absl::nullopt;
272 }
273 
ToRtpEncodings(const cricket::StreamParamsVec & stream_params)274 std::vector<RtpEncodingParameters> ToRtpEncodings(
275     const cricket::StreamParamsVec& stream_params) {
276   std::vector<RtpEncodingParameters> rtp_encodings;
277   for (const cricket::StreamParams& stream_param : stream_params) {
278     RtpEncodingParameters rtp_encoding;
279     rtp_encoding.ssrc.emplace(stream_param.first_ssrc());
280     rtp_encodings.push_back(std::move(rtp_encoding));
281   }
282   return rtp_encodings;
283 }
284 
285 template <typename C>
286 cricket::MediaType KindOfCodec();
287 
288 template <>
KindOfCodec()289 cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
290   return cricket::MEDIA_TYPE_AUDIO;
291 }
292 
293 template <>
KindOfCodec()294 cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
295   return cricket::MEDIA_TYPE_VIDEO;
296 }
297 
298 template <typename C>
299 static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
300                                              RtpCodecCapability* codec);
301 
302 template <>
ToRtpCodecCapabilityTypeSpecific(const cricket::AudioCodec & cricket_codec,RtpCodecCapability * codec)303 void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
304     const cricket::AudioCodec& cricket_codec,
305     RtpCodecCapability* codec) {
306   codec->num_channels = static_cast<int>(cricket_codec.channels);
307 }
308 
309 template <>
ToRtpCodecCapabilityTypeSpecific(const cricket::VideoCodec & cricket_codec,RtpCodecCapability * codec)310 void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
311     const cricket::VideoCodec& cricket_codec,
312     RtpCodecCapability* codec) {}
313 
314 template <typename C>
ToRtpCodecCapability(const C & cricket_codec)315 RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
316   RtpCodecCapability codec;
317   codec.name = cricket_codec.name;
318   codec.kind = KindOfCodec<C>();
319   codec.clock_rate.emplace(cricket_codec.clockrate);
320   codec.preferred_payload_type.emplace(cricket_codec.id);
321   for (const cricket::FeedbackParam& cricket_feedback :
322        cricket_codec.feedback_params.params()) {
323     absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
324     if (feedback) {
325       codec.rtcp_feedback.push_back(feedback.value());
326     }
327   }
328   ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
329   codec.parameters.insert(cricket_codec.params.begin(),
330                           cricket_codec.params.end());
331   return codec;
332 }
333 
334 template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
335     const cricket::AudioCodec& cricket_codec);
336 template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
337     const cricket::VideoCodec& cricket_codec);
338 
339 template <typename C>
340 static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
341                                              RtpCodecParameters* codec);
342 template <>
ToRtpCodecParametersTypeSpecific(const cricket::AudioCodec & cricket_codec,RtpCodecParameters * codec)343 void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
344     const cricket::AudioCodec& cricket_codec,
345     RtpCodecParameters* codec) {
346   codec->num_channels = static_cast<int>(cricket_codec.channels);
347 }
348 
349 template <>
ToRtpCodecParametersTypeSpecific(const cricket::VideoCodec & cricket_codec,RtpCodecParameters * codec)350 void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
351     const cricket::VideoCodec& cricket_codec,
352     RtpCodecParameters* codec) {}
353 
354 template <typename C>
ToRtpCodecParameters(const C & cricket_codec)355 RtpCodecParameters ToRtpCodecParameters(const C& cricket_codec) {
356   RtpCodecParameters codec_param;
357   codec_param.name = cricket_codec.name;
358   codec_param.kind = KindOfCodec<C>();
359   codec_param.clock_rate.emplace(cricket_codec.clockrate);
360   codec_param.payload_type = cricket_codec.id;
361   for (const cricket::FeedbackParam& cricket_feedback :
362        cricket_codec.feedback_params.params()) {
363     absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
364     if (feedback) {
365       codec_param.rtcp_feedback.push_back(feedback.value());
366     }
367   }
368   ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
369   codec_param.parameters = cricket_codec.params;
370   return codec_param;
371 }
372 
373 template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
374     const cricket::AudioCodec& cricket_codec);
375 template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
376     const cricket::VideoCodec& cricket_codec);
377 
378 template <class C>
ToRtpCapabilities(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions)379 RtpCapabilities ToRtpCapabilities(
380     const std::vector<C>& cricket_codecs,
381     const cricket::RtpHeaderExtensions& cricket_extensions) {
382   RtpCapabilities capabilities;
383   bool have_red = false;
384   bool have_ulpfec = false;
385   bool have_flexfec = false;
386   bool have_rtx = false;
387   for (const C& cricket_codec : cricket_codecs) {
388     if (cricket_codec.name == cricket::kRedCodecName) {
389       have_red = true;
390     } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
391       have_ulpfec = true;
392     } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
393       have_flexfec = true;
394     } else if (cricket_codec.name == cricket::kRtxCodecName) {
395       if (have_rtx) {
396         // There should only be one RTX codec entry
397         continue;
398       }
399       have_rtx = true;
400     }
401     auto codec_capability = ToRtpCodecCapability(cricket_codec);
402     if (cricket_codec.name == cricket::kRtxCodecName) {
403       // RTX codec should not have any parameter
404       codec_capability.parameters.clear();
405     }
406     capabilities.codecs.push_back(codec_capability);
407   }
408   for (const RtpExtension& cricket_extension : cricket_extensions) {
409     capabilities.header_extensions.emplace_back(cricket_extension.uri,
410                                                 cricket_extension.id);
411   }
412   if (have_red) {
413     capabilities.fec.push_back(FecMechanism::RED);
414   }
415   if (have_red && have_ulpfec) {
416     capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
417   }
418   if (have_flexfec) {
419     capabilities.fec.push_back(FecMechanism::FLEXFEC);
420   }
421   return capabilities;
422 }
423 
424 template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
425     const std::vector<cricket::AudioCodec>& cricket_codecs,
426     const cricket::RtpHeaderExtensions& cricket_extensions);
427 template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
428     const std::vector<cricket::VideoCodec>& cricket_codecs,
429     const cricket::RtpHeaderExtensions& cricket_extensions);
430 
431 template <class C>
ToRtpParameters(const std::vector<C> & cricket_codecs,const cricket::RtpHeaderExtensions & cricket_extensions,const cricket::StreamParamsVec & stream_params)432 RtpParameters ToRtpParameters(
433     const std::vector<C>& cricket_codecs,
434     const cricket::RtpHeaderExtensions& cricket_extensions,
435     const cricket::StreamParamsVec& stream_params) {
436   RtpParameters rtp_parameters;
437   for (const C& cricket_codec : cricket_codecs) {
438     rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
439   }
440   for (const RtpExtension& cricket_extension : cricket_extensions) {
441     rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
442                                                   cricket_extension.id);
443   }
444   rtp_parameters.encodings = ToRtpEncodings(stream_params);
445   return rtp_parameters;
446 }
447 
448 template RtpParameters ToRtpParameters<cricket::AudioCodec>(
449     const std::vector<cricket::AudioCodec>& cricket_codecs,
450     const cricket::RtpHeaderExtensions& cricket_extensions,
451     const cricket::StreamParamsVec& stream_params);
452 template RtpParameters ToRtpParameters<cricket::VideoCodec>(
453     const std::vector<cricket::VideoCodec>& cricket_codecs,
454     const cricket::RtpHeaderExtensions& cricket_extensions,
455     const cricket::StreamParamsVec& stream_params);
456 
457 }  // namespace webrtc
458