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_transceiver.h"
12
13 #include <algorithm>
14 #include <iterator>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include "absl/algorithm/container.h"
20 #include "absl/memory/memory.h"
21 #include "api/peer_connection_interface.h"
22 #include "api/rtp_parameters.h"
23 #include "api/sequence_checker.h"
24 #include "media/base/codec.h"
25 #include "media/base/media_constants.h"
26 #include "media/base/media_engine.h"
27 #include "pc/channel.h"
28 #include "pc/rtp_media_utils.h"
29 #include "pc/session_description.h"
30 #include "rtc_base/checks.h"
31 #include "rtc_base/logging.h"
32 #include "rtc_base/thread.h"
33
34 namespace webrtc {
35 namespace {
36 template <class T>
VerifyCodecPreferences(const std::vector<RtpCodecCapability> & codecs,const std::vector<T> & send_codecs,const std::vector<T> & recv_codecs)37 RTCError VerifyCodecPreferences(const std::vector<RtpCodecCapability>& codecs,
38 const std::vector<T>& send_codecs,
39 const std::vector<T>& recv_codecs) {
40 // If the intersection between codecs and
41 // RTCRtpSender.getCapabilities(kind).codecs or the intersection between
42 // codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX,
43 // RED or FEC codecs or is an empty set, throw InvalidModificationError.
44 // This ensures that we always have something to offer, regardless of
45 // transceiver.direction.
46
47 if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) {
48 return codec.name != cricket::kRtxCodecName &&
49 codec.name != cricket::kRedCodecName &&
50 codec.name != cricket::kFlexfecCodecName &&
51 absl::c_any_of(recv_codecs, [&codec](const T& recv_codec) {
52 return recv_codec.MatchesCapability(codec);
53 });
54 })) {
55 return RTCError(RTCErrorType::INVALID_MODIFICATION,
56 "Invalid codec preferences: Missing codec from recv "
57 "codec capabilities.");
58 }
59
60 if (!absl::c_any_of(codecs, [&send_codecs](const RtpCodecCapability& codec) {
61 return codec.name != cricket::kRtxCodecName &&
62 codec.name != cricket::kRedCodecName &&
63 codec.name != cricket::kFlexfecCodecName &&
64 absl::c_any_of(send_codecs, [&codec](const T& send_codec) {
65 return send_codec.MatchesCapability(codec);
66 });
67 })) {
68 return RTCError(RTCErrorType::INVALID_MODIFICATION,
69 "Invalid codec preferences: Missing codec from send "
70 "codec capabilities.");
71 }
72
73 // Let codecCapabilities be the union of
74 // RTCRtpSender.getCapabilities(kind).codecs and
75 // RTCRtpReceiver.getCapabilities(kind).codecs. For each codec in codecs, If
76 // codec is not in codecCapabilities, throw InvalidModificationError.
77 for (const auto& codec_preference : codecs) {
78 bool is_recv_codec =
79 absl::c_any_of(recv_codecs, [&codec_preference](const T& codec) {
80 return codec.MatchesCapability(codec_preference);
81 });
82
83 bool is_send_codec =
84 absl::c_any_of(send_codecs, [&codec_preference](const T& codec) {
85 return codec.MatchesCapability(codec_preference);
86 });
87
88 if (!is_recv_codec && !is_send_codec) {
89 return RTCError(
90 RTCErrorType::INVALID_MODIFICATION,
91 std::string("Invalid codec preferences: invalid codec with name \"") +
92 codec_preference.name + "\".");
93 }
94 }
95
96 // Check we have a real codec (not just rtx, red or fec)
97 if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) {
98 return codec.name == cricket::kRtxCodecName ||
99 codec.name == cricket::kRedCodecName ||
100 codec.name == cricket::kUlpfecCodecName;
101 })) {
102 return RTCError(RTCErrorType::INVALID_MODIFICATION,
103 "Invalid codec preferences: codec list must have a non "
104 "RTX, RED or FEC entry.");
105 }
106
107 return RTCError::OK();
108 }
109
110 // Matches the list of codecs as capabilities (potentially without SVC related
111 // information) to the list of send codecs and returns the list of codecs with
112 // all the SVC related information.
MatchCodecPreferences(const std::vector<RtpCodecCapability> & codecs,const std::vector<cricket::VideoCodec> & send_codecs)113 std::vector<cricket::VideoCodec> MatchCodecPreferences(
114 const std::vector<RtpCodecCapability>& codecs,
115 const std::vector<cricket::VideoCodec>& send_codecs) {
116 std::vector<cricket::VideoCodec> result;
117
118 for (const auto& codec_preference : codecs) {
119 for (const cricket::VideoCodec& send_codec : send_codecs) {
120 if (send_codec.MatchesCapability(codec_preference)) {
121 result.push_back(send_codec);
122 }
123 }
124 }
125
126 return result;
127 }
128
GetCurrentTaskQueueOrThread()129 TaskQueueBase* GetCurrentTaskQueueOrThread() {
130 TaskQueueBase* current = TaskQueueBase::Current();
131 if (!current)
132 current = rtc::ThreadManager::Instance()->CurrentThread();
133 return current;
134 }
135
136 } // namespace
137
RtpTransceiver(cricket::MediaType media_type,ConnectionContext * context)138 RtpTransceiver::RtpTransceiver(cricket::MediaType media_type,
139 ConnectionContext* context)
140 : thread_(GetCurrentTaskQueueOrThread()),
141 unified_plan_(false),
142 media_type_(media_type),
143 context_(context) {
144 RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
145 media_type == cricket::MEDIA_TYPE_VIDEO);
146 }
147
RtpTransceiver(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver,ConnectionContext * context,std::vector<RtpHeaderExtensionCapability> header_extensions_offered,std::function<void ()> on_negotiation_needed)148 RtpTransceiver::RtpTransceiver(
149 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
150 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
151 receiver,
152 ConnectionContext* context,
153 std::vector<RtpHeaderExtensionCapability> header_extensions_offered,
154 std::function<void()> on_negotiation_needed)
155 : thread_(GetCurrentTaskQueueOrThread()),
156 unified_plan_(true),
157 media_type_(sender->media_type()),
158 context_(context),
159 header_extensions_to_offer_(std::move(header_extensions_offered)),
160 on_negotiation_needed_(std::move(on_negotiation_needed)) {
161 RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
162 media_type_ == cricket::MEDIA_TYPE_VIDEO);
163 RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
164 if (sender->media_type() == cricket::MEDIA_TYPE_VIDEO)
165 sender->internal()->SetVideoCodecPreferences(
166 media_engine()->video().send_codecs(false));
167 senders_.push_back(sender);
168 receivers_.push_back(receiver);
169 }
170
~RtpTransceiver()171 RtpTransceiver::~RtpTransceiver() {
172 // TODO(tommi): On Android, when running PeerConnectionClientTest (e.g.
173 // PeerConnectionClientTest#testCameraSwitch), the instance doesn't get
174 // deleted on `thread_`. See if we can fix that.
175 if (!stopped_) {
176 RTC_DCHECK_RUN_ON(thread_);
177 StopInternal();
178 }
179
180 RTC_CHECK(!channel_) << "Missing call to ClearChannel?";
181 }
182
CreateChannel(absl::string_view mid,Call * call_ptr,const cricket::MediaConfig & media_config,bool srtp_required,CryptoOptions crypto_options,const cricket::AudioOptions & audio_options,const cricket::VideoOptions & video_options,VideoBitrateAllocatorFactory * video_bitrate_allocator_factory,std::function<RtpTransportInternal * (absl::string_view)> transport_lookup)183 RTCError RtpTransceiver::CreateChannel(
184 absl::string_view mid,
185 Call* call_ptr,
186 const cricket::MediaConfig& media_config,
187 bool srtp_required,
188 CryptoOptions crypto_options,
189 const cricket::AudioOptions& audio_options,
190 const cricket::VideoOptions& video_options,
191 VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
192 std::function<RtpTransportInternal*(absl::string_view)> transport_lookup) {
193 RTC_DCHECK_RUN_ON(thread_);
194 if (!media_engine()) {
195 // TODO(hta): Must be a better way
196 return RTCError(RTCErrorType::INTERNAL_ERROR,
197 "No media engine for mid=" + std::string(mid));
198 }
199 std::unique_ptr<cricket::ChannelInterface> new_channel;
200 if (media_type() == cricket::MEDIA_TYPE_AUDIO) {
201 // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to
202 // the worker thread. We shouldn't be using the `call_ptr_` hack here but
203 // simply be on the worker thread and use `call_` (update upstream code).
204 RTC_DCHECK(call_ptr);
205 RTC_DCHECK(media_engine());
206 // TODO(bugs.webrtc.org/11992): Remove this workaround after updates in
207 // PeerConnection and add the expectation that we're already on the right
208 // thread.
209 context()->worker_thread()->BlockingCall([&] {
210 RTC_DCHECK_RUN_ON(context()->worker_thread());
211
212 cricket::VoiceMediaChannel* media_channel =
213 media_engine()->voice().CreateMediaChannel(
214 call_ptr, media_config, audio_options, crypto_options);
215 if (!media_channel) {
216 return;
217 }
218
219 new_channel = std::make_unique<cricket::VoiceChannel>(
220 context()->worker_thread(), context()->network_thread(),
221 context()->signaling_thread(), absl::WrapUnique(media_channel), mid,
222 srtp_required, crypto_options, context()->ssrc_generator());
223 });
224 } else {
225 RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, media_type());
226
227 // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to
228 // the worker thread. We shouldn't be using the `call_ptr_` hack here but
229 // simply be on the worker thread and use `call_` (update upstream code).
230 context()->worker_thread()->BlockingCall([&] {
231 RTC_DCHECK_RUN_ON(context()->worker_thread());
232 cricket::VideoMediaChannel* media_channel =
233 media_engine()->video().CreateMediaChannel(
234 call_ptr, media_config, video_options, crypto_options,
235 video_bitrate_allocator_factory);
236 if (!media_channel) {
237 return;
238 }
239
240 new_channel = std::make_unique<cricket::VideoChannel>(
241 context()->worker_thread(), context()->network_thread(),
242 context()->signaling_thread(), absl::WrapUnique(media_channel), mid,
243 srtp_required, crypto_options, context()->ssrc_generator());
244 });
245 }
246 if (!new_channel) {
247 // TODO(hta): Must be a better way
248 return RTCError(RTCErrorType::INTERNAL_ERROR,
249 "Failed to create channel for mid=" + std::string(mid));
250 }
251 SetChannel(std::move(new_channel), transport_lookup);
252 return RTCError::OK();
253 }
254
SetChannel(std::unique_ptr<cricket::ChannelInterface> channel,std::function<RtpTransportInternal * (const std::string &)> transport_lookup)255 void RtpTransceiver::SetChannel(
256 std::unique_ptr<cricket::ChannelInterface> channel,
257 std::function<RtpTransportInternal*(const std::string&)> transport_lookup) {
258 RTC_DCHECK_RUN_ON(thread_);
259 RTC_DCHECK(channel);
260 RTC_DCHECK(transport_lookup);
261 RTC_DCHECK(!channel_);
262 // Cannot set a channel on a stopped transceiver.
263 if (stopped_) {
264 return;
265 }
266
267 RTC_LOG_THREAD_BLOCK_COUNT();
268
269 RTC_DCHECK_EQ(media_type(), channel->media_type());
270 signaling_thread_safety_ = PendingTaskSafetyFlag::Create();
271
272 std::unique_ptr<cricket::ChannelInterface> channel_to_delete;
273
274 // An alternative to this, could be to require SetChannel to be called
275 // on the network thread. The channel object operates for the most part
276 // on the network thread, as part of its initialization being on the network
277 // thread is required, so setting a channel object as part of the construction
278 // (without thread hopping) might be the more efficient thing to do than
279 // how SetChannel works today.
280 // Similarly, if the channel() accessor is limited to the network thread, that
281 // helps with keeping the channel implementation requirements being met and
282 // avoids synchronization for accessing the pointer or network related state.
283 context()->network_thread()->BlockingCall([&]() {
284 if (channel_) {
285 channel_->SetFirstPacketReceivedCallback(nullptr);
286 channel_->SetRtpTransport(nullptr);
287 channel_to_delete = std::move(channel_);
288 }
289
290 channel_ = std::move(channel);
291
292 channel_->SetRtpTransport(transport_lookup(channel_->mid()));
293 channel_->SetFirstPacketReceivedCallback(
294 [thread = thread_, flag = signaling_thread_safety_, this]() mutable {
295 thread->PostTask(
296 SafeTask(std::move(flag), [this]() { OnFirstPacketReceived(); }));
297 });
298 });
299 PushNewMediaChannelAndDeleteChannel(nullptr);
300
301 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2);
302 }
303
ClearChannel()304 void RtpTransceiver::ClearChannel() {
305 RTC_DCHECK_RUN_ON(thread_);
306
307 if (!channel_) {
308 return;
309 }
310
311 RTC_LOG_THREAD_BLOCK_COUNT();
312
313 if (channel_) {
314 signaling_thread_safety_->SetNotAlive();
315 signaling_thread_safety_ = nullptr;
316 }
317 std::unique_ptr<cricket::ChannelInterface> channel_to_delete;
318
319 context()->network_thread()->BlockingCall([&]() {
320 if (channel_) {
321 channel_->SetFirstPacketReceivedCallback(nullptr);
322 channel_->SetRtpTransport(nullptr);
323 channel_to_delete = std::move(channel_);
324 }
325 });
326
327 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1);
328 PushNewMediaChannelAndDeleteChannel(std::move(channel_to_delete));
329
330 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2);
331 }
332
PushNewMediaChannelAndDeleteChannel(std::unique_ptr<cricket::ChannelInterface> channel_to_delete)333 void RtpTransceiver::PushNewMediaChannelAndDeleteChannel(
334 std::unique_ptr<cricket::ChannelInterface> channel_to_delete) {
335 // The clumsy combination of pushing down media channel and deleting
336 // the channel is due to the desire to do both things in one Invoke().
337 if (!channel_to_delete && senders_.empty() && receivers_.empty()) {
338 return;
339 }
340 context()->worker_thread()->BlockingCall([&]() {
341 // Push down the new media_channel, if any, otherwise clear it.
342 auto* media_channel = channel_ ? channel_->media_channel() : nullptr;
343 for (const auto& sender : senders_) {
344 sender->internal()->SetMediaChannel(media_channel);
345 }
346
347 for (const auto& receiver : receivers_) {
348 receiver->internal()->SetMediaChannel(media_channel);
349 }
350
351 // Destroy the channel, if we had one, now _after_ updating the receivers
352 // who might have had references to the previous channel.
353 if (channel_to_delete) {
354 channel_to_delete.reset(nullptr);
355 }
356 });
357 }
358
AddSender(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender)359 void RtpTransceiver::AddSender(
360 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender) {
361 RTC_DCHECK_RUN_ON(thread_);
362 RTC_DCHECK(!stopped_);
363 RTC_DCHECK(!unified_plan_);
364 RTC_DCHECK(sender);
365 RTC_DCHECK_EQ(media_type(), sender->media_type());
366 RTC_DCHECK(!absl::c_linear_search(senders_, sender));
367 if (media_type() == cricket::MEDIA_TYPE_VIDEO) {
368 std::vector<cricket::VideoCodec> send_codecs =
369 media_engine()->video().send_codecs(false);
370 sender->internal()->SetVideoCodecPreferences(
371 codec_preferences_.empty()
372 ? send_codecs
373 : MatchCodecPreferences(codec_preferences_, send_codecs));
374 }
375 senders_.push_back(sender);
376 }
377
RemoveSender(RtpSenderInterface * sender)378 bool RtpTransceiver::RemoveSender(RtpSenderInterface* sender) {
379 RTC_DCHECK(!unified_plan_);
380 if (sender) {
381 RTC_DCHECK_EQ(media_type(), sender->media_type());
382 }
383 auto it = absl::c_find(senders_, sender);
384 if (it == senders_.end()) {
385 return false;
386 }
387 (*it)->internal()->Stop();
388 senders_.erase(it);
389 return true;
390 }
391
AddReceiver(rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver)392 void RtpTransceiver::AddReceiver(
393 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
394 receiver) {
395 RTC_DCHECK_RUN_ON(thread_);
396 RTC_DCHECK(!stopped_);
397 RTC_DCHECK(!unified_plan_);
398 RTC_DCHECK(receiver);
399 RTC_DCHECK_EQ(media_type(), receiver->media_type());
400 RTC_DCHECK(!absl::c_linear_search(receivers_, receiver));
401 receivers_.push_back(receiver);
402 }
403
RemoveReceiver(RtpReceiverInterface * receiver)404 bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) {
405 RTC_DCHECK_RUN_ON(thread_);
406 RTC_DCHECK(!unified_plan_);
407 if (receiver) {
408 RTC_DCHECK_EQ(media_type(), receiver->media_type());
409 }
410 auto it = absl::c_find(receivers_, receiver);
411 if (it == receivers_.end()) {
412 return false;
413 }
414
415 (*it)->internal()->Stop();
416 context()->worker_thread()->BlockingCall([&]() {
417 // `Stop()` will clear the receiver's pointer to the media channel.
418 (*it)->internal()->SetMediaChannel(nullptr);
419 });
420
421 receivers_.erase(it);
422 return true;
423 }
424
sender_internal() const425 rtc::scoped_refptr<RtpSenderInternal> RtpTransceiver::sender_internal() const {
426 RTC_DCHECK(unified_plan_);
427 RTC_CHECK_EQ(1u, senders_.size());
428 return rtc::scoped_refptr<RtpSenderInternal>(senders_[0]->internal());
429 }
430
receiver_internal() const431 rtc::scoped_refptr<RtpReceiverInternal> RtpTransceiver::receiver_internal()
432 const {
433 RTC_DCHECK(unified_plan_);
434 RTC_CHECK_EQ(1u, receivers_.size());
435 return rtc::scoped_refptr<RtpReceiverInternal>(receivers_[0]->internal());
436 }
437
media_type() const438 cricket::MediaType RtpTransceiver::media_type() const {
439 return media_type_;
440 }
441
mid() const442 absl::optional<std::string> RtpTransceiver::mid() const {
443 return mid_;
444 }
445
OnFirstPacketReceived()446 void RtpTransceiver::OnFirstPacketReceived() {
447 for (const auto& receiver : receivers_) {
448 receiver->internal()->NotifyFirstPacketReceived();
449 }
450 }
451
sender() const452 rtc::scoped_refptr<RtpSenderInterface> RtpTransceiver::sender() const {
453 RTC_DCHECK(unified_plan_);
454 RTC_CHECK_EQ(1u, senders_.size());
455 return senders_[0];
456 }
457
receiver() const458 rtc::scoped_refptr<RtpReceiverInterface> RtpTransceiver::receiver() const {
459 RTC_DCHECK(unified_plan_);
460 RTC_CHECK_EQ(1u, receivers_.size());
461 return receivers_[0];
462 }
463
set_current_direction(RtpTransceiverDirection direction)464 void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) {
465 RTC_LOG(LS_INFO) << "Changing transceiver (MID=" << mid_.value_or("<not set>")
466 << ") current direction from "
467 << (current_direction_ ? RtpTransceiverDirectionToString(
468 *current_direction_)
469 : "<not set>")
470 << " to " << RtpTransceiverDirectionToString(direction)
471 << ".";
472 current_direction_ = direction;
473 if (RtpTransceiverDirectionHasSend(*current_direction_)) {
474 has_ever_been_used_to_send_ = true;
475 }
476 }
477
set_fired_direction(absl::optional<RtpTransceiverDirection> direction)478 void RtpTransceiver::set_fired_direction(
479 absl::optional<RtpTransceiverDirection> direction) {
480 fired_direction_ = direction;
481 }
482
stopped() const483 bool RtpTransceiver::stopped() const {
484 RTC_DCHECK_RUN_ON(thread_);
485 return stopped_;
486 }
487
stopping() const488 bool RtpTransceiver::stopping() const {
489 RTC_DCHECK_RUN_ON(thread_);
490 return stopping_;
491 }
492
direction() const493 RtpTransceiverDirection RtpTransceiver::direction() const {
494 if (unified_plan_ && stopping())
495 return webrtc::RtpTransceiverDirection::kStopped;
496
497 return direction_;
498 }
499
SetDirectionWithError(RtpTransceiverDirection new_direction)500 RTCError RtpTransceiver::SetDirectionWithError(
501 RtpTransceiverDirection new_direction) {
502 if (unified_plan_ && stopping()) {
503 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
504 "Cannot set direction on a stopping transceiver.");
505 }
506 if (new_direction == direction_)
507 return RTCError::OK();
508
509 if (new_direction == RtpTransceiverDirection::kStopped) {
510 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
511 "The set direction 'stopped' is invalid.");
512 }
513
514 direction_ = new_direction;
515 on_negotiation_needed_();
516
517 return RTCError::OK();
518 }
519
current_direction() const520 absl::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
521 const {
522 if (unified_plan_ && stopped())
523 return webrtc::RtpTransceiverDirection::kStopped;
524
525 return current_direction_;
526 }
527
fired_direction() const528 absl::optional<RtpTransceiverDirection> RtpTransceiver::fired_direction()
529 const {
530 return fired_direction_;
531 }
532
StopSendingAndReceiving()533 void RtpTransceiver::StopSendingAndReceiving() {
534 // 1. Let sender be transceiver.[[Sender]].
535 // 2. Let receiver be transceiver.[[Receiver]].
536 //
537 // 3. Stop sending media with sender.
538 //
539 RTC_DCHECK_RUN_ON(thread_);
540
541 // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as
542 // specified in [RFC3550].
543 for (const auto& sender : senders_)
544 sender->internal()->Stop();
545
546 // Signal to receiver sources that we're stopping.
547 for (const auto& receiver : receivers_)
548 receiver->internal()->Stop();
549
550 context()->worker_thread()->BlockingCall([&]() {
551 // 5 Stop receiving media with receiver.
552 for (const auto& receiver : receivers_)
553 receiver->internal()->SetMediaChannel(nullptr);
554 });
555
556 stopping_ = true;
557 direction_ = webrtc::RtpTransceiverDirection::kInactive;
558 }
559
StopStandard()560 RTCError RtpTransceiver::StopStandard() {
561 RTC_DCHECK_RUN_ON(thread_);
562 // If we're on Plan B, do what Stop() used to do there.
563 if (!unified_plan_) {
564 StopInternal();
565 return RTCError::OK();
566 }
567 // 1. Let transceiver be the RTCRtpTransceiver object on which the method is
568 // invoked.
569 //
570 // 2. Let connection be the RTCPeerConnection object associated with
571 // transceiver.
572 //
573 // 3. If connection.[[IsClosed]] is true, throw an InvalidStateError.
574 if (is_pc_closed_) {
575 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
576 "PeerConnection is closed.");
577 }
578
579 // 4. If transceiver.[[Stopping]] is true, abort these steps.
580 if (stopping_)
581 return RTCError::OK();
582
583 // 5. Stop sending and receiving given transceiver, and update the
584 // negotiation-needed flag for connection.
585 StopSendingAndReceiving();
586 on_negotiation_needed_();
587
588 return RTCError::OK();
589 }
590
StopInternal()591 void RtpTransceiver::StopInternal() {
592 RTC_DCHECK_RUN_ON(thread_);
593 StopTransceiverProcedure();
594 }
595
StopTransceiverProcedure()596 void RtpTransceiver::StopTransceiverProcedure() {
597 RTC_DCHECK_RUN_ON(thread_);
598 // As specified in the "Stop the RTCRtpTransceiver" procedure
599 // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given
600 // transceiver.
601 if (!stopping_)
602 StopSendingAndReceiving();
603
604 // 2. Set transceiver.[[Stopped]] to true.
605 stopped_ = true;
606
607 // Signal the updated change to the senders.
608 for (const auto& sender : senders_)
609 sender->internal()->SetTransceiverAsStopped();
610
611 // 3. Set transceiver.[[Receptive]] to false.
612 // 4. Set transceiver.[[CurrentDirection]] to null.
613 current_direction_ = absl::nullopt;
614 }
615
SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codec_capabilities)616 RTCError RtpTransceiver::SetCodecPreferences(
617 rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
618 RTC_DCHECK(unified_plan_);
619 // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot
620 // to codecs and abort these steps.
621 if (codec_capabilities.empty()) {
622 codec_preferences_.clear();
623 if (media_type() == cricket::MEDIA_TYPE_VIDEO)
624 senders_.front()->internal()->SetVideoCodecPreferences(
625 media_engine()->video().send_codecs(false));
626 return RTCError::OK();
627 }
628
629 // 4. Remove any duplicate values in codecs.
630 std::vector<RtpCodecCapability> codecs;
631 absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
632 [&codecs](const RtpCodecCapability& codec) {
633 return absl::c_linear_search(codecs, codec);
634 });
635
636 // 6. to 8.
637 RTCError result;
638 if (media_type_ == cricket::MEDIA_TYPE_AUDIO) {
639 std::vector<cricket::AudioCodec> recv_codecs, send_codecs;
640 send_codecs = media_engine()->voice().send_codecs();
641 recv_codecs = media_engine()->voice().recv_codecs();
642 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
643 } else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
644 std::vector<cricket::VideoCodec> recv_codecs, send_codecs;
645 send_codecs = media_engine()->video().send_codecs(context()->use_rtx());
646 recv_codecs = media_engine()->video().recv_codecs(context()->use_rtx());
647 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
648
649 if (result.ok()) {
650 senders_.front()->internal()->SetVideoCodecPreferences(
651 MatchCodecPreferences(codecs, send_codecs));
652 }
653 }
654
655 if (result.ok()) {
656 codec_preferences_ = codecs;
657 }
658
659 return result;
660 }
661
662 std::vector<RtpHeaderExtensionCapability>
HeaderExtensionsToOffer() const663 RtpTransceiver::HeaderExtensionsToOffer() const {
664 return header_extensions_to_offer_;
665 }
666
667 std::vector<RtpHeaderExtensionCapability>
HeaderExtensionsNegotiated() const668 RtpTransceiver::HeaderExtensionsNegotiated() const {
669 RTC_DCHECK_RUN_ON(thread_);
670 std::vector<RtpHeaderExtensionCapability> result;
671 for (const auto& ext : negotiated_header_extensions_) {
672 result.emplace_back(ext.uri, ext.id, RtpTransceiverDirection::kSendRecv);
673 }
674 return result;
675 }
676
SetOfferedRtpHeaderExtensions(rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions_to_offer)677 RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
678 rtc::ArrayView<const RtpHeaderExtensionCapability>
679 header_extensions_to_offer) {
680 for (const auto& entry : header_extensions_to_offer) {
681 // Handle unsupported requests for mandatory extensions as per
682 // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.
683 // Note:
684 // - We do not handle setOfferedRtpHeaderExtensions algorithm step 2.1,
685 // this has to be checked on a higher level. We naturally error out
686 // in the handling of Step 2.2 if an unset URI is encountered.
687
688 // Step 2.2.
689 // Handle unknown extensions.
690 auto it = std::find_if(
691 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
692 [&entry](const auto& offered) { return entry.uri == offered.uri; });
693 if (it == header_extensions_to_offer_.end()) {
694 return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
695 "Attempted to modify an unoffered extension.");
696 }
697
698 // Step 2.4-2.5.
699 // - Use of the transceiver interface indicates unified plan is in effect,
700 // hence the MID extension needs to be enabled.
701 // - Also handle the mandatory video orientation extensions.
702 if ((entry.uri == RtpExtension::kMidUri ||
703 entry.uri == RtpExtension::kVideoRotationUri) &&
704 entry.direction != RtpTransceiverDirection::kSendRecv) {
705 return RTCError(RTCErrorType::INVALID_MODIFICATION,
706 "Attempted to stop a mandatory extension.");
707 }
708 }
709
710 // Apply mutation after error checking.
711 for (const auto& entry : header_extensions_to_offer) {
712 auto it = std::find_if(
713 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
714 [&entry](const auto& offered) { return entry.uri == offered.uri; });
715 it->direction = entry.direction;
716 }
717
718 return RTCError::OK();
719 }
720
OnNegotiationUpdate(SdpType sdp_type,const cricket::MediaContentDescription * content)721 void RtpTransceiver::OnNegotiationUpdate(
722 SdpType sdp_type,
723 const cricket::MediaContentDescription* content) {
724 RTC_DCHECK_RUN_ON(thread_);
725 RTC_DCHECK(content);
726 if (sdp_type == SdpType::kAnswer)
727 negotiated_header_extensions_ = content->rtp_header_extensions();
728 }
729
SetPeerConnectionClosed()730 void RtpTransceiver::SetPeerConnectionClosed() {
731 is_pc_closed_ = true;
732 }
733
734 } // namespace webrtc
735