• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2015 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_sender.h"
12 
13 #include <atomic>
14 #include <utility>
15 #include <vector>
16 
17 #include "api/audio_options.h"
18 #include "api/media_stream_interface.h"
19 #include "media/base/media_engine.h"
20 #include "pc/peer_connection.h"
21 #include "pc/stats_collector.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/helpers.h"
24 #include "rtc_base/location.h"
25 #include "rtc_base/logging.h"
26 #include "rtc_base/trace_event.h"
27 
28 namespace webrtc {
29 
30 namespace {
31 
32 // This function is only expected to be called on the signaling thread.
33 // On the other hand, some test or even production setups may use
34 // several signaling threads.
GenerateUniqueId()35 int GenerateUniqueId() {
36   static std::atomic<int> g_unique_id{0};
37 
38   return ++g_unique_id;
39 }
40 
41 // Returns true if a "per-sender" encoding parameter contains a value that isn't
42 // its default. Currently max_bitrate_bps and bitrate_priority both are
43 // implemented "per-sender," meaning that these encoding parameters
44 // are used for the RtpSender as a whole, not for a specific encoding layer.
45 // This is done by setting these encoding parameters at index 0 of
46 // RtpParameters.encodings. This function can be used to check if these
47 // parameters are set at any index other than 0 of RtpParameters.encodings,
48 // because they are currently unimplemented to be used for a specific encoding
49 // layer.
PerSenderRtpEncodingParameterHasValue(const RtpEncodingParameters & encoding_params)50 bool PerSenderRtpEncodingParameterHasValue(
51     const RtpEncodingParameters& encoding_params) {
52   if (encoding_params.bitrate_priority != kDefaultBitratePriority ||
53       encoding_params.network_priority != Priority::kLow) {
54     return true;
55   }
56   return false;
57 }
58 
RemoveEncodingLayers(const std::vector<std::string> & rids,std::vector<RtpEncodingParameters> * encodings)59 void RemoveEncodingLayers(const std::vector<std::string>& rids,
60                           std::vector<RtpEncodingParameters>* encodings) {
61   RTC_DCHECK(encodings);
62   encodings->erase(
63       std::remove_if(encodings->begin(), encodings->end(),
64                      [&rids](const RtpEncodingParameters& encoding) {
65                        return absl::c_linear_search(rids, encoding.rid);
66                      }),
67       encodings->end());
68 }
69 
RestoreEncodingLayers(const RtpParameters & parameters,const std::vector<std::string> & removed_rids,const std::vector<RtpEncodingParameters> & all_layers)70 RtpParameters RestoreEncodingLayers(
71     const RtpParameters& parameters,
72     const std::vector<std::string>& removed_rids,
73     const std::vector<RtpEncodingParameters>& all_layers) {
74   RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(),
75                 all_layers.size());
76   RtpParameters result(parameters);
77   result.encodings.clear();
78   size_t index = 0;
79   for (const RtpEncodingParameters& encoding : all_layers) {
80     if (absl::c_linear_search(removed_rids, encoding.rid)) {
81       result.encodings.push_back(encoding);
82       continue;
83     }
84     result.encodings.push_back(parameters.encodings[index++]);
85   }
86   return result;
87 }
88 
89 }  // namespace
90 
91 // Returns true if any RtpParameters member that isn't implemented contains a
92 // value.
UnimplementedRtpParameterHasValue(const RtpParameters & parameters)93 bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) {
94   if (!parameters.mid.empty()) {
95     return true;
96   }
97   for (size_t i = 0; i < parameters.encodings.size(); ++i) {
98     // Encoding parameters that are per-sender should only contain value at
99     // index 0.
100     if (i != 0 &&
101         PerSenderRtpEncodingParameterHasValue(parameters.encodings[i])) {
102       return true;
103     }
104   }
105   return false;
106 }
107 
RtpSenderBase(rtc::Thread * worker_thread,const std::string & id,SetStreamsObserver * set_streams_observer)108 RtpSenderBase::RtpSenderBase(rtc::Thread* worker_thread,
109                              const std::string& id,
110                              SetStreamsObserver* set_streams_observer)
111     : worker_thread_(worker_thread),
112       id_(id),
113       set_streams_observer_(set_streams_observer) {
114   RTC_DCHECK(worker_thread);
115   init_parameters_.encodings.emplace_back();
116 }
117 
SetFrameEncryptor(rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor)118 void RtpSenderBase::SetFrameEncryptor(
119     rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor) {
120   frame_encryptor_ = std::move(frame_encryptor);
121   // Special Case: Set the frame encryptor to any value on any existing channel.
122   if (media_channel_ && ssrc_ && !stopped_) {
123     worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
124       media_channel_->SetFrameEncryptor(ssrc_, frame_encryptor_);
125     });
126   }
127 }
128 
SetMediaChannel(cricket::MediaChannel * media_channel)129 void RtpSenderBase::SetMediaChannel(cricket::MediaChannel* media_channel) {
130   RTC_DCHECK(media_channel == nullptr ||
131              media_channel->media_type() == media_type());
132   media_channel_ = media_channel;
133 }
134 
GetParametersInternal() const135 RtpParameters RtpSenderBase::GetParametersInternal() const {
136   if (stopped_) {
137     return RtpParameters();
138   }
139   if (!media_channel_ || !ssrc_) {
140     return init_parameters_;
141   }
142   return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
143     RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
144     RemoveEncodingLayers(disabled_rids_, &result.encodings);
145     return result;
146   });
147 }
148 
GetParameters() const149 RtpParameters RtpSenderBase::GetParameters() const {
150   RtpParameters result = GetParametersInternal();
151   last_transaction_id_ = rtc::CreateRandomUuid();
152   result.transaction_id = last_transaction_id_.value();
153   return result;
154 }
155 
SetParametersInternal(const RtpParameters & parameters)156 RTCError RtpSenderBase::SetParametersInternal(const RtpParameters& parameters) {
157   RTC_DCHECK(!stopped_);
158 
159   if (UnimplementedRtpParameterHasValue(parameters)) {
160     LOG_AND_RETURN_ERROR(
161         RTCErrorType::UNSUPPORTED_PARAMETER,
162         "Attempted to set an unimplemented parameter of RtpParameters.");
163   }
164   if (!media_channel_ || !ssrc_) {
165     auto result = cricket::CheckRtpParametersInvalidModificationAndValues(
166         init_parameters_, parameters);
167     if (result.ok()) {
168       init_parameters_ = parameters;
169     }
170     return result;
171   }
172   return worker_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
173     RtpParameters rtp_parameters = parameters;
174     if (!disabled_rids_.empty()) {
175       // Need to add the inactive layers.
176       RtpParameters old_parameters =
177           media_channel_->GetRtpSendParameters(ssrc_);
178       rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_,
179                                              old_parameters.encodings);
180     }
181     return media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters);
182   });
183 }
184 
SetParameters(const RtpParameters & parameters)185 RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) {
186   TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters");
187   if (stopped_) {
188     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
189                          "Cannot set parameters on a stopped sender.");
190   }
191   if (!last_transaction_id_) {
192     LOG_AND_RETURN_ERROR(
193         RTCErrorType::INVALID_STATE,
194         "Failed to set parameters since getParameters() has never been called"
195         " on this sender");
196   }
197   if (last_transaction_id_ != parameters.transaction_id) {
198     LOG_AND_RETURN_ERROR(
199         RTCErrorType::INVALID_MODIFICATION,
200         "Failed to set parameters since the transaction_id doesn't match"
201         " the last value returned from getParameters()");
202   }
203 
204   RTCError result = SetParametersInternal(parameters);
205   last_transaction_id_.reset();
206   return result;
207 }
208 
SetStreams(const std::vector<std::string> & stream_ids)209 void RtpSenderBase::SetStreams(const std::vector<std::string>& stream_ids) {
210   set_stream_ids(stream_ids);
211   if (set_streams_observer_)
212     set_streams_observer_->OnSetStreams();
213 }
214 
SetTrack(MediaStreamTrackInterface * track)215 bool RtpSenderBase::SetTrack(MediaStreamTrackInterface* track) {
216   TRACE_EVENT0("webrtc", "RtpSenderBase::SetTrack");
217   if (stopped_) {
218     RTC_LOG(LS_ERROR) << "SetTrack can't be called on a stopped RtpSender.";
219     return false;
220   }
221   if (track && track->kind() != track_kind()) {
222     RTC_LOG(LS_ERROR) << "SetTrack with " << track->kind()
223                       << " called on RtpSender with " << track_kind()
224                       << " track.";
225     return false;
226   }
227 
228   // Detach from old track.
229   if (track_) {
230     DetachTrack();
231     track_->UnregisterObserver(this);
232     RemoveTrackFromStats();
233   }
234 
235   // Attach to new track.
236   bool prev_can_send_track = can_send_track();
237   // Keep a reference to the old track to keep it alive until we call SetSend.
238   rtc::scoped_refptr<MediaStreamTrackInterface> old_track = track_;
239   track_ = track;
240   if (track_) {
241     track_->RegisterObserver(this);
242     AttachTrack();
243   }
244 
245   // Update channel.
246   if (can_send_track()) {
247     SetSend();
248     AddTrackToStats();
249   } else if (prev_can_send_track) {
250     ClearSend();
251   }
252   attachment_id_ = (track_ ? GenerateUniqueId() : 0);
253   return true;
254 }
255 
SetSsrc(uint32_t ssrc)256 void RtpSenderBase::SetSsrc(uint32_t ssrc) {
257   TRACE_EVENT0("webrtc", "RtpSenderBase::SetSsrc");
258   if (stopped_ || ssrc == ssrc_) {
259     return;
260   }
261   // If we are already sending with a particular SSRC, stop sending.
262   if (can_send_track()) {
263     ClearSend();
264     RemoveTrackFromStats();
265   }
266   ssrc_ = ssrc;
267   if (can_send_track()) {
268     SetSend();
269     AddTrackToStats();
270   }
271   if (!init_parameters_.encodings.empty()) {
272     worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
273       RTC_DCHECK(media_channel_);
274       // Get the current parameters, which are constructed from the SDP.
275       // The number of layers in the SDP is currently authoritative to support
276       // SDP munging for Plan-B simulcast with "a=ssrc-group:SIM <ssrc-id>..."
277       // lines as described in RFC 5576.
278       // All fields should be default constructed and the SSRC field set, which
279       // we need to copy.
280       RtpParameters current_parameters =
281           media_channel_->GetRtpSendParameters(ssrc_);
282       RTC_DCHECK_GE(current_parameters.encodings.size(),
283                     init_parameters_.encodings.size());
284       for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
285         init_parameters_.encodings[i].ssrc =
286             current_parameters.encodings[i].ssrc;
287         init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid;
288         current_parameters.encodings[i] = init_parameters_.encodings[i];
289       }
290       current_parameters.degradation_preference =
291           init_parameters_.degradation_preference;
292       media_channel_->SetRtpSendParameters(ssrc_, current_parameters);
293       init_parameters_.encodings.clear();
294     });
295   }
296   // Attempt to attach the frame decryptor to the current media channel.
297   if (frame_encryptor_) {
298     SetFrameEncryptor(frame_encryptor_);
299   }
300   if (frame_transformer_) {
301     SetEncoderToPacketizerFrameTransformer(frame_transformer_);
302   }
303 }
304 
Stop()305 void RtpSenderBase::Stop() {
306   TRACE_EVENT0("webrtc", "RtpSenderBase::Stop");
307   // TODO(deadbeef): Need to do more here to fully stop sending packets.
308   if (stopped_) {
309     return;
310   }
311   if (track_) {
312     DetachTrack();
313     track_->UnregisterObserver(this);
314   }
315   if (can_send_track()) {
316     ClearSend();
317     RemoveTrackFromStats();
318   }
319   media_channel_ = nullptr;
320   set_streams_observer_ = nullptr;
321   stopped_ = true;
322 }
323 
DisableEncodingLayers(const std::vector<std::string> & rids)324 RTCError RtpSenderBase::DisableEncodingLayers(
325     const std::vector<std::string>& rids) {
326   if (stopped_) {
327     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
328                          "Cannot disable encodings on a stopped sender.");
329   }
330 
331   if (rids.empty()) {
332     return RTCError::OK();
333   }
334 
335   // Check that all the specified layers exist and disable them in the channel.
336   RtpParameters parameters = GetParametersInternal();
337   for (const std::string& rid : rids) {
338     if (absl::c_none_of(parameters.encodings,
339                         [&rid](const RtpEncodingParameters& encoding) {
340                           return encoding.rid == rid;
341                         })) {
342       LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
343                            "RID: " + rid + " does not refer to a valid layer.");
344     }
345   }
346 
347   if (!media_channel_ || !ssrc_) {
348     RemoveEncodingLayers(rids, &init_parameters_.encodings);
349     // Invalidate any transaction upon success.
350     last_transaction_id_.reset();
351     return RTCError::OK();
352   }
353 
354   for (RtpEncodingParameters& encoding : parameters.encodings) {
355     // Remain active if not in the disable list.
356     encoding.active &= absl::c_none_of(
357         rids,
358         [&encoding](const std::string& rid) { return encoding.rid == rid; });
359   }
360 
361   RTCError result = SetParametersInternal(parameters);
362   if (result.ok()) {
363     disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end());
364     // Invalidate any transaction upon success.
365     last_transaction_id_.reset();
366   }
367   return result;
368 }
369 
SetEncoderToPacketizerFrameTransformer(rtc::scoped_refptr<FrameTransformerInterface> frame_transformer)370 void RtpSenderBase::SetEncoderToPacketizerFrameTransformer(
371     rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) {
372   frame_transformer_ = std::move(frame_transformer);
373   if (media_channel_ && ssrc_ && !stopped_) {
374     worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
375       media_channel_->SetEncoderToPacketizerFrameTransformer(
376           ssrc_, frame_transformer_);
377     });
378   }
379 }
380 
LocalAudioSinkAdapter()381 LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {}
382 
~LocalAudioSinkAdapter()383 LocalAudioSinkAdapter::~LocalAudioSinkAdapter() {
384   MutexLock lock(&lock_);
385   if (sink_)
386     sink_->OnClose();
387 }
388 
OnData(const void * audio_data,int bits_per_sample,int sample_rate,size_t number_of_channels,size_t number_of_frames,absl::optional<int64_t> absolute_capture_timestamp_ms)389 void LocalAudioSinkAdapter::OnData(
390     const void* audio_data,
391     int bits_per_sample,
392     int sample_rate,
393     size_t number_of_channels,
394     size_t number_of_frames,
395     absl::optional<int64_t> absolute_capture_timestamp_ms) {
396   MutexLock lock(&lock_);
397   if (sink_) {
398     sink_->OnData(audio_data, bits_per_sample, sample_rate, number_of_channels,
399                   number_of_frames, absolute_capture_timestamp_ms);
400   }
401 }
402 
SetSink(cricket::AudioSource::Sink * sink)403 void LocalAudioSinkAdapter::SetSink(cricket::AudioSource::Sink* sink) {
404   MutexLock lock(&lock_);
405   RTC_DCHECK(!sink || !sink_);
406   sink_ = sink;
407 }
408 
Create(rtc::Thread * worker_thread,const std::string & id,StatsCollector * stats,SetStreamsObserver * set_streams_observer)409 rtc::scoped_refptr<AudioRtpSender> AudioRtpSender::Create(
410     rtc::Thread* worker_thread,
411     const std::string& id,
412     StatsCollector* stats,
413     SetStreamsObserver* set_streams_observer) {
414   return rtc::scoped_refptr<AudioRtpSender>(
415       new rtc::RefCountedObject<AudioRtpSender>(worker_thread, id, stats,
416                                                 set_streams_observer));
417 }
418 
AudioRtpSender(rtc::Thread * worker_thread,const std::string & id,StatsCollector * stats,SetStreamsObserver * set_streams_observer)419 AudioRtpSender::AudioRtpSender(rtc::Thread* worker_thread,
420                                const std::string& id,
421                                StatsCollector* stats,
422                                SetStreamsObserver* set_streams_observer)
423     : RtpSenderBase(worker_thread, id, set_streams_observer),
424       stats_(stats),
425       dtmf_sender_proxy_(DtmfSenderProxy::Create(
426           rtc::Thread::Current(),
427           DtmfSender::Create(rtc::Thread::Current(), this))),
428       sink_adapter_(new LocalAudioSinkAdapter()) {}
429 
~AudioRtpSender()430 AudioRtpSender::~AudioRtpSender() {
431   // For DtmfSender.
432   SignalDestroyed();
433   Stop();
434 }
435 
CanInsertDtmf()436 bool AudioRtpSender::CanInsertDtmf() {
437   if (!media_channel_) {
438     RTC_LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
439     return false;
440   }
441   // Check that this RTP sender is active (description has been applied that
442   // matches an SSRC to its ID).
443   if (!ssrc_) {
444     RTC_LOG(LS_ERROR) << "CanInsertDtmf: Sender does not have SSRC.";
445     return false;
446   }
447   return worker_thread_->Invoke<bool>(
448       RTC_FROM_HERE, [&] { return voice_media_channel()->CanInsertDtmf(); });
449 }
450 
InsertDtmf(int code,int duration)451 bool AudioRtpSender::InsertDtmf(int code, int duration) {
452   if (!media_channel_) {
453     RTC_LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
454     return false;
455   }
456   if (!ssrc_) {
457     RTC_LOG(LS_ERROR) << "InsertDtmf: Sender does not have SSRC.";
458     return false;
459   }
460   bool success = worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
461     return voice_media_channel()->InsertDtmf(ssrc_, code, duration);
462   });
463   if (!success) {
464     RTC_LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
465   }
466   return success;
467 }
468 
GetOnDestroyedSignal()469 sigslot::signal0<>* AudioRtpSender::GetOnDestroyedSignal() {
470   return &SignalDestroyed;
471 }
472 
OnChanged()473 void AudioRtpSender::OnChanged() {
474   TRACE_EVENT0("webrtc", "AudioRtpSender::OnChanged");
475   RTC_DCHECK(!stopped_);
476   if (cached_track_enabled_ != track_->enabled()) {
477     cached_track_enabled_ = track_->enabled();
478     if (can_send_track()) {
479       SetSend();
480     }
481   }
482 }
483 
DetachTrack()484 void AudioRtpSender::DetachTrack() {
485   RTC_DCHECK(track_);
486   audio_track()->RemoveSink(sink_adapter_.get());
487 }
488 
AttachTrack()489 void AudioRtpSender::AttachTrack() {
490   RTC_DCHECK(track_);
491   cached_track_enabled_ = track_->enabled();
492   audio_track()->AddSink(sink_adapter_.get());
493 }
494 
AddTrackToStats()495 void AudioRtpSender::AddTrackToStats() {
496   if (can_send_track() && stats_) {
497     stats_->AddLocalAudioTrack(audio_track().get(), ssrc_);
498   }
499 }
500 
RemoveTrackFromStats()501 void AudioRtpSender::RemoveTrackFromStats() {
502   if (can_send_track() && stats_) {
503     stats_->RemoveLocalAudioTrack(audio_track().get(), ssrc_);
504   }
505 }
506 
GetDtmfSender() const507 rtc::scoped_refptr<DtmfSenderInterface> AudioRtpSender::GetDtmfSender() const {
508   return dtmf_sender_proxy_;
509 }
510 
SetSend()511 void AudioRtpSender::SetSend() {
512   RTC_DCHECK(!stopped_);
513   RTC_DCHECK(can_send_track());
514   if (!media_channel_) {
515     RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
516     return;
517   }
518   cricket::AudioOptions options;
519 #if !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_WEBKIT_BUILD)
520   // TODO(tommi): Remove this hack when we move CreateAudioSource out of
521   // PeerConnection.  This is a bit of a strange way to apply local audio
522   // options since it is also applied to all streams/channels, local or remote.
523   if (track_->enabled() && audio_track()->GetSource() &&
524       !audio_track()->GetSource()->remote()) {
525     options = audio_track()->GetSource()->options();
526   }
527 #endif
528 
529   // |track_->enabled()| hops to the signaling thread, so call it before we hop
530   // to the worker thread or else it will deadlock.
531   bool track_enabled = track_->enabled();
532   bool success = worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
533     return voice_media_channel()->SetAudioSend(ssrc_, track_enabled, &options,
534                                                sink_adapter_.get());
535   });
536   if (!success) {
537     RTC_LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc_;
538   }
539 }
540 
ClearSend()541 void AudioRtpSender::ClearSend() {
542   RTC_DCHECK(ssrc_ != 0);
543   RTC_DCHECK(!stopped_);
544   if (!media_channel_) {
545     RTC_LOG(LS_WARNING) << "ClearAudioSend: No audio channel exists.";
546     return;
547   }
548   cricket::AudioOptions options;
549   bool success = worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
550     return voice_media_channel()->SetAudioSend(ssrc_, false, &options, nullptr);
551   });
552   if (!success) {
553     RTC_LOG(LS_WARNING) << "ClearAudioSend: ssrc is incorrect: " << ssrc_;
554   }
555 }
556 
Create(rtc::Thread * worker_thread,const std::string & id,SetStreamsObserver * set_streams_observer)557 rtc::scoped_refptr<VideoRtpSender> VideoRtpSender::Create(
558     rtc::Thread* worker_thread,
559     const std::string& id,
560     SetStreamsObserver* set_streams_observer) {
561   return rtc::scoped_refptr<VideoRtpSender>(
562       new rtc::RefCountedObject<VideoRtpSender>(worker_thread, id,
563                                                 set_streams_observer));
564 }
565 
VideoRtpSender(rtc::Thread * worker_thread,const std::string & id,SetStreamsObserver * set_streams_observer)566 VideoRtpSender::VideoRtpSender(rtc::Thread* worker_thread,
567                                const std::string& id,
568                                SetStreamsObserver* set_streams_observer)
569     : RtpSenderBase(worker_thread, id, set_streams_observer) {}
570 
~VideoRtpSender()571 VideoRtpSender::~VideoRtpSender() {
572   Stop();
573 }
574 
OnChanged()575 void VideoRtpSender::OnChanged() {
576   TRACE_EVENT0("webrtc", "VideoRtpSender::OnChanged");
577   RTC_DCHECK(!stopped_);
578   if (cached_track_content_hint_ != video_track()->content_hint()) {
579     cached_track_content_hint_ = video_track()->content_hint();
580     if (can_send_track()) {
581       SetSend();
582     }
583   }
584 }
585 
AttachTrack()586 void VideoRtpSender::AttachTrack() {
587   RTC_DCHECK(track_);
588   cached_track_content_hint_ = video_track()->content_hint();
589 }
590 
GetDtmfSender() const591 rtc::scoped_refptr<DtmfSenderInterface> VideoRtpSender::GetDtmfSender() const {
592   RTC_LOG(LS_ERROR) << "Tried to get DTMF sender from video sender.";
593   return nullptr;
594 }
595 
SetSend()596 void VideoRtpSender::SetSend() {
597   RTC_DCHECK(!stopped_);
598   RTC_DCHECK(can_send_track());
599   if (!media_channel_) {
600     RTC_LOG(LS_ERROR) << "SetVideoSend: No video channel exists.";
601     return;
602   }
603   cricket::VideoOptions options;
604   VideoTrackSourceInterface* source = video_track()->GetSource();
605   if (source) {
606     options.is_screencast = source->is_screencast();
607     options.video_noise_reduction = source->needs_denoising();
608   }
609   options.content_hint = cached_track_content_hint_;
610   switch (cached_track_content_hint_) {
611     case VideoTrackInterface::ContentHint::kNone:
612       break;
613     case VideoTrackInterface::ContentHint::kFluid:
614       options.is_screencast = false;
615       break;
616     case VideoTrackInterface::ContentHint::kDetailed:
617     case VideoTrackInterface::ContentHint::kText:
618       options.is_screencast = true;
619       break;
620   }
621   bool success = worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
622     return video_media_channel()->SetVideoSend(ssrc_, &options, video_track());
623   });
624   RTC_DCHECK(success);
625 }
626 
ClearSend()627 void VideoRtpSender::ClearSend() {
628   RTC_DCHECK(ssrc_ != 0);
629   RTC_DCHECK(!stopped_);
630   if (!media_channel_) {
631     RTC_LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
632     return;
633   }
634   // Allow SetVideoSend to fail since |enable| is false and |source| is null.
635   // This the normal case when the underlying media channel has already been
636   // deleted.
637   worker_thread_->Invoke<bool>(RTC_FROM_HERE, [&] {
638     return video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr);
639   });
640 }
641 
642 }  // namespace webrtc
643