blob: 93f28f18159d414baee4a2ec784c3345244172a5 [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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/rtp_parameters_conversion.h"
deadbeefe814a0d2017-02-25 18:15:09 -080012
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <cstdint>
deadbeefe814a0d2017-02-25 18:15:09 -080014#include <set>
Yves Gerey3e707812018-11-28 16:47:49 +010015#include <string>
16#include <unordered_map>
deadbeefe814a0d2017-02-25 18:15:09 -080017#include <utility>
18
Yves Gerey3e707812018-11-28 16:47:49 +010019#include "api/array_view.h"
Steve Anton10542f22019-01-11 09:11:00 -080020#include "api/media_types.h"
21#include "media/base/media_constants.h"
22#include "media/base/rtp_utils.h"
Yves Gerey3e707812018-11-28 16:47:49 +010023#include "rtc_base/checks.h"
24#include "rtc_base/logging.h"
25#include "rtc_base/strings/string_builder.h"
deadbeefe814a0d2017-02-25 18:15:09 -080026
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 13:40:02 +020042 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-25 18:15:09 -080049 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
83template <typename C>
84static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
85 C* cricket_codec);
86
87template <>
88RTCError 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.
120template <>
121RTCError 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
144template <typename C>
145RTCErrorOr<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)) {
Florent Castelli72b751a2018-06-28 14:09:33 +0200154 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());
deadbeefe814a0d2017-02-25 18:15:09 -0800158 }
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.insert(codec.parameters.begin(), codec.parameters.end());
168 return std::move(cricket_codec);
169}
170
171template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
172 const RtpCodecParameters& codec);
173template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
174 const RtpCodecParameters& codec);
175
176template <typename C>
177RTCErrorOr<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) {
Florent Castelli72b751a2018-06-28 14:09:33 +0200187 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());
deadbeefe814a0d2017-02-25 18:15:09 -0800191 }
192 cricket_codecs.push_back(result.MoveValue());
193 }
194 return std::move(cricket_codecs);
195}
196
197template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
198 cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
199
200template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
201 cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
202
deadbeefe814a0d2017-02-25 18:15:09 -0800203RTCErrorOr<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];
deadbeefe814a0d2017-02-25 18:15:09 -0800214 if (encoding.ssrc) {
215 cricket::StreamParams stream_params;
216 stream_params.add_ssrc(*encoding.ssrc);
deadbeefe814a0d2017-02-25 18:15:09 -0800217 cricket_streams.push_back(std::move(stream_params));
218 }
219 return std::move(cricket_streams);
220}
221
Danil Chapovalov00c71832018-06-15 15:58:38 +0200222absl::optional<RtcpFeedback> ToRtcpFeedback(
deadbeefe814a0d2017-02-25 18:15:09 -0800223 const cricket::FeedbackParam& cricket_feedback) {
224 if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
225 if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100226 return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR);
deadbeefe814a0d2017-02-25 18:15:09 -0800227 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100228 RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
229 << cricket_feedback.param();
Danil Chapovalov00c71832018-06-15 15:58:38 +0200230 return absl::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800231 }
Elad Alonfadb1812019-05-24 13:40:02 +0200232 } 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 }
deadbeefe814a0d2017-02-25 18:15:09 -0800240 } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
241 if (cricket_feedback.param().empty()) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100242 return RtcpFeedback(RtcpFeedbackType::NACK,
243 RtcpFeedbackMessageType::GENERIC_NACK);
deadbeefe814a0d2017-02-25 18:15:09 -0800244 } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100245 return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI);
deadbeefe814a0d2017-02-25 18:15:09 -0800246 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100247 RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
248 << cricket_feedback.param();
Danil Chapovalov00c71832018-06-15 15:58:38 +0200249 return absl::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800250 }
251 } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
252 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100253 RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
254 << cricket_feedback.param();
Danil Chapovalov00c71832018-06-15 15:58:38 +0200255 return absl::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800256 } else {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100257 return RtcpFeedback(RtcpFeedbackType::REMB);
deadbeefe814a0d2017-02-25 18:15:09 -0800258 }
259 } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
260 if (!cricket_feedback.param().empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100261 RTC_LOG(LS_WARNING)
deadbeefe814a0d2017-02-25 18:15:09 -0800262 << "Unsupported parameter for transport-cc RTCP feedback: "
263 << cricket_feedback.param();
Danil Chapovalov00c71832018-06-15 15:58:38 +0200264 return absl::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800265 } else {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100266 return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC);
deadbeefe814a0d2017-02-25 18:15:09 -0800267 }
268 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100269 RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
270 << cricket_feedback.id();
Danil Chapovalov00c71832018-06-15 15:58:38 +0200271 return absl::nullopt;
deadbeefe814a0d2017-02-25 18:15:09 -0800272}
273
zhihuang24366392017-03-08 17:15:06 -0800274std::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());
zhihuang24366392017-03-08 17:15:06 -0800280 rtp_encodings.push_back(std::move(rtp_encoding));
281 }
282 return rtp_encodings;
283}
284
deadbeefe814a0d2017-02-25 18:15:09 -0800285template <typename C>
286cricket::MediaType KindOfCodec();
287
288template <>
289cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
290 return cricket::MEDIA_TYPE_AUDIO;
291}
292
293template <>
294cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
295 return cricket::MEDIA_TYPE_VIDEO;
296}
297
298template <typename C>
299static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
300 RtpCodecCapability* codec);
301
302template <>
303void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
304 const cricket::AudioCodec& cricket_codec,
305 RtpCodecCapability* codec) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100306 codec->num_channels = static_cast<int>(cricket_codec.channels);
deadbeefe814a0d2017-02-25 18:15:09 -0800307}
308
309template <>
310void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
311 const cricket::VideoCodec& cricket_codec,
312 RtpCodecCapability* codec) {}
313
314template <typename C>
315RtpCodecCapability 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()) {
Danil Chapovalov00c71832018-06-15 15:58:38 +0200323 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
deadbeefe814a0d2017-02-25 18:15:09 -0800324 if (feedback) {
Danil Chapovalov57ff2732018-03-28 11:25:15 +0200325 codec.rtcp_feedback.push_back(feedback.value());
deadbeefe814a0d2017-02-25 18:15:09 -0800326 }
327 }
328 ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
329 codec.parameters.insert(cricket_codec.params.begin(),
330 cricket_codec.params.end());
331 return codec;
332}
333
334template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
335 const cricket::AudioCodec& cricket_codec);
336template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
337 const cricket::VideoCodec& cricket_codec);
338
zhihuang24366392017-03-08 17:15:06 -0800339template <typename C>
340static void ToRtpCodecParametersTypeSpecific(const C& cricket_codec,
341 RtpCodecParameters* codec);
342template <>
343void ToRtpCodecParametersTypeSpecific<cricket::AudioCodec>(
344 const cricket::AudioCodec& cricket_codec,
345 RtpCodecParameters* codec) {
Oskar Sundbomff610bd2017-11-16 10:57:44 +0100346 codec->num_channels = static_cast<int>(cricket_codec.channels);
zhihuang24366392017-03-08 17:15:06 -0800347}
348
349template <>
350void ToRtpCodecParametersTypeSpecific<cricket::VideoCodec>(
351 const cricket::VideoCodec& cricket_codec,
352 RtpCodecParameters* codec) {}
353
354template <typename C>
355RtpCodecParameters 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()) {
Danil Chapovalov00c71832018-06-15 15:58:38 +0200363 absl::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
zhihuang24366392017-03-08 17:15:06 -0800364 if (feedback) {
Danil Chapovalov57ff2732018-03-28 11:25:15 +0200365 codec_param.rtcp_feedback.push_back(feedback.value());
zhihuang24366392017-03-08 17:15:06 -0800366 }
367 }
368 ToRtpCodecParametersTypeSpecific(cricket_codec, &codec_param);
369 codec_param.parameters.insert(cricket_codec.params.begin(),
370 cricket_codec.params.end());
371 return codec_param;
372}
373
374template RtpCodecParameters ToRtpCodecParameters<cricket::AudioCodec>(
375 const cricket::AudioCodec& cricket_codec);
376template RtpCodecParameters ToRtpCodecParameters<cricket::VideoCodec>(
377 const cricket::VideoCodec& cricket_codec);
378
deadbeefe814a0d2017-02-25 18:15:09 -0800379template <class C>
380RtpCapabilities ToRtpCapabilities(
381 const std::vector<C>& cricket_codecs,
382 const cricket::RtpHeaderExtensions& cricket_extensions) {
383 RtpCapabilities capabilities;
384 bool have_red = false;
385 bool have_ulpfec = false;
386 bool have_flexfec = false;
Florent Castelli5473a452018-11-06 17:27:21 +0100387 bool have_rtx = false;
deadbeefe814a0d2017-02-25 18:15:09 -0800388 for (const C& cricket_codec : cricket_codecs) {
389 if (cricket_codec.name == cricket::kRedCodecName) {
390 have_red = true;
391 } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
392 have_ulpfec = true;
393 } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
394 have_flexfec = true;
Florent Castelli5473a452018-11-06 17:27:21 +0100395 } else if (cricket_codec.name == cricket::kRtxCodecName) {
396 if (have_rtx) {
397 // There should only be one RTX codec entry
398 continue;
399 }
400 have_rtx = true;
deadbeefe814a0d2017-02-25 18:15:09 -0800401 }
Florent Castelli5473a452018-11-06 17:27:21 +0100402 auto codec_capability = ToRtpCodecCapability(cricket_codec);
403 if (cricket_codec.name == cricket::kRtxCodecName) {
404 // RTX codec should not have any parameter
405 codec_capability.parameters.clear();
406 }
407 capabilities.codecs.push_back(codec_capability);
deadbeefe814a0d2017-02-25 18:15:09 -0800408 }
409 for (const RtpExtension& cricket_extension : cricket_extensions) {
410 capabilities.header_extensions.emplace_back(cricket_extension.uri,
411 cricket_extension.id);
412 }
413 if (have_red) {
414 capabilities.fec.push_back(FecMechanism::RED);
415 }
416 if (have_red && have_ulpfec) {
417 capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
418 }
419 if (have_flexfec) {
420 capabilities.fec.push_back(FecMechanism::FLEXFEC);
421 }
422 return capabilities;
423}
424
425template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
426 const std::vector<cricket::AudioCodec>& cricket_codecs,
427 const cricket::RtpHeaderExtensions& cricket_extensions);
428template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
429 const std::vector<cricket::VideoCodec>& cricket_codecs,
430 const cricket::RtpHeaderExtensions& cricket_extensions);
431
zhihuang24366392017-03-08 17:15:06 -0800432template <class C>
433RtpParameters ToRtpParameters(
434 const std::vector<C>& cricket_codecs,
435 const cricket::RtpHeaderExtensions& cricket_extensions,
436 const cricket::StreamParamsVec& stream_params) {
437 RtpParameters rtp_parameters;
438 for (const C& cricket_codec : cricket_codecs) {
439 rtp_parameters.codecs.push_back(ToRtpCodecParameters(cricket_codec));
440 }
441 for (const RtpExtension& cricket_extension : cricket_extensions) {
442 rtp_parameters.header_extensions.emplace_back(cricket_extension.uri,
443 cricket_extension.id);
444 }
445 rtp_parameters.encodings = ToRtpEncodings(stream_params);
446 return rtp_parameters;
447}
448
449template RtpParameters ToRtpParameters<cricket::AudioCodec>(
450 const std::vector<cricket::AudioCodec>& cricket_codecs,
451 const cricket::RtpHeaderExtensions& cricket_extensions,
452 const cricket::StreamParamsVec& stream_params);
453template RtpParameters ToRtpParameters<cricket::VideoCodec>(
454 const std::vector<cricket::VideoCodec>& cricket_codecs,
455 const cricket::RtpHeaderExtensions& cricket_extensions,
456 const cricket::StreamParamsVec& stream_params);
457
deadbeefe814a0d2017-02-25 18:15:09 -0800458} // namespace webrtc