blob: 5d5bf5ed7badbde39f7080decd6cdfba6cafec48 [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/rtptransportcontrolleradapter.h"
deadbeefe814a0d2017-02-25 18:15:09 -080012
13#include <algorithm> // For "remove", "find".
deadbeefe814a0d2017-02-25 18:15:09 -080014#include <set>
zhihuangd3501ad2017-03-03 14:39:06 -080015#include <sstream>
deadbeefe814a0d2017-02-25 18:15:09 -080016#include <unordered_map>
17#include <utility> // For std::move.
18
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "api/proxy.h"
20#include "media/base/mediaconstants.h"
21#include "ortc/ortcrtpreceiveradapter.h"
22#include "ortc/ortcrtpsenderadapter.h"
23#include "ortc/rtpparametersconversion.h"
24#include "ortc/rtptransportadapter.h"
Steve Anton1d03a752017-11-27 14:30:09 -080025#include "pc/rtpmediautils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "rtc_base/checks.h"
Sebastian Jansson9a03dd82018-02-22 10:31:14 +010027#include "rtc_base/ptr_util.h"
deadbeefe814a0d2017-02-25 18:15:09 -080028
29namespace webrtc {
30
31// Note: It's assumed that each individual list doesn't have conflicts, since
32// they should have been detected already by rtpparametersconversion.cc. This
33// only needs to detect conflicts *between* A and B.
34template <typename C1, typename C2>
35static RTCError CheckForIdConflicts(
36 const std::vector<C1>& codecs_a,
37 const cricket::RtpHeaderExtensions& extensions_a,
38 const cricket::StreamParamsVec& streams_a,
39 const std::vector<C2>& codecs_b,
40 const cricket::RtpHeaderExtensions& extensions_b,
41 const cricket::StreamParamsVec& streams_b) {
42 std::ostringstream oss;
43 // Since it's assumed that C1 and C2 are different types, codecs_a and
44 // codecs_b should never contain the same payload type, and thus we can just
45 // use a set.
46 std::set<int> seen_payload_types;
47 for (const C1& codec : codecs_a) {
48 seen_payload_types.insert(codec.id);
49 }
50 for (const C2& codec : codecs_b) {
51 if (!seen_payload_types.insert(codec.id).second) {
52 oss << "Same payload type used for audio and video codecs: " << codec.id;
53 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
54 }
55 }
56 // Audio and video *may* use the same header extensions, so use a map.
57 std::unordered_map<int, std::string> seen_extensions;
58 for (const webrtc::RtpExtension& extension : extensions_a) {
59 seen_extensions[extension.id] = extension.uri;
60 }
61 for (const webrtc::RtpExtension& extension : extensions_b) {
62 if (seen_extensions.find(extension.id) != seen_extensions.end() &&
63 seen_extensions.at(extension.id) != extension.uri) {
64 oss << "Same ID used for different RTP header extensions: "
65 << extension.id;
66 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
67 }
68 }
69 std::set<uint32_t> seen_ssrcs;
70 for (const cricket::StreamParams& stream : streams_a) {
71 seen_ssrcs.insert(stream.ssrcs.begin(), stream.ssrcs.end());
72 }
73 for (const cricket::StreamParams& stream : streams_b) {
74 for (uint32_t ssrc : stream.ssrcs) {
75 if (!seen_ssrcs.insert(ssrc).second) {
76 oss << "Same SSRC used for audio and video senders: " << ssrc;
77 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
78 }
79 }
80 }
81 return RTCError::OK();
82}
83
84BEGIN_OWNED_PROXY_MAP(RtpTransportController)
85PROXY_SIGNALING_THREAD_DESTRUCTOR()
86PROXY_CONSTMETHOD0(std::vector<RtpTransportInterface*>, GetTransports)
87protected:
88RtpTransportControllerAdapter* GetInternal() override {
89 return internal();
90}
91END_PROXY_MAP()
92
93// static
94std::unique_ptr<RtpTransportControllerInterface>
95RtpTransportControllerAdapter::CreateProxied(
96 const cricket::MediaConfig& config,
97 cricket::ChannelManager* channel_manager,
98 webrtc::RtcEventLog* event_log,
99 rtc::Thread* signaling_thread,
Zhi Huange830e682018-03-30 10:48:35 -0700100 rtc::Thread* worker_thread,
101 rtc::Thread* network_thread) {
deadbeefe814a0d2017-02-25 18:15:09 -0800102 std::unique_ptr<RtpTransportControllerAdapter> wrapped(
103 new RtpTransportControllerAdapter(config, channel_manager, event_log,
Zhi Huange830e682018-03-30 10:48:35 -0700104 signaling_thread, worker_thread,
105 network_thread));
deadbeefe814a0d2017-02-25 18:15:09 -0800106 return RtpTransportControllerProxyWithInternal<
107 RtpTransportControllerAdapter>::Create(signaling_thread, worker_thread,
108 std::move(wrapped));
109}
110
111RtpTransportControllerAdapter::~RtpTransportControllerAdapter() {
112 RTC_DCHECK_RUN_ON(signaling_thread_);
113 if (!transport_proxies_.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100114 RTC_LOG(LS_ERROR)
deadbeefe814a0d2017-02-25 18:15:09 -0800115 << "Destroying RtpTransportControllerAdapter while RtpTransports "
116 "are still using it; this is unsafe.";
117 }
118 if (voice_channel_) {
119 // This would mean audio RTP senders/receivers that are using us haven't
120 // been destroyed. This isn't safe (see error log above).
121 DestroyVoiceChannel();
122 }
123 if (voice_channel_) {
124 // This would mean video RTP senders/receivers that are using us haven't
125 // been destroyed. This isn't safe (see error log above).
126 DestroyVideoChannel();
127 }
nisseeaabdf62017-05-05 02:23:02 -0700128 // Call must be destroyed on the worker thread.
129 worker_thread_->Invoke<void>(
130 RTC_FROM_HERE,
131 rtc::Bind(&RtpTransportControllerAdapter::Close_w, this));
deadbeefe814a0d2017-02-25 18:15:09 -0800132}
133
134RTCErrorOr<std::unique_ptr<RtpTransportInterface>>
135RtpTransportControllerAdapter::CreateProxiedRtpTransport(
sprangdb2a9fc2017-08-09 06:42:32 -0700136 const RtpTransportParameters& parameters,
deadbeefe814a0d2017-02-25 18:15:09 -0800137 PacketTransportInterface* rtp,
138 PacketTransportInterface* rtcp) {
sprangdb2a9fc2017-08-09 06:42:32 -0700139 if (!transport_proxies_.empty() && (parameters.keepalive != keepalive_)) {
140 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
141 "Cannot create RtpTransport with different keep-alive "
142 "from the RtpTransports already associated with this "
143 "transport controller.");
144 }
145 auto result = RtpTransportAdapter::CreateProxied(parameters, rtp, rtcp, this);
deadbeefe814a0d2017-02-25 18:15:09 -0800146 if (result.ok()) {
147 transport_proxies_.push_back(result.value().get());
148 transport_proxies_.back()->GetInternal()->SignalDestroyed.connect(
149 this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed);
150 }
151 return result;
152}
153
zhihuangd3501ad2017-03-03 14:39:06 -0800154RTCErrorOr<std::unique_ptr<SrtpTransportInterface>>
155RtpTransportControllerAdapter::CreateProxiedSrtpTransport(
sprangdb2a9fc2017-08-09 06:42:32 -0700156 const RtpTransportParameters& parameters,
zhihuangd3501ad2017-03-03 14:39:06 -0800157 PacketTransportInterface* rtp,
158 PacketTransportInterface* rtcp) {
159 auto result =
sprangdb2a9fc2017-08-09 06:42:32 -0700160 RtpTransportAdapter::CreateSrtpProxied(parameters, rtp, rtcp, this);
zhihuangd3501ad2017-03-03 14:39:06 -0800161 if (result.ok()) {
162 transport_proxies_.push_back(result.value().get());
163 transport_proxies_.back()->GetInternal()->SignalDestroyed.connect(
164 this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed);
165 }
166 return result;
167}
168
deadbeefe814a0d2017-02-25 18:15:09 -0800169RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>
170RtpTransportControllerAdapter::CreateProxiedRtpSender(
171 cricket::MediaType kind,
172 RtpTransportInterface* transport_proxy) {
173 RTC_DCHECK(transport_proxy);
174 RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
175 transport_proxy) != transport_proxies_.end());
176 std::unique_ptr<OrtcRtpSenderAdapter> new_sender(
177 new OrtcRtpSenderAdapter(kind, transport_proxy, this));
178 RTCError err;
179 switch (kind) {
180 case cricket::MEDIA_TYPE_AUDIO:
181 err = AttachAudioSender(new_sender.get(), transport_proxy->GetInternal());
182 break;
183 case cricket::MEDIA_TYPE_VIDEO:
184 err = AttachVideoSender(new_sender.get(), transport_proxy->GetInternal());
185 break;
186 case cricket::MEDIA_TYPE_DATA:
187 RTC_NOTREACHED();
188 }
189 if (!err.ok()) {
190 return std::move(err);
191 }
192
193 return OrtcRtpSenderAdapter::CreateProxy(std::move(new_sender));
194}
195
196RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
197RtpTransportControllerAdapter::CreateProxiedRtpReceiver(
198 cricket::MediaType kind,
199 RtpTransportInterface* transport_proxy) {
200 RTC_DCHECK(transport_proxy);
201 RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
202 transport_proxy) != transport_proxies_.end());
203 std::unique_ptr<OrtcRtpReceiverAdapter> new_receiver(
204 new OrtcRtpReceiverAdapter(kind, transport_proxy, this));
205 RTCError err;
206 switch (kind) {
207 case cricket::MEDIA_TYPE_AUDIO:
208 err = AttachAudioReceiver(new_receiver.get(),
209 transport_proxy->GetInternal());
210 break;
211 case cricket::MEDIA_TYPE_VIDEO:
212 err = AttachVideoReceiver(new_receiver.get(),
213 transport_proxy->GetInternal());
214 break;
215 case cricket::MEDIA_TYPE_DATA:
216 RTC_NOTREACHED();
217 }
218 if (!err.ok()) {
219 return std::move(err);
220 }
221
222 return OrtcRtpReceiverAdapter::CreateProxy(std::move(new_receiver));
223}
224
225std::vector<RtpTransportInterface*>
226RtpTransportControllerAdapter::GetTransports() const {
227 RTC_DCHECK_RUN_ON(signaling_thread_);
228 return transport_proxies_;
229}
230
sprangdb2a9fc2017-08-09 06:42:32 -0700231RTCError RtpTransportControllerAdapter::SetRtpTransportParameters(
232 const RtpTransportParameters& parameters,
deadbeefe814a0d2017-02-25 18:15:09 -0800233 RtpTransportInterface* inner_transport) {
sprangdb2a9fc2017-08-09 06:42:32 -0700234 if ((video_channel_ != nullptr || voice_channel_ != nullptr) &&
235 (parameters.keepalive != keepalive_)) {
236 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
237 "Cannot change keep-alive settings after creating "
238 "media streams or additional transports for the same "
239 "transport controller.");
240 }
241 // Call must be configured on the worker thread.
242 worker_thread_->Invoke<void>(
243 RTC_FROM_HERE,
244 rtc::Bind(&RtpTransportControllerAdapter::SetRtpTransportParameters_w,
245 this, parameters));
246
deadbeefe814a0d2017-02-25 18:15:09 -0800247 do {
248 if (inner_transport == inner_audio_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700249 CopyRtcpParametersToDescriptions(parameters.rtcp,
250 &local_audio_description_,
deadbeefe814a0d2017-02-25 18:15:09 -0800251 &remote_audio_description_);
252 if (!voice_channel_->SetLocalContent(&local_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800253 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800254 break;
255 }
256 if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800257 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800258 break;
259 }
260 } else if (inner_transport == inner_video_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700261 CopyRtcpParametersToDescriptions(parameters.rtcp,
262 &local_video_description_,
deadbeefe814a0d2017-02-25 18:15:09 -0800263 &remote_video_description_);
264 if (!video_channel_->SetLocalContent(&local_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800265 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800266 break;
267 }
268 if (!video_channel_->SetRemoteContent(&remote_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800269 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800270 break;
271 }
272 }
273 return RTCError::OK();
274 } while (false);
275 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
276 "Failed to apply new RTCP parameters.");
277}
278
sprangdb2a9fc2017-08-09 06:42:32 -0700279void RtpTransportControllerAdapter::SetRtpTransportParameters_w(
280 const RtpTransportParameters& parameters) {
281 call_send_rtp_transport_controller_->SetKeepAliveConfig(parameters.keepalive);
282}
283
deadbeefe814a0d2017-02-25 18:15:09 -0800284RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioSenderParameters(
285 const RtpParameters& parameters,
286 uint32_t* primary_ssrc) {
287 RTC_DCHECK(voice_channel_);
288 RTC_DCHECK(have_audio_sender_);
289
290 auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
291 if (!codecs_result.ok()) {
292 return codecs_result.MoveError();
293 }
294
295 auto extensions_result =
296 ToCricketRtpHeaderExtensions(parameters.header_extensions);
297 if (!extensions_result.ok()) {
298 return extensions_result.MoveError();
299 }
300
301 auto stream_params_result = MakeSendStreamParamsVec(
sprangdb2a9fc2017-08-09 06:42:32 -0700302 parameters.encodings, inner_audio_transport_->GetParameters().rtcp.cname,
deadbeefe814a0d2017-02-25 18:15:09 -0800303 local_audio_description_);
304 if (!stream_params_result.ok()) {
305 return stream_params_result.MoveError();
306 }
307
308 // Check that audio/video sender aren't using the same IDs to refer to
309 // different things, if they share the same transport.
310 if (inner_audio_transport_ == inner_video_transport_) {
311 RTCError err = CheckForIdConflicts(
312 codecs_result.value(), extensions_result.value(),
313 stream_params_result.value(), remote_video_description_.codecs(),
314 remote_video_description_.rtp_header_extensions(),
315 local_video_description_.streams());
316 if (!err.ok()) {
317 return err;
318 }
319 }
320
Steve Anton4e70a722017-11-28 14:57:10 -0800321 bool local_send = false;
deadbeefe814a0d2017-02-25 18:15:09 -0800322 int bandwidth = cricket::kAutoBandwidth;
323 if (parameters.encodings.size() == 1u) {
324 if (parameters.encodings[0].max_bitrate_bps) {
325 bandwidth = *parameters.encodings[0].max_bitrate_bps;
326 }
Steve Anton1d03a752017-11-27 14:30:09 -0800327 local_send = parameters.encodings[0].active;
deadbeefe814a0d2017-02-25 18:15:09 -0800328 }
Steve Anton4e70a722017-11-28 14:57:10 -0800329 const bool local_recv =
330 RtpTransceiverDirectionHasRecv(local_audio_description_.direction());
331 const auto local_direction =
332 RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
deadbeefe814a0d2017-02-25 18:15:09 -0800333 if (primary_ssrc && !stream_params_result.value().empty()) {
334 *primary_ssrc = stream_params_result.value()[0].first_ssrc();
335 }
336
337 // Validation is done, so we can attempt applying the descriptions. Sent
338 // codecs and header extensions go in remote description, streams go in
339 // local.
340 //
341 // If there are no codecs or encodings, just leave the previous set of
342 // codecs. The media engine doesn't like an empty set of codecs.
343 if (local_audio_description_.streams().empty() &&
344 remote_audio_description_.codecs().empty()) {
345 } else {
346 remote_audio_description_.set_codecs(codecs_result.MoveValue());
347 }
348 remote_audio_description_.set_rtp_header_extensions(
349 extensions_result.MoveValue());
350 remote_audio_description_.set_bandwidth(bandwidth);
351 local_audio_description_.mutable_streams() = stream_params_result.MoveValue();
352 // Direction set based on encoding "active" flag.
Steve Anton4e70a722017-11-28 14:57:10 -0800353 local_audio_description_.set_direction(local_direction);
deadbeefe814a0d2017-02-25 18:15:09 -0800354 remote_audio_description_.set_direction(
Steve Anton4e70a722017-11-28 14:57:10 -0800355 RtpTransceiverDirectionReversed(local_direction));
deadbeefe814a0d2017-02-25 18:15:09 -0800356
357 // Set remote content first, to ensure the stream is created with the correct
358 // codec.
359 if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800360 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800361 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
362 "Failed to apply remote parameters to media channel.");
363 }
364 if (!voice_channel_->SetLocalContent(&local_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800365 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800366 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
367 "Failed to apply local parameters to media channel.");
368 }
369 return RTCError::OK();
370}
371
372RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoSenderParameters(
373 const RtpParameters& parameters,
374 uint32_t* primary_ssrc) {
375 RTC_DCHECK(video_channel_);
376 RTC_DCHECK(have_video_sender_);
377
378 auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
379 if (!codecs_result.ok()) {
380 return codecs_result.MoveError();
381 }
382
383 auto extensions_result =
384 ToCricketRtpHeaderExtensions(parameters.header_extensions);
385 if (!extensions_result.ok()) {
386 return extensions_result.MoveError();
387 }
388
389 auto stream_params_result = MakeSendStreamParamsVec(
sprangdb2a9fc2017-08-09 06:42:32 -0700390 parameters.encodings, inner_video_transport_->GetParameters().rtcp.cname,
deadbeefe814a0d2017-02-25 18:15:09 -0800391 local_video_description_);
392 if (!stream_params_result.ok()) {
393 return stream_params_result.MoveError();
394 }
395
396 // Check that audio/video sender aren't using the same IDs to refer to
397 // different things, if they share the same transport.
398 if (inner_audio_transport_ == inner_video_transport_) {
399 RTCError err = CheckForIdConflicts(
400 codecs_result.value(), extensions_result.value(),
401 stream_params_result.value(), remote_audio_description_.codecs(),
402 remote_audio_description_.rtp_header_extensions(),
403 local_audio_description_.streams());
404 if (!err.ok()) {
405 return err;
406 }
407 }
408
Steve Anton4e70a722017-11-28 14:57:10 -0800409 bool local_send = false;
deadbeefe814a0d2017-02-25 18:15:09 -0800410 int bandwidth = cricket::kAutoBandwidth;
411 if (parameters.encodings.size() == 1u) {
412 if (parameters.encodings[0].max_bitrate_bps) {
413 bandwidth = *parameters.encodings[0].max_bitrate_bps;
414 }
Steve Anton1d03a752017-11-27 14:30:09 -0800415 local_send = parameters.encodings[0].active;
deadbeefe814a0d2017-02-25 18:15:09 -0800416 }
Steve Anton4e70a722017-11-28 14:57:10 -0800417 const bool local_recv =
418 RtpTransceiverDirectionHasRecv(local_audio_description_.direction());
419 const auto local_direction =
420 RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
deadbeefe814a0d2017-02-25 18:15:09 -0800421 if (primary_ssrc && !stream_params_result.value().empty()) {
422 *primary_ssrc = stream_params_result.value()[0].first_ssrc();
423 }
424
425 // Validation is done, so we can attempt applying the descriptions. Sent
426 // codecs and header extensions go in remote description, streams go in
427 // local.
428 //
429 // If there are no codecs or encodings, just leave the previous set of
430 // codecs. The media engine doesn't like an empty set of codecs.
431 if (local_video_description_.streams().empty() &&
432 remote_video_description_.codecs().empty()) {
433 } else {
434 remote_video_description_.set_codecs(codecs_result.MoveValue());
435 }
436 remote_video_description_.set_rtp_header_extensions(
437 extensions_result.MoveValue());
438 remote_video_description_.set_bandwidth(bandwidth);
439 local_video_description_.mutable_streams() = stream_params_result.MoveValue();
440 // Direction set based on encoding "active" flag.
Steve Anton4e70a722017-11-28 14:57:10 -0800441 local_video_description_.set_direction(local_direction);
deadbeefe814a0d2017-02-25 18:15:09 -0800442 remote_video_description_.set_direction(
Steve Anton4e70a722017-11-28 14:57:10 -0800443 RtpTransceiverDirectionReversed(local_direction));
deadbeefe814a0d2017-02-25 18:15:09 -0800444
445 // Set remote content first, to ensure the stream is created with the correct
446 // codec.
447 if (!video_channel_->SetRemoteContent(&remote_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800448 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800449 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
450 "Failed to apply remote parameters to media channel.");
451 }
452 if (!video_channel_->SetLocalContent(&local_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800453 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800454 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
455 "Failed to apply local parameters to media channel.");
456 }
457 return RTCError::OK();
458}
459
460RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioReceiverParameters(
461 const RtpParameters& parameters) {
462 RTC_DCHECK(voice_channel_);
463 RTC_DCHECK(have_audio_receiver_);
464
465 auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
466 if (!codecs_result.ok()) {
467 return codecs_result.MoveError();
468 }
469
470 auto extensions_result =
471 ToCricketRtpHeaderExtensions(parameters.header_extensions);
472 if (!extensions_result.ok()) {
473 return extensions_result.MoveError();
474 }
475
deadbeefe814a0d2017-02-25 18:15:09 -0800476 auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
477 if (!stream_params_result.ok()) {
478 return stream_params_result.MoveError();
479 }
480
481 // Check that audio/video receive aren't using the same IDs to refer to
482 // different things, if they share the same transport.
483 if (inner_audio_transport_ == inner_video_transport_) {
484 RTCError err = CheckForIdConflicts(
485 codecs_result.value(), extensions_result.value(),
486 stream_params_result.value(), local_video_description_.codecs(),
487 local_video_description_.rtp_header_extensions(),
488 remote_video_description_.streams());
489 if (!err.ok()) {
490 return err;
491 }
492 }
493
Steve Anton4e70a722017-11-28 14:57:10 -0800494 const bool local_send =
495 RtpTransceiverDirectionHasSend(local_audio_description_.direction());
496 const bool local_recv =
deadbeefe814a0d2017-02-25 18:15:09 -0800497 !parameters.encodings.empty() && parameters.encodings[0].active;
Steve Anton4e70a722017-11-28 14:57:10 -0800498 const auto local_direction =
Steve Anton1d03a752017-11-27 14:30:09 -0800499 RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
deadbeefe814a0d2017-02-25 18:15:09 -0800500
501 // Validation is done, so we can attempt applying the descriptions. Received
502 // codecs and header extensions go in local description, streams go in
503 // remote.
504 //
505 // If there are no codecs or encodings, just leave the previous set of
506 // codecs. The media engine doesn't like an empty set of codecs.
507 if (remote_audio_description_.streams().empty() &&
508 local_audio_description_.codecs().empty()) {
509 } else {
510 local_audio_description_.set_codecs(codecs_result.MoveValue());
511 }
512 local_audio_description_.set_rtp_header_extensions(
513 extensions_result.MoveValue());
514 remote_audio_description_.mutable_streams() =
515 stream_params_result.MoveValue();
516 // Direction set based on encoding "active" flag.
Steve Anton4e70a722017-11-28 14:57:10 -0800517 local_audio_description_.set_direction(local_direction);
deadbeefe814a0d2017-02-25 18:15:09 -0800518 remote_audio_description_.set_direction(
Steve Anton4e70a722017-11-28 14:57:10 -0800519 RtpTransceiverDirectionReversed(local_direction));
deadbeefe814a0d2017-02-25 18:15:09 -0800520
521 if (!voice_channel_->SetLocalContent(&local_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800522 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800523 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
524 "Failed to apply local parameters to media channel.");
525 }
526 if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800527 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800528 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
529 "Failed to apply remote parameters to media channel.");
530 }
531 return RTCError::OK();
532}
533
534RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoReceiverParameters(
535 const RtpParameters& parameters) {
536 RTC_DCHECK(video_channel_);
537 RTC_DCHECK(have_video_receiver_);
538
539 auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
540 if (!codecs_result.ok()) {
541 return codecs_result.MoveError();
542 }
543
544 auto extensions_result =
545 ToCricketRtpHeaderExtensions(parameters.header_extensions);
546 if (!extensions_result.ok()) {
547 return extensions_result.MoveError();
548 }
549
deadbeefe814a0d2017-02-25 18:15:09 -0800550 int bandwidth = cricket::kAutoBandwidth;
551 auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
552 if (!stream_params_result.ok()) {
553 return stream_params_result.MoveError();
554 }
555
556 // Check that audio/video receiver aren't using the same IDs to refer to
557 // different things, if they share the same transport.
558 if (inner_audio_transport_ == inner_video_transport_) {
559 RTCError err = CheckForIdConflicts(
560 codecs_result.value(), extensions_result.value(),
561 stream_params_result.value(), local_audio_description_.codecs(),
562 local_audio_description_.rtp_header_extensions(),
563 remote_audio_description_.streams());
564 if (!err.ok()) {
565 return err;
566 }
567 }
568
Steve Anton4e70a722017-11-28 14:57:10 -0800569 const bool local_send =
570 RtpTransceiverDirectionHasSend(local_video_description_.direction());
571 const bool local_recv =
deadbeefe814a0d2017-02-25 18:15:09 -0800572 !parameters.encodings.empty() && parameters.encodings[0].active;
Steve Anton4e70a722017-11-28 14:57:10 -0800573 const auto local_direction =
Steve Anton1d03a752017-11-27 14:30:09 -0800574 RtpTransceiverDirectionFromSendRecv(local_send, local_recv);
deadbeefe814a0d2017-02-25 18:15:09 -0800575
576 // Validation is done, so we can attempt applying the descriptions. Received
577 // codecs and header extensions go in local description, streams go in
578 // remote.
579 //
580 // If there are no codecs or encodings, just leave the previous set of
581 // codecs. The media engine doesn't like an empty set of codecs.
582 if (remote_video_description_.streams().empty() &&
583 local_video_description_.codecs().empty()) {
584 } else {
585 local_video_description_.set_codecs(codecs_result.MoveValue());
586 }
587 local_video_description_.set_rtp_header_extensions(
588 extensions_result.MoveValue());
589 local_video_description_.set_bandwidth(bandwidth);
590 remote_video_description_.mutable_streams() =
591 stream_params_result.MoveValue();
592 // Direction set based on encoding "active" flag.
Steve Anton4e70a722017-11-28 14:57:10 -0800593 local_video_description_.set_direction(local_direction);
deadbeefe814a0d2017-02-25 18:15:09 -0800594 remote_video_description_.set_direction(
Steve Anton4e70a722017-11-28 14:57:10 -0800595 RtpTransceiverDirectionReversed(local_direction));
deadbeefe814a0d2017-02-25 18:15:09 -0800596
597 if (!video_channel_->SetLocalContent(&local_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800598 SdpType::kOffer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800599 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
600 "Failed to apply local parameters to media channel.");
601 }
602 if (!video_channel_->SetRemoteContent(&remote_video_description_,
Steve Anton3828c062017-12-06 10:34:51 -0800603 SdpType::kAnswer, nullptr)) {
deadbeefe814a0d2017-02-25 18:15:09 -0800604 LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
605 "Failed to apply remote parameters to media channel.");
606 }
607 return RTCError::OK();
608}
609
610RtpTransportControllerAdapter::RtpTransportControllerAdapter(
611 const cricket::MediaConfig& config,
612 cricket::ChannelManager* channel_manager,
613 webrtc::RtcEventLog* event_log,
614 rtc::Thread* signaling_thread,
Zhi Huange830e682018-03-30 10:48:35 -0700615 rtc::Thread* worker_thread,
616 rtc::Thread* network_thread)
deadbeefe814a0d2017-02-25 18:15:09 -0800617 : signaling_thread_(signaling_thread),
618 worker_thread_(worker_thread),
Zhi Huange830e682018-03-30 10:48:35 -0700619 network_thread_(network_thread),
nisseeaabdf62017-05-05 02:23:02 -0700620 media_config_(config),
621 channel_manager_(channel_manager),
sprangdb2a9fc2017-08-09 06:42:32 -0700622 event_log_(event_log),
623 call_send_rtp_transport_controller_(nullptr) {
deadbeefe814a0d2017-02-25 18:15:09 -0800624 RTC_DCHECK_RUN_ON(signaling_thread_);
nisseeaabdf62017-05-05 02:23:02 -0700625 RTC_DCHECK(channel_manager_);
deadbeefe814a0d2017-02-25 18:15:09 -0800626 // Add "dummy" codecs to the descriptions, because the media engines
627 // currently reject empty lists of codecs. Note that these codecs will never
628 // actually be used, because when parameters are set, the dummy codecs will
629 // be replaced by actual codecs before any send/receive streams are created.
630 static const cricket::AudioCodec dummy_audio(0, cricket::kPcmuCodecName, 8000,
631 0, 1);
632 static const cricket::VideoCodec dummy_video(96, cricket::kVp8CodecName);
633 local_audio_description_.AddCodec(dummy_audio);
634 remote_audio_description_.AddCodec(dummy_audio);
635 local_video_description_.AddCodec(dummy_video);
636 remote_video_description_.AddCodec(dummy_video);
nisseeaabdf62017-05-05 02:23:02 -0700637
638 worker_thread_->Invoke<void>(
639 RTC_FROM_HERE,
640 rtc::Bind(&RtpTransportControllerAdapter::Init_w, this));
641}
642
643// TODO(nisse): Duplicates corresponding method in PeerConnection (used
644// to be in MediaController).
645void RtpTransportControllerAdapter::Init_w() {
646 RTC_DCHECK(worker_thread_->IsCurrent());
647 RTC_DCHECK(!call_);
648
649 const int kMinBandwidthBps = 30000;
650 const int kStartBandwidthBps = 300000;
651 const int kMaxBandwidthBps = 2000000;
652
653 webrtc::Call::Config call_config(event_log_);
654 call_config.audio_state = channel_manager_->media_engine()->GetAudioState();
655 call_config.bitrate_config.min_bitrate_bps = kMinBandwidthBps;
656 call_config.bitrate_config.start_bitrate_bps = kStartBandwidthBps;
657 call_config.bitrate_config.max_bitrate_bps = kMaxBandwidthBps;
Sebastian Jansson9a03dd82018-02-22 10:31:14 +0100658 std::unique_ptr<RtpTransportControllerSend> controller_send =
659 rtc::MakeUnique<RtpTransportControllerSend>(
Sebastian Janssondfce03a2018-05-18 18:05:10 +0200660 Clock::GetRealTimeClock(), event_log_,
661 call_config.network_controller_factory, call_config.bitrate_config);
Sebastian Jansson9a03dd82018-02-22 10:31:14 +0100662 call_send_rtp_transport_controller_ = controller_send.get();
663 call_.reset(webrtc::Call::Create(call_config, std::move(controller_send)));
nisseeaabdf62017-05-05 02:23:02 -0700664}
665
666void RtpTransportControllerAdapter::Close_w() {
667 call_.reset();
sprangdb2a9fc2017-08-09 06:42:32 -0700668 call_send_rtp_transport_controller_ = nullptr;
deadbeefe814a0d2017-02-25 18:15:09 -0800669}
670
671RTCError RtpTransportControllerAdapter::AttachAudioSender(
672 OrtcRtpSenderAdapter* sender,
673 RtpTransportInterface* inner_transport) {
674 if (have_audio_sender_) {
675 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
676 "Using two audio RtpSenders with the same "
677 "RtpTransportControllerAdapter is not currently "
678 "supported.");
679 }
680 if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
681 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
682 "Using different transports for the audio "
683 "RtpSender and RtpReceiver is not currently "
684 "supported.");
685 }
Zhi Huange830e682018-03-30 10:48:35 -0700686
deadbeefe814a0d2017-02-25 18:15:09 -0800687 // If setting new transport, extract its RTCP parameters and create voice
688 // channel.
689 if (!inner_audio_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700690 CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
deadbeefe814a0d2017-02-25 18:15:09 -0800691 &local_audio_description_,
692 &remote_audio_description_);
693 inner_audio_transport_ = inner_transport;
694 CreateVoiceChannel();
695 }
696 have_audio_sender_ = true;
697 sender->SignalDestroyed.connect(
698 this, &RtpTransportControllerAdapter::OnAudioSenderDestroyed);
699 return RTCError::OK();
700}
701
702RTCError RtpTransportControllerAdapter::AttachVideoSender(
703 OrtcRtpSenderAdapter* sender,
704 RtpTransportInterface* inner_transport) {
705 if (have_video_sender_) {
706 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
707 "Using two video RtpSenders with the same "
708 "RtpTransportControllerAdapter is not currently "
709 "supported.");
710 }
711 if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
712 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
713 "Using different transports for the video "
714 "RtpSender and RtpReceiver is not currently "
715 "supported.");
716 }
Zhi Huange830e682018-03-30 10:48:35 -0700717
deadbeefe814a0d2017-02-25 18:15:09 -0800718 // If setting new transport, extract its RTCP parameters and create video
719 // channel.
720 if (!inner_video_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700721 CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
deadbeefe814a0d2017-02-25 18:15:09 -0800722 &local_video_description_,
723 &remote_video_description_);
724 inner_video_transport_ = inner_transport;
725 CreateVideoChannel();
726 }
727 have_video_sender_ = true;
728 sender->SignalDestroyed.connect(
729 this, &RtpTransportControllerAdapter::OnVideoSenderDestroyed);
730 return RTCError::OK();
731}
732
733RTCError RtpTransportControllerAdapter::AttachAudioReceiver(
734 OrtcRtpReceiverAdapter* receiver,
735 RtpTransportInterface* inner_transport) {
736 if (have_audio_receiver_) {
737 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
738 "Using two audio RtpReceivers with the same "
739 "RtpTransportControllerAdapter is not currently "
740 "supported.");
741 }
742 if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
743 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
744 "Using different transports for the audio "
745 "RtpReceiver and RtpReceiver is not currently "
746 "supported.");
747 }
Zhi Huange830e682018-03-30 10:48:35 -0700748
deadbeefe814a0d2017-02-25 18:15:09 -0800749 // If setting new transport, extract its RTCP parameters and create voice
750 // channel.
751 if (!inner_audio_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700752 CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
deadbeefe814a0d2017-02-25 18:15:09 -0800753 &local_audio_description_,
754 &remote_audio_description_);
755 inner_audio_transport_ = inner_transport;
756 CreateVoiceChannel();
757 }
758 have_audio_receiver_ = true;
759 receiver->SignalDestroyed.connect(
760 this, &RtpTransportControllerAdapter::OnAudioReceiverDestroyed);
761 return RTCError::OK();
762}
763
764RTCError RtpTransportControllerAdapter::AttachVideoReceiver(
765 OrtcRtpReceiverAdapter* receiver,
766 RtpTransportInterface* inner_transport) {
767 if (have_video_receiver_) {
768 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
769 "Using two video RtpReceivers with the same "
770 "RtpTransportControllerAdapter is not currently "
771 "supported.");
772 }
773 if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
774 LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
775 "Using different transports for the video "
776 "RtpReceiver and RtpReceiver is not currently "
777 "supported.");
778 }
779 // If setting new transport, extract its RTCP parameters and create video
780 // channel.
781 if (!inner_video_transport_) {
sprangdb2a9fc2017-08-09 06:42:32 -0700782 CopyRtcpParametersToDescriptions(inner_transport->GetParameters().rtcp,
deadbeefe814a0d2017-02-25 18:15:09 -0800783 &local_video_description_,
784 &remote_video_description_);
785 inner_video_transport_ = inner_transport;
786 CreateVideoChannel();
787 }
788 have_video_receiver_ = true;
789 receiver->SignalDestroyed.connect(
790 this, &RtpTransportControllerAdapter::OnVideoReceiverDestroyed);
791 return RTCError::OK();
792}
793
794void RtpTransportControllerAdapter::OnRtpTransportDestroyed(
795 RtpTransportAdapter* transport) {
796 RTC_DCHECK_RUN_ON(signaling_thread_);
797 auto it = std::find_if(transport_proxies_.begin(), transport_proxies_.end(),
798 [transport](RtpTransportInterface* proxy) {
799 return proxy->GetInternal() == transport;
800 });
801 if (it == transport_proxies_.end()) {
802 RTC_NOTREACHED();
803 return;
804 }
805 transport_proxies_.erase(it);
806}
807
808void RtpTransportControllerAdapter::OnAudioSenderDestroyed() {
809 if (!have_audio_sender_) {
810 RTC_NOTREACHED();
811 return;
812 }
813 // Empty parameters should result in sending being stopped.
814 RTCError err =
815 ValidateAndApplyAudioSenderParameters(RtpParameters(), nullptr);
816 RTC_DCHECK(err.ok());
817 have_audio_sender_ = false;
818 if (!have_audio_receiver_) {
819 DestroyVoiceChannel();
820 }
821}
822
823void RtpTransportControllerAdapter::OnVideoSenderDestroyed() {
824 if (!have_video_sender_) {
825 RTC_NOTREACHED();
826 return;
827 }
828 // Empty parameters should result in sending being stopped.
829 RTCError err =
830 ValidateAndApplyVideoSenderParameters(RtpParameters(), nullptr);
831 RTC_DCHECK(err.ok());
832 have_video_sender_ = false;
833 if (!have_video_receiver_) {
834 DestroyVideoChannel();
835 }
836}
837
838void RtpTransportControllerAdapter::OnAudioReceiverDestroyed() {
839 if (!have_audio_receiver_) {
840 RTC_NOTREACHED();
841 return;
842 }
843 // Empty parameters should result in receiving being stopped.
844 RTCError err = ValidateAndApplyAudioReceiverParameters(RtpParameters());
845 RTC_DCHECK(err.ok());
846 have_audio_receiver_ = false;
847 if (!have_audio_sender_) {
848 DestroyVoiceChannel();
849 }
850}
851
852void RtpTransportControllerAdapter::OnVideoReceiverDestroyed() {
853 if (!have_video_receiver_) {
854 RTC_NOTREACHED();
855 return;
856 }
857 // Empty parameters should result in receiving being stopped.
858 RTCError err = ValidateAndApplyVideoReceiverParameters(RtpParameters());
859 RTC_DCHECK(err.ok());
860 have_video_receiver_ = false;
861 if (!have_video_sender_) {
862 DestroyVideoChannel();
863 }
864}
865
866void RtpTransportControllerAdapter::CreateVoiceChannel() {
nisseeaabdf62017-05-05 02:23:02 -0700867 voice_channel_ = channel_manager_->CreateVoiceChannel(
Zhi Huange830e682018-03-30 10:48:35 -0700868 call_.get(), media_config_, inner_audio_transport_->GetInternal(),
869 signaling_thread_, "audio", false, rtc::CryptoOptions(),
870 cricket::AudioOptions());
deadbeefe814a0d2017-02-25 18:15:09 -0800871 RTC_DCHECK(voice_channel_);
872 voice_channel_->Enable(true);
873}
874
875void RtpTransportControllerAdapter::CreateVideoChannel() {
nisseeaabdf62017-05-05 02:23:02 -0700876 video_channel_ = channel_manager_->CreateVideoChannel(
Zhi Huange830e682018-03-30 10:48:35 -0700877 call_.get(), media_config_, inner_video_transport_->GetInternal(),
878 signaling_thread_, "video", false, rtc::CryptoOptions(),
879 cricket::VideoOptions());
deadbeefe814a0d2017-02-25 18:15:09 -0800880 RTC_DCHECK(video_channel_);
881 video_channel_->Enable(true);
882}
883
884void RtpTransportControllerAdapter::DestroyVoiceChannel() {
885 RTC_DCHECK(voice_channel_);
nisseeaabdf62017-05-05 02:23:02 -0700886 channel_manager_->DestroyVoiceChannel(voice_channel_);
deadbeefe814a0d2017-02-25 18:15:09 -0800887 voice_channel_ = nullptr;
888 inner_audio_transport_ = nullptr;
889}
890
891void RtpTransportControllerAdapter::DestroyVideoChannel() {
892 RTC_DCHECK(video_channel_);
nisseeaabdf62017-05-05 02:23:02 -0700893 channel_manager_->DestroyVideoChannel(video_channel_);
deadbeefe814a0d2017-02-25 18:15:09 -0800894 video_channel_ = nullptr;
895 inner_video_transport_ = nullptr;
896}
897
898void RtpTransportControllerAdapter::CopyRtcpParametersToDescriptions(
899 const RtcpParameters& params,
900 cricket::MediaContentDescription* local,
901 cricket::MediaContentDescription* remote) {
902 local->set_rtcp_mux(params.mux);
903 remote->set_rtcp_mux(params.mux);
904 local->set_rtcp_reduced_size(params.reduced_size);
905 remote->set_rtcp_reduced_size(params.reduced_size);
906 for (cricket::StreamParams& stream_params : local->mutable_streams()) {
907 stream_params.cname = params.cname;
908 }
909}
910
911uint32_t RtpTransportControllerAdapter::GenerateUnusedSsrc(
912 std::set<uint32_t>* new_ssrcs) const {
913 uint32_t ssrc;
914 do {
915 ssrc = rtc::CreateRandomNonZeroId();
916 } while (
917 cricket::GetStreamBySsrc(local_audio_description_.streams(), ssrc) ||
918 cricket::GetStreamBySsrc(remote_audio_description_.streams(), ssrc) ||
919 cricket::GetStreamBySsrc(local_video_description_.streams(), ssrc) ||
920 cricket::GetStreamBySsrc(remote_video_description_.streams(), ssrc) ||
921 !new_ssrcs->insert(ssrc).second);
922 return ssrc;
923}
924
925RTCErrorOr<cricket::StreamParamsVec>
926RtpTransportControllerAdapter::MakeSendStreamParamsVec(
927 std::vector<RtpEncodingParameters> encodings,
928 const std::string& cname,
929 const cricket::MediaContentDescription& description) const {
930 if (encodings.size() > 1u) {
931 LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER,
932 "ORTC API implementation doesn't currently "
933 "support simulcast or layered encodings.");
934 } else if (encodings.empty()) {
935 return cricket::StreamParamsVec();
936 }
937 RtpEncodingParameters& encoding = encodings[0];
938 std::set<uint32_t> new_ssrcs;
939 if (encoding.ssrc) {
940 new_ssrcs.insert(*encoding.ssrc);
941 }
942 if (encoding.rtx && encoding.rtx->ssrc) {
943 new_ssrcs.insert(*encoding.rtx->ssrc);
944 }
945 // May need to fill missing SSRCs with generated ones.
946 if (!encoding.ssrc) {
947 if (!description.streams().empty()) {
948 encoding.ssrc.emplace(description.streams()[0].first_ssrc());
949 } else {
950 encoding.ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
951 }
952 }
953 if (encoding.rtx && !encoding.rtx->ssrc) {
954 uint32_t existing_rtx_ssrc;
955 if (!description.streams().empty() &&
956 description.streams()[0].GetFidSsrc(
957 description.streams()[0].first_ssrc(), &existing_rtx_ssrc)) {
958 encoding.rtx->ssrc.emplace(existing_rtx_ssrc);
959 } else {
960 encoding.rtx->ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
961 }
962 }
963
964 auto result = ToCricketStreamParamsVec(encodings);
965 if (!result.ok()) {
966 return result.MoveError();
967 }
968 // If conversion was successful, there should be one StreamParams.
969 RTC_DCHECK_EQ(1u, result.value().size());
970 result.value()[0].cname = cname;
971 return result;
972}
973
deadbeefe814a0d2017-02-25 18:15:09 -0800974} // namespace webrtc