xref: /aosp_15_r20/external/webrtc/pc/rtp_transceiver.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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