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 <string>
14 #include <utility>
15
16 #include "absl/algorithm/container.h"
17 #include "api/rtp_parameters.h"
18 #include "pc/channel_manager.h"
19 #include "pc/rtp_media_utils.h"
20 #include "pc/rtp_parameters_conversion.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/logging.h"
23
24 namespace webrtc {
25 namespace {
26 template <class T>
VerifyCodecPreferences(const std::vector<RtpCodecCapability> & codecs,const std::vector<T> & send_codecs,const std::vector<T> & recv_codecs)27 RTCError VerifyCodecPreferences(const std::vector<RtpCodecCapability>& codecs,
28 const std::vector<T>& send_codecs,
29 const std::vector<T>& recv_codecs) {
30 // If the intersection between codecs and
31 // RTCRtpSender.getCapabilities(kind).codecs or the intersection between
32 // codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX,
33 // RED or FEC codecs or is an empty set, throw InvalidModificationError.
34 // This ensures that we always have something to offer, regardless of
35 // transceiver.direction.
36
37 if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) {
38 return codec.name != cricket::kRtxCodecName &&
39 codec.name != cricket::kRedCodecName &&
40 codec.name != cricket::kFlexfecCodecName &&
41 absl::c_any_of(recv_codecs, [&codec](const T& recv_codec) {
42 return recv_codec.MatchesCapability(codec);
43 });
44 })) {
45 return RTCError(RTCErrorType::INVALID_MODIFICATION,
46 "Invalid codec preferences: Missing codec from recv "
47 "codec capabilities.");
48 }
49
50 if (!absl::c_any_of(codecs, [&send_codecs](const RtpCodecCapability& codec) {
51 return codec.name != cricket::kRtxCodecName &&
52 codec.name != cricket::kRedCodecName &&
53 codec.name != cricket::kFlexfecCodecName &&
54 absl::c_any_of(send_codecs, [&codec](const T& send_codec) {
55 return send_codec.MatchesCapability(codec);
56 });
57 })) {
58 return RTCError(RTCErrorType::INVALID_MODIFICATION,
59 "Invalid codec preferences: Missing codec from send "
60 "codec capabilities.");
61 }
62
63 // Let codecCapabilities be the union of
64 // RTCRtpSender.getCapabilities(kind).codecs and
65 // RTCRtpReceiver.getCapabilities(kind).codecs. For each codec in codecs, If
66 // codec is not in codecCapabilities, throw InvalidModificationError.
67 for (const auto& codec_preference : codecs) {
68 bool is_recv_codec =
69 absl::c_any_of(recv_codecs, [&codec_preference](const T& codec) {
70 return codec.MatchesCapability(codec_preference);
71 });
72
73 bool is_send_codec =
74 absl::c_any_of(send_codecs, [&codec_preference](const T& codec) {
75 return codec.MatchesCapability(codec_preference);
76 });
77
78 if (!is_recv_codec && !is_send_codec) {
79 return RTCError(
80 RTCErrorType::INVALID_MODIFICATION,
81 std::string("Invalid codec preferences: invalid codec with name \"") +
82 codec_preference.name + "\".");
83 }
84 }
85
86 // Check we have a real codec (not just rtx, red or fec)
87 if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) {
88 return codec.name == cricket::kRtxCodecName ||
89 codec.name == cricket::kRedCodecName ||
90 codec.name == cricket::kUlpfecCodecName;
91 })) {
92 return RTCError(RTCErrorType::INVALID_MODIFICATION,
93 "Invalid codec preferences: codec list must have a non "
94 "RTX, RED or FEC entry.");
95 }
96
97 return RTCError::OK();
98 }
99
100 } // namespace
101
RtpTransceiver(cricket::MediaType media_type)102 RtpTransceiver::RtpTransceiver(cricket::MediaType media_type)
103 : unified_plan_(false), media_type_(media_type) {
104 RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
105 media_type == cricket::MEDIA_TYPE_VIDEO);
106 }
107
RtpTransceiver(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver,cricket::ChannelManager * channel_manager,std::vector<RtpHeaderExtensionCapability> header_extensions_offered)108 RtpTransceiver::RtpTransceiver(
109 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
110 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
111 receiver,
112 cricket::ChannelManager* channel_manager,
113 std::vector<RtpHeaderExtensionCapability> header_extensions_offered)
114 : unified_plan_(true),
115 media_type_(sender->media_type()),
116 channel_manager_(channel_manager),
117 header_extensions_to_offer_(std::move(header_extensions_offered)) {
118 RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
119 media_type_ == cricket::MEDIA_TYPE_VIDEO);
120 RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
121 senders_.push_back(sender);
122 receivers_.push_back(receiver);
123 }
124
~RtpTransceiver()125 RtpTransceiver::~RtpTransceiver() {
126 Stop();
127 }
128
SetChannel(cricket::ChannelInterface * channel)129 void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) {
130 // Cannot set a non-null channel on a stopped transceiver.
131 if (stopped_ && channel) {
132 return;
133 }
134
135 if (channel) {
136 RTC_DCHECK_EQ(media_type(), channel->media_type());
137 }
138
139 if (channel_) {
140 channel_->SignalFirstPacketReceived().disconnect(this);
141 }
142
143 channel_ = channel;
144
145 if (channel_) {
146 channel_->SignalFirstPacketReceived().connect(
147 this, &RtpTransceiver::OnFirstPacketReceived);
148 }
149
150 for (const auto& sender : senders_) {
151 sender->internal()->SetMediaChannel(channel_ ? channel_->media_channel()
152 : nullptr);
153 }
154
155 for (const auto& receiver : receivers_) {
156 if (!channel_) {
157 receiver->internal()->Stop();
158 }
159
160 receiver->internal()->SetMediaChannel(channel_ ? channel_->media_channel()
161 : nullptr);
162 }
163 }
164
AddSender(rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender)165 void RtpTransceiver::AddSender(
166 rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender) {
167 RTC_DCHECK(!stopped_);
168 RTC_DCHECK(!unified_plan_);
169 RTC_DCHECK(sender);
170 RTC_DCHECK_EQ(media_type(), sender->media_type());
171 RTC_DCHECK(!absl::c_linear_search(senders_, sender));
172 senders_.push_back(sender);
173 }
174
RemoveSender(RtpSenderInterface * sender)175 bool RtpTransceiver::RemoveSender(RtpSenderInterface* sender) {
176 RTC_DCHECK(!unified_plan_);
177 if (sender) {
178 RTC_DCHECK_EQ(media_type(), sender->media_type());
179 }
180 auto it = absl::c_find(senders_, sender);
181 if (it == senders_.end()) {
182 return false;
183 }
184 (*it)->internal()->Stop();
185 senders_.erase(it);
186 return true;
187 }
188
AddReceiver(rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>> receiver)189 void RtpTransceiver::AddReceiver(
190 rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
191 receiver) {
192 RTC_DCHECK(!stopped_);
193 RTC_DCHECK(!unified_plan_);
194 RTC_DCHECK(receiver);
195 RTC_DCHECK_EQ(media_type(), receiver->media_type());
196 RTC_DCHECK(!absl::c_linear_search(receivers_, receiver));
197 receivers_.push_back(receiver);
198 }
199
RemoveReceiver(RtpReceiverInterface * receiver)200 bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) {
201 RTC_DCHECK(!unified_plan_);
202 if (receiver) {
203 RTC_DCHECK_EQ(media_type(), receiver->media_type());
204 }
205 auto it = absl::c_find(receivers_, receiver);
206 if (it == receivers_.end()) {
207 return false;
208 }
209 (*it)->internal()->Stop();
210 // After the receiver has been removed, there's no guarantee that the
211 // contained media channel isn't deleted shortly after this. To make sure that
212 // the receiver doesn't spontaneously try to use it's (potentially stale)
213 // media channel reference, we clear it out.
214 (*it)->internal()->SetMediaChannel(nullptr);
215 receivers_.erase(it);
216 return true;
217 }
218
sender_internal() const219 rtc::scoped_refptr<RtpSenderInternal> RtpTransceiver::sender_internal() const {
220 RTC_DCHECK(unified_plan_);
221 RTC_CHECK_EQ(1u, senders_.size());
222 return senders_[0]->internal();
223 }
224
receiver_internal() const225 rtc::scoped_refptr<RtpReceiverInternal> RtpTransceiver::receiver_internal()
226 const {
227 RTC_DCHECK(unified_plan_);
228 RTC_CHECK_EQ(1u, receivers_.size());
229 return receivers_[0]->internal();
230 }
231
media_type() const232 cricket::MediaType RtpTransceiver::media_type() const {
233 return media_type_;
234 }
235
mid() const236 absl::optional<std::string> RtpTransceiver::mid() const {
237 return mid_;
238 }
239
OnFirstPacketReceived(cricket::ChannelInterface *)240 void RtpTransceiver::OnFirstPacketReceived(cricket::ChannelInterface*) {
241 for (const auto& receiver : receivers_) {
242 receiver->internal()->NotifyFirstPacketReceived();
243 }
244 }
245
sender() const246 rtc::scoped_refptr<RtpSenderInterface> RtpTransceiver::sender() const {
247 RTC_DCHECK(unified_plan_);
248 RTC_CHECK_EQ(1u, senders_.size());
249 return senders_[0];
250 }
251
receiver() const252 rtc::scoped_refptr<RtpReceiverInterface> RtpTransceiver::receiver() const {
253 RTC_DCHECK(unified_plan_);
254 RTC_CHECK_EQ(1u, receivers_.size());
255 return receivers_[0];
256 }
257
set_current_direction(RtpTransceiverDirection direction)258 void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) {
259 RTC_LOG(LS_INFO) << "Changing transceiver (MID=" << mid_.value_or("<not set>")
260 << ") current direction from "
261 << (current_direction_ ? RtpTransceiverDirectionToString(
262 *current_direction_)
263 : "<not set>")
264 << " to " << RtpTransceiverDirectionToString(direction)
265 << ".";
266 current_direction_ = direction;
267 if (RtpTransceiverDirectionHasSend(*current_direction_)) {
268 has_ever_been_used_to_send_ = true;
269 }
270 }
271
set_fired_direction(RtpTransceiverDirection direction)272 void RtpTransceiver::set_fired_direction(RtpTransceiverDirection direction) {
273 fired_direction_ = direction;
274 }
275
stopped() const276 bool RtpTransceiver::stopped() const {
277 return stopped_;
278 }
279
direction() const280 RtpTransceiverDirection RtpTransceiver::direction() const {
281 return direction_;
282 }
283
SetDirection(RtpTransceiverDirection new_direction)284 void RtpTransceiver::SetDirection(RtpTransceiverDirection new_direction) {
285 if (stopped()) {
286 return;
287 }
288 if (new_direction == direction_) {
289 return;
290 }
291 direction_ = new_direction;
292 SignalNegotiationNeeded();
293 }
294
current_direction() const295 absl::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
296 const {
297 return current_direction_;
298 }
299
fired_direction() const300 absl::optional<RtpTransceiverDirection> RtpTransceiver::fired_direction()
301 const {
302 return fired_direction_;
303 }
304
Stop()305 void RtpTransceiver::Stop() {
306 for (const auto& sender : senders_) {
307 sender->internal()->Stop();
308 }
309 for (const auto& receiver : receivers_) {
310 receiver->internal()->Stop();
311 }
312 stopped_ = true;
313 current_direction_ = absl::nullopt;
314 }
315
SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codec_capabilities)316 RTCError RtpTransceiver::SetCodecPreferences(
317 rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
318 RTC_DCHECK(unified_plan_);
319
320 // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot
321 // to codecs and abort these steps.
322 if (codec_capabilities.empty()) {
323 codec_preferences_.clear();
324 return RTCError::OK();
325 }
326
327 // 4. Remove any duplicate values in codecs.
328 std::vector<RtpCodecCapability> codecs;
329 absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
330 [&codecs](const RtpCodecCapability& codec) {
331 return absl::c_linear_search(codecs, codec);
332 });
333
334 // 6. to 8.
335 RTCError result;
336 if (media_type_ == cricket::MEDIA_TYPE_AUDIO) {
337 std::vector<cricket::AudioCodec> recv_codecs, send_codecs;
338 channel_manager_->GetSupportedAudioReceiveCodecs(&recv_codecs);
339 channel_manager_->GetSupportedAudioSendCodecs(&send_codecs);
340
341 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
342 } else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
343 std::vector<cricket::VideoCodec> recv_codecs, send_codecs;
344 channel_manager_->GetSupportedVideoReceiveCodecs(&recv_codecs);
345 channel_manager_->GetSupportedVideoSendCodecs(&send_codecs);
346
347 result = VerifyCodecPreferences(codecs, send_codecs, recv_codecs);
348 }
349
350 if (result.ok()) {
351 codec_preferences_ = codecs;
352 }
353
354 return result;
355 }
356
357 std::vector<RtpHeaderExtensionCapability>
HeaderExtensionsToOffer() const358 RtpTransceiver::HeaderExtensionsToOffer() const {
359 return header_extensions_to_offer_;
360 }
361
SetOfferedRtpHeaderExtensions(rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions_to_offer)362 RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
363 rtc::ArrayView<const RtpHeaderExtensionCapability>
364 header_extensions_to_offer) {
365 for (const auto& entry : header_extensions_to_offer) {
366 // Handle unsupported requests for mandatory extensions as per
367 // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.
368 // Note:
369 // - We do not handle setOfferedRtpHeaderExtensions algorithm step 2.1,
370 // this has to be checked on a higher level. We naturally error out
371 // in the handling of Step 2.2 if an unset URI is encountered.
372
373 // Step 2.2.
374 // Handle unknown extensions.
375 auto it = std::find_if(
376 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
377 [&entry](const auto& offered) { return entry.uri == offered.uri; });
378 if (it == header_extensions_to_offer_.end()) {
379 return RTCError(RTCErrorType::INVALID_PARAMETER,
380 "Attempted to modify an unoffered extension.");
381 }
382
383 // Step 2.4-2.5.
384 // - Use of the transceiver interface indicates unified plan is in effect,
385 // hence the MID extension needs to be enabled.
386 // - Also handle the mandatory video orientation extensions.
387 if ((entry.uri == RtpExtension::kMidUri ||
388 entry.uri == RtpExtension::kVideoRotationUri) &&
389 entry.direction != RtpTransceiverDirection::kSendRecv) {
390 return RTCError(RTCErrorType::INVALID_MODIFICATION,
391 "Attempted to stop a mandatory extension.");
392 }
393 }
394
395 // Apply mutation after error checking.
396 for (const auto& entry : header_extensions_to_offer) {
397 auto it = std::find_if(
398 header_extensions_to_offer_.begin(), header_extensions_to_offer_.end(),
399 [&entry](const auto& offered) { return entry.uri == offered.uri; });
400 it->direction = entry.direction;
401 }
402
403 return RTCError::OK();
404 }
405
406 } // namespace webrtc
407