blob: 6c5acacd7db51ca31b042708c0377f9e12015d5b [file] [log] [blame]
deadbeef6979b022015-09-24 23:47:531/*
kjellanderb24317b2016-02-10 15:54:432 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
deadbeef6979b022015-09-24 23:47:533 *
kjellanderb24317b2016-02-10 15:54:434 * 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.
deadbeef6979b022015-09-24 23:47:539 */
10
Steve Anton10542f22019-01-11 17:11:0011#include "pc/rtp_sender.h"
deadbeef6979b022015-09-24 23:47:5312
Harald Alvestrand5761e7b2021-01-29 14:45:0813#include <algorithm>
Yves Gereycb11a312019-07-26 16:51:5914#include <atomic>
Harald Alvestrandc24a2182022-02-23 13:44:5915#include <string>
Benjamin Wrightd81ac952018-08-30 00:02:1016#include <utility>
Steve Anton36b29d12017-10-30 16:57:4217#include <vector>
18
Harald Alvestrand5761e7b2021-01-29 14:45:0819#include "absl/algorithm/container.h"
Yves Gerey3e707812018-11-28 15:47:4920#include "api/audio_options.h"
Steve Anton10542f22019-01-11 17:11:0021#include "api/media_stream_interface.h"
Harald Alvestrand5761e7b2021-01-29 14:45:0822#include "api/priority.h"
Florent Castelli8c4b9ea2023-06-02 16:06:5723#include "api/rtc_error.h"
Steve Anton10542f22019-01-11 17:11:0024#include "media/base/media_engine.h"
Henrik Boströmf7859892022-07-04 12:36:3725#include "pc/legacy_stats_collector_interface.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3126#include "rtc_base/checks.h"
27#include "rtc_base/helpers.h"
Yves Gerey3e707812018-11-28 15:47:4928#include "rtc_base/logging.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3129#include "rtc_base/trace_event.h"
deadbeef70ab1a12015-09-28 23:53:5530
31namespace webrtc {
32
Harald Alvestrandc72af932018-01-11 16:18:1933namespace {
34
Yves Gereycb11a312019-07-26 16:51:5935// This function is only expected to be called on the signaling thread.
36// On the other hand, some test or even production setups may use
37// several signaling threads.
Harald Alvestrandc72af932018-01-11 16:18:1938int GenerateUniqueId() {
Yves Gereycb11a312019-07-26 16:51:5939 static std::atomic<int> g_unique_id{0};
Harald Alvestrandc72af932018-01-11 16:18:1940
41 return ++g_unique_id;
42}
43
Seth Hampson2d2c8882018-05-16 23:02:3244// Returns true if a "per-sender" encoding parameter contains a value that isn't
45// its default. Currently max_bitrate_bps and bitrate_priority both are
46// implemented "per-sender," meaning that these encoding parameters
47// are used for the RtpSender as a whole, not for a specific encoding layer.
48// This is done by setting these encoding parameters at index 0 of
49// RtpParameters.encodings. This function can be used to check if these
50// parameters are set at any index other than 0 of RtpParameters.encodings,
51// because they are currently unimplemented to be used for a specific encoding
52// layer.
53bool PerSenderRtpEncodingParameterHasValue(
54 const RtpEncodingParameters& encoding_params) {
Tim Haloun648d28a2018-10-18 23:52:2255 if (encoding_params.bitrate_priority != kDefaultBitratePriority ||
Taylor Brandstetter3f1aee32020-02-27 19:59:2356 encoding_params.network_priority != Priority::kLow) {
Seth Hampson2d2c8882018-05-16 23:02:3257 return true;
58 }
59 return false;
60}
61
Amit Hilbuch2297d332019-02-19 20:49:2262void RemoveEncodingLayers(const std::vector<std::string>& rids,
63 std::vector<RtpEncodingParameters>* encodings) {
64 RTC_DCHECK(encodings);
65 encodings->erase(
66 std::remove_if(encodings->begin(), encodings->end(),
67 [&rids](const RtpEncodingParameters& encoding) {
68 return absl::c_linear_search(rids, encoding.rid);
69 }),
70 encodings->end());
71}
72
73RtpParameters RestoreEncodingLayers(
74 const RtpParameters& parameters,
75 const std::vector<std::string>& removed_rids,
76 const std::vector<RtpEncodingParameters>& all_layers) {
Henrik Boström2b1f5092022-07-13 09:10:1477 RTC_CHECK_EQ(parameters.encodings.size() + removed_rids.size(),
78 all_layers.size());
Amit Hilbuch2297d332019-02-19 20:49:2279 RtpParameters result(parameters);
80 result.encodings.clear();
81 size_t index = 0;
82 for (const RtpEncodingParameters& encoding : all_layers) {
83 if (absl::c_linear_search(removed_rids, encoding.rid)) {
84 result.encodings.push_back(encoding);
85 continue;
86 }
87 result.encodings.push_back(parameters.encodings[index++]);
88 }
89 return result;
90}
91
Florent Castelliacabb362022-10-18 15:05:1692class SignalingThreadCallback {
93 public:
94 SignalingThreadCallback(rtc::Thread* signaling_thread,
95 SetParametersCallback callback)
96 : signaling_thread_(signaling_thread), callback_(std::move(callback)) {}
97 SignalingThreadCallback(SignalingThreadCallback&& other)
98 : signaling_thread_(other.signaling_thread_),
99 callback_(std::move(other.callback_)) {
100 other.callback_ = nullptr;
101 }
102
103 ~SignalingThreadCallback() {
104 if (callback_) {
105 Resolve(RTCError(RTCErrorType::INTERNAL_ERROR));
106
107 RTC_CHECK_NOTREACHED();
108 }
109 }
110
111 void operator()(const RTCError& error) { Resolve(error); }
112
113 private:
114 void Resolve(const RTCError& error) {
115 if (!signaling_thread_->IsCurrent()) {
116 signaling_thread_->PostTask(
117 [callback = std::move(callback_), error]() mutable {
118 webrtc::InvokeSetParametersCallback(callback, error);
119 });
120 callback_ = nullptr;
121 return;
122 }
123
124 webrtc::InvokeSetParametersCallback(callback_, error);
125 callback_ = nullptr;
126 }
127
128 rtc::Thread* signaling_thread_;
129 SetParametersCallback callback_;
130};
131
Florent Castelli892acf02018-10-01 20:47:20132} // namespace
133
Seth Hampson2d2c8882018-05-16 23:02:32134// Returns true if any RtpParameters member that isn't implemented contains a
135// value.
136bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) {
Florent Castelli87b3c512018-07-18 14:00:28137 if (!parameters.mid.empty()) {
Seth Hampson2d2c8882018-05-16 23:02:32138 return true;
139 }
140 for (size_t i = 0; i < parameters.encodings.size(); ++i) {
Seth Hampson2d2c8882018-05-16 23:02:32141 // Encoding parameters that are per-sender should only contain value at
142 // index 0.
143 if (i != 0 &&
144 PerSenderRtpEncodingParameterHasValue(parameters.encodings[i])) {
145 return true;
146 }
147 }
148 return false;
149}
150
Guido Urdaneta1ff16c82019-05-20 17:31:53151RtpSenderBase::RtpSenderBase(rtc::Thread* worker_thread,
152 const std::string& id,
153 SetStreamsObserver* set_streams_observer)
Tomas Gunnarssonfe328ca2022-02-16 19:02:12154 : signaling_thread_(rtc::Thread::Current()),
155 worker_thread_(worker_thread),
Guido Urdaneta1ff16c82019-05-20 17:31:53156 id_(id),
157 set_streams_observer_(set_streams_observer) {
Steve Anton47136dd2018-01-12 18:49:35158 RTC_DCHECK(worker_thread);
Florent Castelli892acf02018-10-01 20:47:20159 init_parameters_.encodings.emplace_back();
deadbeef20cb0c12017-02-02 04:27:00160}
deadbeeffac06552015-11-25 19:26:01161
Amit Hilbuchea7ef2a2019-02-19 23:20:21162void RtpSenderBase::SetFrameEncryptor(
Benjamin Wrightd81ac952018-08-30 00:02:10163 rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12164 RTC_DCHECK_RUN_ON(signaling_thread_);
Benjamin Wrightd81ac952018-08-30 00:02:10165 frame_encryptor_ = std::move(frame_encryptor);
Benjamin Wright6cc9cca2018-10-10 00:29:54166 // Special Case: Set the frame encryptor to any value on any existing channel.
Benjamin Wrightc462a6e2018-10-26 20:16:16167 if (media_channel_ && ssrc_ && !stopped_) {
Danil Chapovalov9e09a1f2022-09-08 16:38:10168 worker_thread_->BlockingCall(
169 [&] { media_channel_->SetFrameEncryptor(ssrc_, frame_encryptor_); });
Benjamin Wright6cc9cca2018-10-10 00:29:54170 }
Benjamin Wrightd81ac952018-08-30 00:02:10171}
172
Jonas Oreland65455162022-06-08 09:25:46173void RtpSenderBase::SetEncoderSelector(
174 std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>
175 encoder_selector) {
176 RTC_DCHECK_RUN_ON(signaling_thread_);
177 encoder_selector_ = std::move(encoder_selector);
178 SetEncoderSelectorOnChannel();
179}
180
181void RtpSenderBase::SetEncoderSelectorOnChannel() {
182 RTC_DCHECK_RUN_ON(signaling_thread_);
183 if (media_channel_ && ssrc_ && !stopped_) {
Danil Chapovalov9e09a1f2022-09-08 16:38:10184 worker_thread_->BlockingCall([&] {
Jonas Oreland65455162022-06-08 09:25:46185 media_channel_->SetEncoderSelector(ssrc_, encoder_selector_.get());
186 });
187 }
188}
189
Harald Alvestrand36fafc82022-12-08 08:47:42190void RtpSenderBase::SetMediaChannel(
191 cricket::MediaSendChannelInterface* media_channel) {
Amit Hilbuchdd9390c2018-11-14 00:26:05192 RTC_DCHECK(media_channel == nullptr ||
193 media_channel->media_type() == media_type());
Amit Hilbuchea7ef2a2019-02-19 23:20:21194 media_channel_ = media_channel;
Benjamin Wrightbfd412e2018-09-10 21:06:02195}
196
Amit Hilbuch619b2942019-02-26 23:55:19197RtpParameters RtpSenderBase::GetParametersInternal() const {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12198 RTC_DCHECK_RUN_ON(signaling_thread_);
Florent Castelli892acf02018-10-01 20:47:20199 if (stopped_) {
Taylor Brandstetterba29c6a2016-06-27 23:30:35200 return RtpParameters();
201 }
Amit Hilbuchaa584152019-02-07 01:09:52202 if (!media_channel_ || !ssrc_) {
Amit Hilbuch619b2942019-02-26 23:55:19203 return init_parameters_;
Florent Castelli892acf02018-10-01 20:47:20204 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10205 return worker_thread_->BlockingCall([&] {
Florent Castellicebf50f2018-05-03 13:31:53206 RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
Amit Hilbuch2297d332019-02-19 20:49:22207 RemoveEncodingLayers(disabled_rids_, &result.encodings);
Florent Castellicebf50f2018-05-03 13:31:53208 return result;
Steve Anton47136dd2018-01-12 18:49:35209 });
deadbeefa601f5c2016-06-06 21:27:39210}
211
Harald Alvestrand0166be82022-08-25 11:31:01212RtpParameters RtpSenderBase::GetParametersInternalWithAllLayers() const {
213 RTC_DCHECK_RUN_ON(signaling_thread_);
214 if (stopped_) {
215 return RtpParameters();
216 }
217 if (!media_channel_ || !ssrc_) {
218 return init_parameters_;
219 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10220 return worker_thread_->BlockingCall([&] {
Harald Alvestrand0166be82022-08-25 11:31:01221 RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
222 return result;
223 });
224}
225
Amit Hilbuch619b2942019-02-26 23:55:19226RtpParameters RtpSenderBase::GetParameters() const {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12227 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuch619b2942019-02-26 23:55:19228 RtpParameters result = GetParametersInternal();
229 last_transaction_id_ = rtc::CreateRandomUuid();
230 result.transaction_id = last_transaction_id_.value();
231 return result;
232}
233
Florent Castelliacabb362022-10-18 15:05:16234void RtpSenderBase::SetParametersInternal(const RtpParameters& parameters,
235 SetParametersCallback callback,
236 bool blocking) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12237 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuch619b2942019-02-26 23:55:19238 RTC_DCHECK(!stopped_);
Florent Castellicebf50f2018-05-03 13:31:53239
Seth Hampson2d2c8882018-05-16 23:02:32240 if (UnimplementedRtpParameterHasValue(parameters)) {
Florent Castelliacabb362022-10-18 15:05:16241 RTCError error(
Seth Hampson2d2c8882018-05-16 23:02:32242 RTCErrorType::UNSUPPORTED_PARAMETER,
243 "Attempted to set an unimplemented parameter of RtpParameters.");
Florent Castelliacabb362022-10-18 15:05:16244 RTC_LOG(LS_ERROR) << error.message() << " ("
245 << ::webrtc::ToString(error.type()) << ")";
246 webrtc::InvokeSetParametersCallback(callback, error);
247 return;
Seth Hampson2d2c8882018-05-16 23:02:32248 }
Amit Hilbuchaa584152019-02-07 01:09:52249 if (!media_channel_ || !ssrc_) {
Florent Castellic1a0bcb2019-01-29 13:26:48250 auto result = cricket::CheckRtpParametersInvalidModificationAndValues(
Florent Castelli725ee242022-10-18 15:05:58251 init_parameters_, parameters, video_codec_preferences_);
Florent Castelli892acf02018-10-01 20:47:20252 if (result.ok()) {
253 init_parameters_ = parameters;
254 }
Florent Castelliacabb362022-10-18 15:05:16255 webrtc::InvokeSetParametersCallback(callback, result);
256 return;
Florent Castelli892acf02018-10-01 20:47:20257 }
Florent Castelliacabb362022-10-18 15:05:16258 auto task = [&, callback = std::move(callback),
259 parameters = std::move(parameters)]() mutable {
Amit Hilbuch2297d332019-02-19 20:49:22260 RtpParameters rtp_parameters = parameters;
Florent Castelli725ee242022-10-18 15:05:58261 RtpParameters old_parameters = media_channel_->GetRtpSendParameters(ssrc_);
Amit Hilbuch2297d332019-02-19 20:49:22262 if (!disabled_rids_.empty()) {
263 // Need to add the inactive layers.
Amit Hilbuch2297d332019-02-19 20:49:22264 rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_,
265 old_parameters.encodings);
266 }
Florent Castelli725ee242022-10-18 15:05:58267
Florent Castelliacabb362022-10-18 15:05:16268 RTCError result = cricket::CheckRtpParametersInvalidModificationAndValues(
Florent Castelli725ee242022-10-18 15:05:58269 old_parameters, rtp_parameters);
Florent Castelliacabb362022-10-18 15:05:16270 if (!result.ok()) {
271 webrtc::InvokeSetParametersCallback(callback, result);
272 return;
273 }
Florent Castelli725ee242022-10-18 15:05:58274
275 result = CheckSVCParameters(rtp_parameters);
Florent Castelliacabb362022-10-18 15:05:16276 if (!result.ok()) {
277 webrtc::InvokeSetParametersCallback(callback, result);
278 return;
279 }
Florent Castelli725ee242022-10-18 15:05:58280
Florent Castelliacabb362022-10-18 15:05:16281 media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters,
282 std::move(callback));
283 };
284 if (blocking)
285 worker_thread_->BlockingCall(task);
286 else
287 worker_thread_->PostTask(std::move(task));
deadbeefa601f5c2016-06-06 21:27:39288}
289
Harald Alvestrand0166be82022-08-25 11:31:01290RTCError RtpSenderBase::SetParametersInternalWithAllLayers(
291 const RtpParameters& parameters) {
292 RTC_DCHECK_RUN_ON(signaling_thread_);
293 RTC_DCHECK(!stopped_);
294
295 if (UnimplementedRtpParameterHasValue(parameters)) {
296 LOG_AND_RETURN_ERROR(
297 RTCErrorType::UNSUPPORTED_PARAMETER,
298 "Attempted to set an unimplemented parameter of RtpParameters.");
299 }
300 if (!media_channel_ || !ssrc_) {
301 auto result = cricket::CheckRtpParametersInvalidModificationAndValues(
Florent Castelli725ee242022-10-18 15:05:58302 init_parameters_, parameters, video_codec_preferences_);
Harald Alvestrand0166be82022-08-25 11:31:01303 if (result.ok()) {
304 init_parameters_ = parameters;
305 }
306 return result;
307 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10308 return worker_thread_->BlockingCall([&] {
Harald Alvestrand0166be82022-08-25 11:31:01309 RtpParameters rtp_parameters = parameters;
Florent Castelliacabb362022-10-18 15:05:16310 return media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters, nullptr);
Harald Alvestrand0166be82022-08-25 11:31:01311 });
312}
313
Florent Castelliacabb362022-10-18 15:05:16314RTCError RtpSenderBase::CheckSetParameters(const RtpParameters& parameters) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12315 RTC_DCHECK_RUN_ON(signaling_thread_);
Harald Alvestrand6060df52020-08-11 07:54:02316 if (is_transceiver_stopped_) {
317 LOG_AND_RETURN_ERROR(
318 RTCErrorType::INVALID_STATE,
319 "Cannot set parameters on sender of a stopped transceiver.");
320 }
321 if (stopped_) {
322 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
323 "Cannot set parameters on a stopped sender.");
324 }
Amit Hilbuch619b2942019-02-26 23:55:19325 if (!last_transaction_id_) {
326 LOG_AND_RETURN_ERROR(
327 RTCErrorType::INVALID_STATE,
328 "Failed to set parameters since getParameters() has never been called"
329 " on this sender");
330 }
331 if (last_transaction_id_ != parameters.transaction_id) {
332 LOG_AND_RETURN_ERROR(
333 RTCErrorType::INVALID_MODIFICATION,
334 "Failed to set parameters since the transaction_id doesn't match"
335 " the last value returned from getParameters()");
336 }
337
Florent Castelliacabb362022-10-18 15:05:16338 return RTCError::OK();
339}
340
341RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) {
342 RTC_DCHECK_RUN_ON(signaling_thread_);
343 TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters");
344 RTCError result = CheckSetParameters(parameters);
345 if (!result.ok())
346 return result;
347
348 // Some tests rely on working in single thread mode without a run loop and a
349 // blocking call is required to keep them working. The encoder configuration
350 // also involves another thread with an asynchronous task, thus we still do
351 // need to wait for the callback to be resolved this way.
352 std::unique_ptr<rtc::Event> done_event = std::make_unique<rtc::Event>();
353 SetParametersInternal(
354 parameters,
355 [done = done_event.get(), &result](RTCError error) {
356 result = error;
357 done->Set();
358 },
359 true);
360 done_event->Wait(rtc::Event::kForever);
Amit Hilbuch619b2942019-02-26 23:55:19361 last_transaction_id_.reset();
362 return result;
363}
364
Florent Castelliacabb362022-10-18 15:05:16365void RtpSenderBase::SetParametersAsync(const RtpParameters& parameters,
366 SetParametersCallback callback) {
367 RTC_DCHECK_RUN_ON(signaling_thread_);
368 RTC_DCHECK(callback);
369 TRACE_EVENT0("webrtc", "RtpSenderBase::SetParametersAsync");
370 RTCError result = CheckSetParameters(parameters);
371 if (!result.ok()) {
372 webrtc::InvokeSetParametersCallback(callback, result);
373 return;
374 }
375
376 SetParametersInternal(
377 parameters,
378 SignalingThreadCallback(
379 signaling_thread_,
380 [this, callback = std::move(callback)](RTCError error) mutable {
381 last_transaction_id_.reset();
382 webrtc::InvokeSetParametersCallback(callback, error);
383 }),
384 false);
385}
386
Philipp Hanckee04c3972022-12-21 14:46:46387void RtpSenderBase::set_stream_ids(const std::vector<std::string>& stream_ids) {
388 stream_ids_.clear();
389 absl::c_copy_if(stream_ids, std::back_inserter(stream_ids_),
390 [this](const std::string& stream_id) {
391 return !absl::c_linear_search(stream_ids_, stream_id);
392 });
393}
394
Guido Urdaneta1ff16c82019-05-20 17:31:53395void RtpSenderBase::SetStreams(const std::vector<std::string>& stream_ids) {
396 set_stream_ids(stream_ids);
397 if (set_streams_observer_)
398 set_streams_observer_->OnSetStreams();
399}
400
Amit Hilbuchea7ef2a2019-02-19 23:20:21401bool RtpSenderBase::SetTrack(MediaStreamTrackInterface* track) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12402 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21403 TRACE_EVENT0("webrtc", "RtpSenderBase::SetTrack");
404 if (stopped_) {
405 RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender.";
406 return false;
Benjamin Wright6cc9cca2018-10-10 00:29:54407 }
Amit Hilbuchea7ef2a2019-02-19 23:20:21408 if (track && track->kind() != track_kind()) {
409 RTC_LOG(LS_ERROR) << "SetTrack with " << track->kind()
410 << " called on RtpSender with " << track_kind()
411 << " track.";
412 return false;
413 }
414
415 // Detach from old track.
416 if (track_) {
417 DetachTrack();
418 track_->UnregisterObserver(this);
419 RemoveTrackFromStats();
420 }
421
422 // Attach to new track.
423 bool prev_can_send_track = can_send_track();
424 // Keep a reference to the old track to keep it alive until we call SetSend.
425 rtc::scoped_refptr<MediaStreamTrackInterface> old_track = track_;
426 track_ = track;
427 if (track_) {
428 track_->RegisterObserver(this);
429 AttachTrack();
430 }
431
432 // Update channel.
433 if (can_send_track()) {
434 SetSend();
435 AddTrackToStats();
436 } else if (prev_can_send_track) {
437 ClearSend();
438 }
439 attachment_id_ = (track_ ? GenerateUniqueId() : 0);
440 return true;
Benjamin Wrightd81ac952018-08-30 00:02:10441}
442
Amit Hilbuchea7ef2a2019-02-19 23:20:21443void RtpSenderBase::SetSsrc(uint32_t ssrc) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12444 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21445 TRACE_EVENT0("webrtc", "RtpSenderBase::SetSsrc");
deadbeeffac06552015-11-25 19:26:01446 if (stopped_ || ssrc == ssrc_) {
447 return;
448 }
449 // If we are already sending with a particular SSRC, stop sending.
450 if (can_send_track()) {
Amit Hilbuchea7ef2a2019-02-19 23:20:21451 ClearSend();
452 RemoveTrackFromStats();
deadbeeffac06552015-11-25 19:26:01453 }
454 ssrc_ = ssrc;
455 if (can_send_track()) {
Amit Hilbuchea7ef2a2019-02-19 23:20:21456 SetSend();
457 AddTrackToStats();
deadbeeffac06552015-11-25 19:26:01458 }
Byoungchan Lee10a7d232022-07-20 12:49:17459 if (!init_parameters_.encodings.empty() ||
460 init_parameters_.degradation_preference.has_value()) {
Danil Chapovalov9e09a1f2022-09-08 16:38:10461 worker_thread_->BlockingCall([&] {
Florent Castelli892acf02018-10-01 20:47:20462 RTC_DCHECK(media_channel_);
463 // Get the current parameters, which are constructed from the SDP.
464 // The number of layers in the SDP is currently authoritative to support
465 // SDP munging for Plan-B simulcast with "a=ssrc-group:SIM <ssrc-id>..."
466 // lines as described in RFC 5576.
467 // All fields should be default constructed and the SSRC field set, which
468 // we need to copy.
469 RtpParameters current_parameters =
470 media_channel_->GetRtpSendParameters(ssrc_);
Harald Alvestrand3fe8b0d2022-07-01 07:36:59471 RTC_CHECK_GE(current_parameters.encodings.size(),
472 init_parameters_.encodings.size());
Florent Castelli892acf02018-10-01 20:47:20473 for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
474 init_parameters_.encodings[i].ssrc =
475 current_parameters.encodings[i].ssrc;
Amit Hilbuch2297d332019-02-19 20:49:22476 init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid;
Florent Castelli892acf02018-10-01 20:47:20477 current_parameters.encodings[i] = init_parameters_.encodings[i];
478 }
479 current_parameters.degradation_preference =
480 init_parameters_.degradation_preference;
Florent Castelliacabb362022-10-18 15:05:16481 media_channel_->SetRtpSendParameters(ssrc_, current_parameters, nullptr);
Florent Castelli892acf02018-10-01 20:47:20482 init_parameters_.encodings.clear();
Byoungchan Lee10a7d232022-07-20 12:49:17483 init_parameters_.degradation_preference = absl::nullopt;
Florent Castelli892acf02018-10-01 20:47:20484 });
485 }
Amit Hilbuchea7ef2a2019-02-19 23:20:21486 // Attempt to attach the frame decryptor to the current media channel.
487 if (frame_encryptor_) {
488 SetFrameEncryptor(frame_encryptor_);
489 }
Marina Cioceae77912b2020-02-27 15:16:55490 if (frame_transformer_) {
491 SetEncoderToPacketizerFrameTransformer(frame_transformer_);
492 }
Jonas Oreland65455162022-06-08 09:25:46493 if (encoder_selector_) {
494 SetEncoderSelectorOnChannel();
495 }
deadbeeffac06552015-11-25 19:26:01496}
497
Amit Hilbuchea7ef2a2019-02-19 23:20:21498void RtpSenderBase::Stop() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12499 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21500 TRACE_EVENT0("webrtc", "RtpSenderBase::Stop");
deadbeef70ab1a12015-09-28 23:53:55501 // TODO(deadbeef): Need to do more here to fully stop sending packets.
deadbeeffac06552015-11-25 19:26:01502 if (stopped_) {
deadbeef70ab1a12015-09-28 23:53:55503 return;
504 }
deadbeeffac06552015-11-25 19:26:01505 if (track_) {
Amit Hilbuchea7ef2a2019-02-19 23:20:21506 DetachTrack();
deadbeeffac06552015-11-25 19:26:01507 track_->UnregisterObserver(this);
508 }
509 if (can_send_track()) {
Amit Hilbuchea7ef2a2019-02-19 23:20:21510 ClearSend();
511 RemoveTrackFromStats();
deadbeeffac06552015-11-25 19:26:01512 }
Harald Alvestrand3d976f62018-03-19 18:05:06513 media_channel_ = nullptr;
Guido Urdaneta1ff16c82019-05-20 17:31:53514 set_streams_observer_ = nullptr;
deadbeeffac06552015-11-25 19:26:01515 stopped_ = true;
deadbeef70ab1a12015-09-28 23:53:55516}
517
Amit Hilbuchea7ef2a2019-02-19 23:20:21518RTCError RtpSenderBase::DisableEncodingLayers(
Amit Hilbuch2297d332019-02-19 20:49:22519 const std::vector<std::string>& rids) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12520 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuch2297d332019-02-19 20:49:22521 if (stopped_) {
Amit Hilbuch619b2942019-02-26 23:55:19522 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
523 "Cannot disable encodings on a stopped sender.");
Amit Hilbuch2297d332019-02-19 20:49:22524 }
525
Amit Hilbuch619b2942019-02-26 23:55:19526 if (rids.empty()) {
Amit Hilbuch2297d332019-02-19 20:49:22527 return RTCError::OK();
528 }
529
530 // Check that all the specified layers exist and disable them in the channel.
Harald Alvestrand0166be82022-08-25 11:31:01531 RtpParameters parameters = GetParametersInternalWithAllLayers();
Amit Hilbuch2297d332019-02-19 20:49:22532 for (const std::string& rid : rids) {
Amit Hilbuch619b2942019-02-26 23:55:19533 if (absl::c_none_of(parameters.encodings,
534 [&rid](const RtpEncodingParameters& encoding) {
535 return encoding.rid == rid;
536 })) {
537 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
538 "RID: " + rid + " does not refer to a valid layer.");
Amit Hilbuch2297d332019-02-19 20:49:22539 }
Amit Hilbuch2297d332019-02-19 20:49:22540 }
541
Amit Hilbuch619b2942019-02-26 23:55:19542 if (!media_channel_ || !ssrc_) {
543 RemoveEncodingLayers(rids, &init_parameters_.encodings);
544 // Invalidate any transaction upon success.
545 last_transaction_id_.reset();
546 return RTCError::OK();
547 }
548
549 for (RtpEncodingParameters& encoding : parameters.encodings) {
550 // Remain active if not in the disable list.
551 encoding.active &= absl::c_none_of(
552 rids,
553 [&encoding](const std::string& rid) { return encoding.rid == rid; });
554 }
555
Harald Alvestrand0166be82022-08-25 11:31:01556 RTCError result = SetParametersInternalWithAllLayers(parameters);
Amit Hilbuch2297d332019-02-19 20:49:22557 if (result.ok()) {
558 disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end());
Amit Hilbuch619b2942019-02-26 23:55:19559 // Invalidate any transaction upon success.
560 last_transaction_id_.reset();
Amit Hilbuch2297d332019-02-19 20:49:22561 }
562 return result;
563}
564
Marina Cioceae77912b2020-02-27 15:16:55565void RtpSenderBase::SetEncoderToPacketizerFrameTransformer(
566 rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12567 RTC_DCHECK_RUN_ON(signaling_thread_);
Marina Cioceae77912b2020-02-27 15:16:55568 frame_transformer_ = std::move(frame_transformer);
569 if (media_channel_ && ssrc_ && !stopped_) {
Danil Chapovalov9e09a1f2022-09-08 16:38:10570 worker_thread_->BlockingCall([&] {
Marina Cioceae77912b2020-02-27 15:16:55571 media_channel_->SetEncoderToPacketizerFrameTransformer(
572 ssrc_, frame_transformer_);
573 });
574 }
575}
576
Amit Hilbuchea7ef2a2019-02-19 23:20:21577LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {}
578
579LocalAudioSinkAdapter::~LocalAudioSinkAdapter() {
Markus Handell6fcd0f82020-07-07 17:08:53580 MutexLock lock(&lock_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21581 if (sink_)
582 sink_->OnClose();
583}
584
Minyue Li99d6d812020-01-29 09:25:12585void LocalAudioSinkAdapter::OnData(
586 const void* audio_data,
587 int bits_per_sample,
588 int sample_rate,
589 size_t number_of_channels,
590 size_t number_of_frames,
591 absl::optional<int64_t> absolute_capture_timestamp_ms) {
Olga Sharonova2d0ba282022-09-27 13:22:34592 TRACE_EVENT2("webrtc", "LocalAudioSinkAdapter::OnData", "sample_rate",
593 sample_rate, "number_of_frames", number_of_frames);
Markus Handell6fcd0f82020-07-07 17:08:53594 MutexLock lock(&lock_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21595 if (sink_) {
596 sink_->OnData(audio_data, bits_per_sample, sample_rate, number_of_channels,
Minyue Li99d6d812020-01-29 09:25:12597 number_of_frames, absolute_capture_timestamp_ms);
Gustaf Ullberg46ea5d72020-12-15 14:12:16598 num_preferred_channels_ = sink_->NumPreferredChannels();
Amit Hilbuchea7ef2a2019-02-19 23:20:21599 }
600}
601
602void LocalAudioSinkAdapter::SetSink(cricket::AudioSource::Sink* sink) {
Markus Handell6fcd0f82020-07-07 17:08:53603 MutexLock lock(&lock_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21604 RTC_DCHECK(!sink || !sink_);
605 sink_ = sink;
606}
607
608rtc::scoped_refptr<AudioRtpSender> AudioRtpSender::Create(
609 rtc::Thread* worker_thread,
610 const std::string& id,
Henrik Boströmf7859892022-07-04 12:36:37611 LegacyStatsCollectorInterface* stats,
Guido Urdaneta1ff16c82019-05-20 17:31:53612 SetStreamsObserver* set_streams_observer) {
Tommi87f70902021-04-27 12:43:08613 return rtc::make_ref_counted<AudioRtpSender>(worker_thread, id, stats,
614 set_streams_observer);
Amit Hilbuchea7ef2a2019-02-19 23:20:21615}
616
617AudioRtpSender::AudioRtpSender(rtc::Thread* worker_thread,
618 const std::string& id,
Henrik Boströmf7859892022-07-04 12:36:37619 LegacyStatsCollectorInterface* legacy_stats,
Guido Urdaneta1ff16c82019-05-20 17:31:53620 SetStreamsObserver* set_streams_observer)
621 : RtpSenderBase(worker_thread, id, set_streams_observer),
Henrik Boströmf7859892022-07-04 12:36:37622 legacy_stats_(legacy_stats),
Fredrik Solenbergda2afbd2022-08-03 10:07:51623 dtmf_sender_(DtmfSender::Create(rtc::Thread::Current(), this)),
624 dtmf_sender_proxy_(
625 DtmfSenderProxy::Create(rtc::Thread::Current(), dtmf_sender_)),
Amit Hilbuchea7ef2a2019-02-19 23:20:21626 sink_adapter_(new LocalAudioSinkAdapter()) {}
627
628AudioRtpSender::~AudioRtpSender() {
Fredrik Solenbergda2afbd2022-08-03 10:07:51629 dtmf_sender_->OnDtmfProviderDestroyed();
Amit Hilbuchea7ef2a2019-02-19 23:20:21630 Stop();
631}
632
633bool AudioRtpSender::CanInsertDtmf() {
634 if (!media_channel_) {
635 RTC_LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
636 return false;
637 }
638 // Check that this RTP sender is active (description has been applied that
639 // matches an SSRC to its ID).
640 if (!ssrc_) {
641 RTC_LOG(LS_ERROR) << "CanInsertDtmf: Sender does not have SSRC.";
642 return false;
643 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10644 return worker_thread_->BlockingCall(
645 [&] { return voice_media_channel()->CanInsertDtmf(); });
Amit Hilbuchea7ef2a2019-02-19 23:20:21646}
647
648bool AudioRtpSender::InsertDtmf(int code, int duration) {
649 if (!media_channel_) {
650 RTC_LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
651 return false;
652 }
653 if (!ssrc_) {
654 RTC_LOG(LS_ERROR) << "InsertDtmf: Sender does not have SSRC.";
655 return false;
656 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10657 bool success = worker_thread_->BlockingCall(
658 [&] { return voice_media_channel()->InsertDtmf(ssrc_, code, duration); });
Amit Hilbuchea7ef2a2019-02-19 23:20:21659 if (!success) {
660 RTC_LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
661 }
662 return success;
663}
664
Amit Hilbuchea7ef2a2019-02-19 23:20:21665void AudioRtpSender::OnChanged() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12666 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21667 TRACE_EVENT0("webrtc", "AudioRtpSender::OnChanged");
668 RTC_DCHECK(!stopped_);
669 if (cached_track_enabled_ != track_->enabled()) {
670 cached_track_enabled_ = track_->enabled();
671 if (can_send_track()) {
672 SetSend();
673 }
674 }
675}
676
677void AudioRtpSender::DetachTrack() {
678 RTC_DCHECK(track_);
679 audio_track()->RemoveSink(sink_adapter_.get());
680}
681
682void AudioRtpSender::AttachTrack() {
683 RTC_DCHECK(track_);
684 cached_track_enabled_ = track_->enabled();
685 audio_track()->AddSink(sink_adapter_.get());
686}
687
688void AudioRtpSender::AddTrackToStats() {
Henrik Boströmf7859892022-07-04 12:36:37689 if (can_send_track() && legacy_stats_) {
690 legacy_stats_->AddLocalAudioTrack(audio_track().get(), ssrc_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21691 }
692}
693
694void AudioRtpSender::RemoveTrackFromStats() {
Henrik Boströmf7859892022-07-04 12:36:37695 if (can_send_track() && legacy_stats_) {
696 legacy_stats_->RemoveLocalAudioTrack(audio_track().get(), ssrc_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21697 }
698}
699
700rtc::scoped_refptr<DtmfSenderInterface> AudioRtpSender::GetDtmfSender() const {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12701 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21702 return dtmf_sender_proxy_;
703}
704
Philipp Hanckea1b4eb22022-11-04 13:45:23705RTCError AudioRtpSender::GenerateKeyFrame(
706 const std::vector<std::string>& rids) {
Philipp Hancked237c2b2022-10-25 07:54:28707 RTC_DCHECK_RUN_ON(signaling_thread_);
708 RTC_DLOG(LS_ERROR) << "Tried to get generate a key frame for audio.";
709 return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
710 "Generating key frames for audio is not supported.");
711}
712
Amit Hilbuchea7ef2a2019-02-19 23:20:21713void AudioRtpSender::SetSend() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12714 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21715 RTC_DCHECK(!stopped_);
716 RTC_DCHECK(can_send_track());
717 if (!media_channel_) {
718 RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
719 return;
720 }
721 cricket::AudioOptions options;
722#if !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_WEBKIT_BUILD)
723 // TODO(tommi): Remove this hack when we move CreateAudioSource out of
724 // PeerConnection. This is a bit of a strange way to apply local audio
725 // options since it is also applied to all streams/channels, local or remote.
726 if (track_->enabled() && audio_track()->GetSource() &&
727 !audio_track()->GetSource()->remote()) {
728 options = audio_track()->GetSource()->options();
729 }
730#endif
731
Artem Titovcfea2182021-08-09 23:22:31732 // `track_->enabled()` hops to the signaling thread, so call it before we hop
Amit Hilbuchea7ef2a2019-02-19 23:20:21733 // to the worker thread or else it will deadlock.
734 bool track_enabled = track_->enabled();
Danil Chapovalov9e09a1f2022-09-08 16:38:10735 bool success = worker_thread_->BlockingCall([&] {
Amit Hilbuchea7ef2a2019-02-19 23:20:21736 return voice_media_channel()->SetAudioSend(ssrc_, track_enabled, &options,
737 sink_adapter_.get());
738 });
739 if (!success) {
740 RTC_LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc_;
741 }
742}
743
744void AudioRtpSender::ClearSend() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12745 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21746 RTC_DCHECK(ssrc_ != 0);
747 RTC_DCHECK(!stopped_);
748 if (!media_channel_) {
749 RTC_LOG(LS_WARNING) << "ClearAudioSend: No audio channel exists.";
750 return;
751 }
752 cricket::AudioOptions options;
Danil Chapovalov9e09a1f2022-09-08 16:38:10753 bool success = worker_thread_->BlockingCall([&] {
Amit Hilbuchea7ef2a2019-02-19 23:20:21754 return voice_media_channel()->SetAudioSend(ssrc_, false, &options, nullptr);
755 });
756 if (!success) {
757 RTC_LOG(LS_WARNING) << "ClearAudioSend: ssrc is incorrect: " << ssrc_;
758 }
759}
760
761rtc::scoped_refptr<VideoRtpSender> VideoRtpSender::Create(
762 rtc::Thread* worker_thread,
Guido Urdaneta1ff16c82019-05-20 17:31:53763 const std::string& id,
764 SetStreamsObserver* set_streams_observer) {
Tommi87f70902021-04-27 12:43:08765 return rtc::make_ref_counted<VideoRtpSender>(worker_thread, id,
766 set_streams_observer);
Amit Hilbuchea7ef2a2019-02-19 23:20:21767}
768
769VideoRtpSender::VideoRtpSender(rtc::Thread* worker_thread,
Guido Urdaneta1ff16c82019-05-20 17:31:53770 const std::string& id,
771 SetStreamsObserver* set_streams_observer)
772 : RtpSenderBase(worker_thread, id, set_streams_observer) {}
Amit Hilbuchea7ef2a2019-02-19 23:20:21773
774VideoRtpSender::~VideoRtpSender() {
775 Stop();
776}
777
778void VideoRtpSender::OnChanged() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12779 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21780 TRACE_EVENT0("webrtc", "VideoRtpSender::OnChanged");
781 RTC_DCHECK(!stopped_);
Tomas Gunnarssondfd69c22022-02-15 12:56:50782
783 auto content_hint = video_track()->content_hint();
784 if (cached_track_content_hint_ != content_hint) {
785 cached_track_content_hint_ = content_hint;
Amit Hilbuchea7ef2a2019-02-19 23:20:21786 if (can_send_track()) {
787 SetSend();
788 }
789 }
790}
791
792void VideoRtpSender::AttachTrack() {
793 RTC_DCHECK(track_);
794 cached_track_content_hint_ = video_track()->content_hint();
795}
796
797rtc::scoped_refptr<DtmfSenderInterface> VideoRtpSender::GetDtmfSender() const {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12798 RTC_DCHECK_RUN_ON(signaling_thread_);
799 RTC_DLOG(LS_ERROR) << "Tried to get DTMF sender from video sender.";
Amit Hilbuchea7ef2a2019-02-19 23:20:21800 return nullptr;
801}
802
Philipp Hanckea1b4eb22022-11-04 13:45:23803RTCError VideoRtpSender::GenerateKeyFrame(
804 const std::vector<std::string>& rids) {
Philipp Hancked237c2b2022-10-25 07:54:28805 RTC_DCHECK_RUN_ON(signaling_thread_);
806 if (video_media_channel() && ssrc_ && !stopped_) {
Philipp Hanckefa67bcc2022-12-07 07:59:24807 const auto parameters = GetParametersInternal();
Philipp Hanckeb83cd922022-11-09 10:06:38808 for (const auto& rid : rids) {
809 if (rid.empty()) {
810 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
811 "Attempted to specify an empty rid.");
812 }
813 if (!absl::c_any_of(parameters.encodings,
814 [&rid](const RtpEncodingParameters& parameters) {
815 return parameters.rid == rid;
816 })) {
817 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
818 "Attempted to specify a rid not configured.");
819 }
820 }
Philipp Hanckea1b4eb22022-11-04 13:45:23821 worker_thread_->PostTask([&, rids] {
822 video_media_channel()->GenerateSendKeyFrame(ssrc_, rids);
823 });
Philipp Hancked237c2b2022-10-25 07:54:28824 } else {
825 RTC_LOG(LS_WARNING) << "Tried to generate key frame for sender that is "
826 "stopped or has no media channel.";
827 }
828 return RTCError::OK();
829}
830
Amit Hilbuchea7ef2a2019-02-19 23:20:21831void VideoRtpSender::SetSend() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12832 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21833 RTC_DCHECK(!stopped_);
834 RTC_DCHECK(can_send_track());
835 if (!media_channel_) {
836 RTC_LOG(LS_ERROR) << "SetVideoSend: No video channel exists.";
837 return;
838 }
839 cricket::VideoOptions options;
840 VideoTrackSourceInterface* source = video_track()->GetSource();
841 if (source) {
842 options.is_screencast = source->is_screencast();
843 options.video_noise_reduction = source->needs_denoising();
844 }
Florent Castellib05ca4b2020-03-05 12:39:55845 options.content_hint = cached_track_content_hint_;
Amit Hilbuchea7ef2a2019-02-19 23:20:21846 switch (cached_track_content_hint_) {
847 case VideoTrackInterface::ContentHint::kNone:
848 break;
849 case VideoTrackInterface::ContentHint::kFluid:
850 options.is_screencast = false;
851 break;
852 case VideoTrackInterface::ContentHint::kDetailed:
853 case VideoTrackInterface::ContentHint::kText:
854 options.is_screencast = true;
855 break;
856 }
Danil Chapovalov9e09a1f2022-09-08 16:38:10857 bool success = worker_thread_->BlockingCall([&] {
Niels Möllerafb246b2022-04-20 12:26:50858 return video_media_channel()->SetVideoSend(ssrc_, &options,
859 video_track().get());
Amit Hilbuchea7ef2a2019-02-19 23:20:21860 });
861 RTC_DCHECK(success);
862}
863
864void VideoRtpSender::ClearSend() {
Tomas Gunnarssonfe328ca2022-02-16 19:02:12865 RTC_DCHECK_RUN_ON(signaling_thread_);
Amit Hilbuchea7ef2a2019-02-19 23:20:21866 RTC_DCHECK(ssrc_ != 0);
867 RTC_DCHECK(!stopped_);
868 if (!media_channel_) {
869 RTC_LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
870 return;
871 }
Artem Titov880fa812021-07-30 20:30:23872 // Allow SetVideoSend to fail since `enable` is false and `source` is null.
Amit Hilbuchea7ef2a2019-02-19 23:20:21873 // This the normal case when the underlying media channel has already been
874 // deleted.
Danil Chapovalov9e09a1f2022-09-08 16:38:10875 worker_thread_->BlockingCall(
876 [&] { video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr); });
Amit Hilbuchea7ef2a2019-02-19 23:20:21877}
878
Florent Castelli725ee242022-10-18 15:05:58879RTCError VideoRtpSender::CheckSVCParameters(const RtpParameters& parameters) {
Florent Castelli8c4b9ea2023-06-02 16:06:57880 absl::optional<cricket::VideoCodec> send_codec =
881 video_media_channel()->GetSendCodec();
Florent Castelli725ee242022-10-18 15:05:58882
883 // Match the currently used codec against the codec preferences to gather
884 // the SVC capabilities.
885 std::vector<cricket::VideoCodec> codecs;
Florent Castelli8c4b9ea2023-06-02 16:06:57886 if (send_codec) {
887 for (const auto& codec_preference : video_codec_preferences_) {
888 if (send_codec->Matches(codec_preference)) {
889 codecs.push_back(codec_preference);
890 break;
891 }
Florent Castelli725ee242022-10-18 15:05:58892 }
893 }
894
895 return cricket::CheckScalabilityModeValues(parameters, codecs);
896}
897
deadbeef70ab1a12015-09-28 23:53:55898} // namespace webrtc