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 <iterator>
14 #include <string>
15 #include <utility>
16 #include <vector>
17
18 #include "absl/algorithm/container.h"
19 #include "api/rtp_parameters.h"
20 #include "api/sequence_checker.h"
21 #include "media/base/codec.h"
22 #include "media/base/media_constants.h"
23 #include "pc/channel_manager.h"
24 #include "pc/rtp_media_utils.h"
25 #include "pc/session_description.h"
26 #include "rtc_base/checks.h"
27 #include "rtc_base/logging.h"
28 #include "rtc_base/thread.h"
29
30 namespace webrtc {
31 namespace {
32 template <class T>
VerifyCodecPreferences(const std::vector<RtpCodecCapability> & codecs,const std::vector<T> & send_codecs,const std::vector<T> & recv_codecs)33 RTCError VerifyCodecPreferences(const std::vector<RtpCodecCapability>& codecs,
34 const std::vector<T>& send_codecs,
35 const std::vector<T>& recv_codecs) {
36 // If the intersection between codecs and
37 // RTCRtpSender.getCapabilities(kind).codecs or the intersection between
38 // codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX,
39 // RED or FEC codecs or is an empty set, throw InvalidModificationError.
40 // This ensures that we always have something to offer, regardless of
41 // transceiver.direction.
42
43 if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) {
44 return codec.name != cricket::kRtxCodecName &&
45 codec.name != cricket::kRedCodecName &&
46 codec.name != cricket::kFlexfecCodecName &&
47 absl::c_any_of(recv_codecs, [&codec](const T& recv_codec) {
48 return recv_codec.MatchesCapability(codec);
49 });
50 })) {
51 return RTCError(RTCErrorType::INVALID_MODIFICATION,
52 "Invalid codec preferences: Missing codec from recv "
53 "codec capabilities.");
54 }
55
56 if (!absl::c_any_of(codecs, [&send_codecs](const RtpCodecCapability& codec) {
57 return codec.name != cricket::kRtxCodecName &&
58 codec.name != cricket::kRedCodecName &&
59 codec.name != cricket::kFlexfecCodecName &&
60 absl::c_any_of(send_codecs, [&codec](const T& send_codec) {
61 return send_codec.MatchesCapability(codec);
62 });
63 })) {
64 return RTCError(RTCErrorType::INVALID_MODIFICATION,
65 "Invalid codec preferences: Missing codec from send "
66 "codec capabilities.");
67 }
68
69 // Let codecCapabilities be the union of
70 // RTCRtpSender.getCapabilities(kind).codecs and
71 // RTCRtpReceiver.getCapabilities(kind).codecs. For each codec in codecs, If
72 // codec is not in codecCapabilities, throw InvalidModificationError.
73 for (const auto& codec_preference : codecs) {
74 bool is_recv_codec =
75 absl::c_any_of(recv_codecs, [&codec_preference](const T& codec) {
76 return codec.MatchesCapability(codec_preference);
77 });
78
79 bool is_send_codec =
80 absl::c_any_of(send_codecs, [&codec_preference](const T& codec) {
81 return codec.MatchesCapability(codec_preference);
82 });
83
84 if (!is_recv_codec && !is_send_codec) {
85 return RTCError(
86 RTCErrorType::INVALID_MODIFICATION,
87 std::string("Invalid codec preferences: invalid codec with name \"") +
88 codec_preference.name + "\".");
89 }
90 }
91
92 // Check we have a real codec (not just rtx, red or fec)
93 if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) {
94 return codec.name == cricket::kRtxCodecName ||
95 codec.name == cricket::kRedCodecName ||
96 codec.name == cricket::kUlpfecCodecName;
97 })) {
98 return RTCError(RTCErrorType::INVALID_MODIFICATION,
99 "Invalid codec preferences: codec list must have a non "
100 "RTX, RED or FEC entry.");
101 }
102
103 return RTCError::OK();
104 }
105
GetCurrentTaskQueueOrThread()106 TaskQueueBase* GetCurrentTaskQueueOrThread() {
107 TaskQueueBase* current = TaskQueueBase::Current();
108 if (!current)
109 current = rtc::ThreadManager::Instance()->CurrentThread();
110 return current;
111 }
112
113 } // namespace
114
RtpTransceiver(cricket::MediaType media_type)115 RtpTransceiver::RtpTransceiver(cricket::MediaType media_type)
116 : thread_(GetCurrentTaskQueueOrThread()),
117 unified_plan_(false),
118 media_type_(media_type) {
119 RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
120 media_type == cricket::MEDIA_TYPE_VIDEO);
121 }
122
RtpTransceiver(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver,cricket::ChannelManager * channel_manager,std::vector<RtpHeaderExtensionCapability> header_extensions_offered,std::function<void ()> on_negotiation_needed)123 RtpTransceiver::RtpTransceiver(
124 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
125 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
126 receiver,
127 cricket::ChannelManager* channel_manager,
128 std::vector<RtpHeaderExtensionCapability> header_extensions_offered,
129 std::function<void()> on_negotiation_needed)
130 : thread_(GetCurrentTaskQueueOrThread()),
131 unified_plan_(true),
132 media_type_(sender->media_type()),
133 channel_manager_(channel_manager),
134 header_extensions_to_offer_(std::move(header_extensions_offered)),
135 on_negotiation_needed_(std::move(on_negotiation_needed)) {
136 RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
137 media_type_ == cricket::MEDIA_TYPE_VIDEO);
138 RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
139 senders_.push_back(sender);
140 receivers_.push_back(receiver);
141 }
142
~RtpTransceiver()143 RtpTransceiver::~RtpTransceiver() {
144 StopInternal();
145 }
146
SetChannel(cricket::ChannelInterface * channel)147 void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) {
148 // Cannot set a non-null channel on a stopped transceiver.
149 if (stopped_ && channel) {
150 return;
151 }
152
153 RTC_LOG_THREAD_BLOCK_COUNT();
154
155 if (channel) {
156 RTC_DCHECK_EQ(media_type(), channel->media_type());
157 }
158
159 if (channel_) {
160 channel_->SignalFirstPacketReceived().disconnect(this);
161 }
162
163 channel_ = channel;
164
165 if (channel_) {
166 channel_->SignalFirstPacketReceived().connect(
167 this, &RtpTransceiver::OnFirstPacketReceived);
168 }
169
170 for (const auto& sender : senders_) {
171 sender->internal()->SetMediaChannel(channel_ ? channel_->media_channel()
172 : nullptr);
173 }
174
175 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(0);
176
177 for (const auto& receiver : receivers_) {
178 if (!channel_) {
179 // TODO(tommi): This can internally block and hop to the worker thread.
180 // It's likely that SetMediaChannel also does that, so perhaps we should
181 // require SetMediaChannel(nullptr) to also Stop() and skip this call.
182 receiver->internal()->Stop();
183 }
184
185 receiver->internal()->SetMediaChannel(channel_ ? channel_->media_channel()
186 : nullptr);
187 }
188
189 RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(receivers_.size() * 2);
190 }
191
AddSender(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender)192 void RtpTransceiver::AddSender(
193 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender) {
194 RTC_DCHECK(!stopped_);
195 RTC_DCHECK(!unified_plan_);
196 RTC_DCHECK(sender);
197 RTC_DCHECK_EQ(media_type(), sender->media_type());
198 RTC_DCHECK(!absl::c_linear_search(senders_, sender));
199 senders_.push_back(sender);
200 }
201
RemoveSender(RtpSenderInterface * sender)202 bool RtpTransceiver::RemoveSender(RtpSenderInterface* sender) {
203 RTC_DCHECK(!unified_plan_);
204 if (sender) {
205 RTC_DCHECK_EQ(media_type(), sender->media_type());
206 }
207 auto it = absl::c_find(senders_, sender);
208 if (it == senders_.end()) {
209 return false;
210 }
211 (*it)->internal()->Stop();
212 senders_.erase(it);
213 return true;
214 }
215
AddReceiver(rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver)216 void RtpTransceiver::AddReceiver(
217 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
218 receiver) {
219 RTC_DCHECK(!stopped_);
220 RTC_DCHECK(!unified_plan_);
221 RTC_DCHECK(receiver);
222 RTC_DCHECK_EQ(media_type(), receiver->media_type());
223 RTC_DCHECK(!absl::c_linear_search(receivers_, receiver));
224 receivers_.push_back(receiver);
225 }
226
RemoveReceiver(RtpReceiverInterface * receiver)227 bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) {
228 RTC_DCHECK(!unified_plan_);
229 if (receiver) {
230 RTC_DCHECK_EQ(media_type(), receiver->media_type());
231 }
232 auto it = absl::c_find(receivers_, receiver);
233 if (it == receivers_.end()) {
234 return false;
235 }
236 (*it)->internal()->Stop();
237 // After the receiver has been removed, there's no guarantee that the
238 // contained media channel isn't deleted shortly after this. To make sure that
239 // the receiver doesn't spontaneously try to use it's (potentially stale)
240 // media channel reference, we clear it out.
241 (*it)->internal()->SetMediaChannel(nullptr);
242 receivers_.erase(it);
243 return true;
244 }
245
sender_internal() const246 rtc::scoped_refptr<RtpSenderInternal> RtpTransceiver::sender_internal() const {
247 RTC_DCHECK(unified_plan_);
248 RTC_CHECK_EQ(1u, senders_.size());
249 return senders_[0]->internal();
250 }
251
receiver_internal() const252 rtc::scoped_refptr<RtpReceiverInternal> RtpTransceiver::receiver_internal()
253 const {
254 RTC_DCHECK(unified_plan_);
255 RTC_CHECK_EQ(1u, receivers_.size());
256 return receivers_[0]->internal();
257 }
258
media_type() const259 cricket::MediaType RtpTransceiver::media_type() const {
260 return media_type_;
261 }
262
mid() const263 absl::optional<std::string> RtpTransceiver::mid() const {
264 return mid_;
265 }
266
OnFirstPacketReceived(cricket::ChannelInterface *)267 void RtpTransceiver::OnFirstPacketReceived(cricket::ChannelInterface*) {
268 for (const auto& receiver : receivers_) {
269 receiver->internal()->NotifyFirstPacketReceived();
270 }
271 }
272
sender() const273 rtc::scoped_refptr<RtpSenderInterface> RtpTransceiver::sender() const {
274 RTC_DCHECK(unified_plan_);
275 RTC_CHECK_EQ(1u, senders_.size());
276 return senders_[0];
277 }
278
receiver() const279 rtc::scoped_refptr<RtpReceiverInterface> RtpTransceiver::receiver() const {
280 RTC_DCHECK(unified_plan_);
281 RTC_CHECK_EQ(1u, receivers_.size());
282 return receivers_[0];
283 }
284
set_current_direction(RtpTransceiverDirection direction)285 void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) {
286 RTC_LOG(LS_INFO) << "Changing transceiver (MID=" << mid_.value_or("<not set>")
287 << ") current direction from "
288 << (current_direction_ ? RtpTransceiverDirectionToString(
289 *current_direction_)
290 : "<not set>")
291 << " to " << RtpTransceiverDirectionToString(direction)
292 << ".";
293 current_direction_ = direction;
294 if (RtpTransceiverDirectionHasSend(*current_direction_)) {
295 has_ever_been_used_to_send_ = true;
296 }
297 }
298
set_fired_direction(RtpTransceiverDirection direction)299 void RtpTransceiver::set_fired_direction(RtpTransceiverDirection direction) {
300 fired_direction_ = direction;
301 }
302
stopped() const303 bool RtpTransceiver::stopped() const {
304 return stopped_;
305 }
306
stopping() const307 bool RtpTransceiver::stopping() const {
308 RTC_DCHECK_RUN_ON(thread_);
309 return stopping_;
310 }
311
direction() const312 RtpTransceiverDirection RtpTransceiver::direction() const {
313 if (unified_plan_ && stopping())
314 return webrtc::RtpTransceiverDirection::kStopped;
315
316 return direction_;
317 }
318
SetDirectionWithError(RtpTransceiverDirection new_direction)319 RTCError RtpTransceiver::SetDirectionWithError(
320 RtpTransceiverDirection new_direction) {
321 if (unified_plan_ && stopping()) {
322 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
323 "Cannot set direction on a stopping transceiver.");
324 }
325 if (new_direction == direction_)
326 return RTCError::OK();
327
328 if (new_direction == RtpTransceiverDirection::kStopped) {
329 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
330 "The set direction 'stopped' is invalid.");
331 }
332
333 direction_ = new_direction;
334 on_negotiation_needed_();
335
336 return RTCError::OK();
337 }
338
current_direction() const339 absl::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
340 const {
341 if (unified_plan_ && stopped())
342 return webrtc::RtpTransceiverDirection::kStopped;
343
344 return current_direction_;
345 }
346
fired_direction() const347 absl::optional<RtpTransceiverDirection> RtpTransceiver::fired_direction()
348 const {
349 return fired_direction_;
350 }
351
StopSendingAndReceiving()352 void RtpTransceiver::StopSendingAndReceiving() {
353 // 1. Let sender be transceiver.[[Sender]].
354 // 2. Let receiver be transceiver.[[Receiver]].
355 //
356 // 3. Stop sending media with sender.
357 //
358 // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as
359 // specified in [RFC3550].
360 RTC_DCHECK_RUN_ON(thread_);
361 for (const auto& sender : senders_)
362 sender->internal()->Stop();
363
364 // 5. Stop receiving media with receiver.
365 for (const auto& receiver : receivers_)
366 receiver->internal()->StopAndEndTrack();
367
368 stopping_ = true;
369 direction_ = webrtc::RtpTransceiverDirection::kInactive;
370 }
371
StopStandard()372 RTCError RtpTransceiver::StopStandard() {
373 RTC_DCHECK_RUN_ON(thread_);
374 // If we're on Plan B, do what Stop() used to do there.
375 if (!unified_plan_) {
376 StopInternal();
377 return RTCError::OK();
378 }
379 // 1. Let transceiver be the RTCRtpTransceiver object on which the method is
380 // invoked.
381 //
382 // 2. Let connection be the RTCPeerConnection object associated with
383 // transceiver.
384 //
385 // 3. If connection.[[IsClosed]] is true, throw an InvalidStateError.
386 if (is_pc_closed_) {
387 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
388 "PeerConnection is closed.");
389 }
390
391 // 4. If transceiver.[[Stopping]] is true, abort these steps.
392 if (stopping_)
393 return RTCError::OK();
394
395 // 5. Stop sending and receiving given transceiver, and update the
396 // negotiation-needed flag for connection.
397 StopSendingAndReceiving();
398 on_negotiation_needed_();
399
400 return RTCError::OK();
401 }
402
StopInternal()403 void RtpTransceiver::StopInternal() {
404 StopTransceiverProcedure();
405 }
406
StopTransceiverProcedure()407 void RtpTransceiver::StopTransceiverProcedure() {
408 RTC_DCHECK_RUN_ON(thread_);
409 // As specified in the "Stop the RTCRtpTransceiver" procedure
410 // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given
411 // transceiver.
412 if (!stopping_)
413 StopSendingAndReceiving();
414
415 // 2. Set transceiver.[[Stopped]] to true.
416 stopped_ = true;
417
418 // Signal the updated change to the senders.
419 for (const auto& sender : senders_)
420 sender->internal()->SetTransceiverAsStopped();
421
422 // 3. Set transceiver.[[Receptive]] to false.
423 // 4. Set transceiver.[[CurrentDirection]] to null.
424 current_direction_ = absl::nullopt;
425 }
426
SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codec_capabilities)427 RTCError RtpTransceiver::SetCodecPreferences(
428 rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
429 RTC_DCHECK(unified_plan_);
430
431 // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot
432 // to codecs and abort these steps.
433 if (codec_capabilities.empty()) {
434 codec_preferences_.clear();
435 return RTCError::OK();
436 }
437
438 // 4. Remove any duplicate values in codecs.
439 std::vector<RtpCodecCapability> codecs;
440 absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
441 [&codecs](const RtpCodecCapability& codec) {
442 return absl::c_linear_search(codecs, codec);
443 });
444
445 // 6. to 8.
446 RTCError result;
447 if (media_type_ == cricket::MEDIA_TYPE_AUDIO) {
448 std::vector<cricket::AudioCodec> recv_codecs, send_codecs;
449 channel_manager_->GetSupportedAudioReceiveCodecs(&recv_codecs);
450 channel_manager_->GetSupportedAudioSendCodecs(&send_codecs);
451
452 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
453 } else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
454 std::vector<cricket::VideoCodec> recv_codecs, send_codecs;
455 channel_manager_->GetSupportedVideoReceiveCodecs(&recv_codecs);
456 channel_manager_->GetSupportedVideoSendCodecs(&send_codecs);
457
458 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
459 }
460
461 if (result.ok()) {
462 codec_preferences_ = codecs;
463 }
464
465 return result;
466 }
467
468 std::vector<RtpHeaderExtensionCapability>
HeaderExtensionsToOffer() const469 RtpTransceiver::HeaderExtensionsToOffer() const {
470 return header_extensions_to_offer_;
471 }
472
473 std::vector<RtpHeaderExtensionCapability>
HeaderExtensionsNegotiated() const474 RtpTransceiver::HeaderExtensionsNegotiated() const {
475 if (!channel_)
476 return {};
477 std::vector<RtpHeaderExtensionCapability> result;
478 for (const auto& ext : channel_->GetNegotiatedRtpHeaderExtensions()) {
479 result.emplace_back(ext.uri, ext.id, RtpTransceiverDirection::kSendRecv);
480 }
481 return result;
482 }
483
SetOfferedRtpHeaderExtensions(rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions_to_offer)484 RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
485 rtc::ArrayView<const RtpHeaderExtensionCapability>
486 header_extensions_to_offer) {
487 for (const auto& entry : header_extensions_to_offer) {
488 // Handle unsupported requests for mandatory extensions as per
489 // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.
490 // Note:
491 // - We do not handle setOfferedRtpHeaderExtensions algorithm step 2.1,
492 // this has to be checked on a higher level. We naturally error out
493 // in the handling of Step 2.2 if an unset URI is encountered.
494
495 // Step 2.2.
496 // Handle unknown extensions.
497 auto it = std::find_if(
498 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
499 [&entry](const auto& offered) { return entry.uri == offered.uri; });
500 if (it == header_extensions_to_offer_.end()) {
501 return RTCError(RTCErrorType::UNSUPPORTED_PARAMETER,
502 "Attempted to modify an unoffered extension.");
503 }
504
505 // Step 2.4-2.5.
506 // - Use of the transceiver interface indicates unified plan is in effect,
507 // hence the MID extension needs to be enabled.
508 // - Also handle the mandatory video orientation extensions.
509 if ((entry.uri == RtpExtension::kMidUri ||
510 entry.uri == RtpExtension::kVideoRotationUri) &&
511 entry.direction != RtpTransceiverDirection::kSendRecv) {
512 return RTCError(RTCErrorType::INVALID_MODIFICATION,
513 "Attempted to stop a mandatory extension.");
514 }
515 }
516
517 // Apply mutation after error checking.
518 for (const auto& entry : header_extensions_to_offer) {
519 auto it = std::find_if(
520 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
521 [&entry](const auto& offered) { return entry.uri == offered.uri; });
522 it->direction = entry.direction;
523 }
524
525 return RTCError::OK();
526 }
527
SetPeerConnectionClosed()528 void RtpTransceiver::SetPeerConnectionClosed() {
529 is_pc_closed_ = true;
530 }
531
532 } // namespace webrtc
533