webrtc_m130/pc/video_track.cc
Tommi 5ffefe9d2d Fix race between enabled() and set_enabled() in VideoTrack.
Along the way I introduced VideoSourceBaseGuarded, which is equivalent
to VideoSourceBase except that it applies thread checks. I found that
it's easy to use VideoSourceBase incorrectly and in fact there appear
to be tests that do this.

I made the source object const in VideoTrack, as it already was in
AudioTrack, and that allowed for making the GetSource() accessors
bypass the proxy thread hop and give the caller direct access.

Bug: webrtc:12773, b/188139639, webrtc:12780
Change-Id: I022175c4239a1306ef54059c131d81411d5124fe
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219160
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Andrey Logvin <landrey@webrtc.org>
Commit-Queue: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34096}
2021-05-24 09:13:56 +00:00

125 lines
4.0 KiB
C++

/*
* Copyright 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "pc/video_track.h"
#include <string>
#include <utility>
#include <vector>
#include "api/notifier.h"
#include "api/sequence_checker.h"
#include "rtc_base/checks.h"
#include "rtc_base/location.h"
#include "rtc_base/ref_counted_object.h"
namespace webrtc {
VideoTrack::VideoTrack(const std::string& label,
VideoTrackSourceInterface* video_source,
rtc::Thread* worker_thread)
: MediaStreamTrack<VideoTrackInterface>(label),
worker_thread_(worker_thread),
video_source_(video_source),
content_hint_(ContentHint::kNone) {
RTC_DCHECK_RUN_ON(&signaling_thread_);
// Detach the thread checker for VideoSourceBaseGuarded since we'll make calls
// to VideoSourceBaseGuarded on the worker thread, but we're currently on the
// signaling thread.
source_sequence_.Detach();
video_source_->RegisterObserver(this);
}
VideoTrack::~VideoTrack() {
RTC_DCHECK_RUN_ON(&signaling_thread_);
video_source_->UnregisterObserver(this);
}
std::string VideoTrack::kind() const {
return kVideoKind;
}
// AddOrUpdateSink and RemoveSink should be called on the worker
// thread.
void VideoTrack::AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) {
RTC_DCHECK_RUN_ON(worker_thread_);
VideoSourceBaseGuarded::AddOrUpdateSink(sink, wants);
rtc::VideoSinkWants modified_wants = wants;
modified_wants.black_frames = !enabled();
video_source_->AddOrUpdateSink(sink, modified_wants);
}
void VideoTrack::RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) {
RTC_DCHECK_RUN_ON(worker_thread_);
VideoSourceBaseGuarded::RemoveSink(sink);
video_source_->RemoveSink(sink);
}
VideoTrackSourceInterface* VideoTrack::GetSource() const {
RTC_DCHECK_RUN_ON(&signaling_thread_);
return video_source_.get();
}
VideoTrackInterface::ContentHint VideoTrack::content_hint() const {
RTC_DCHECK_RUN_ON(worker_thread_);
return content_hint_;
}
void VideoTrack::set_content_hint(ContentHint hint) {
RTC_DCHECK_RUN_ON(worker_thread_);
if (content_hint_ == hint)
return;
content_hint_ = hint;
Notifier<VideoTrackInterface>::FireOnChanged();
}
bool VideoTrack::set_enabled(bool enable) {
RTC_DCHECK_RUN_ON(worker_thread_);
for (auto& sink_pair : sink_pairs()) {
rtc::VideoSinkWants modified_wants = sink_pair.wants;
modified_wants.black_frames = !enable;
video_source_->AddOrUpdateSink(sink_pair.sink, modified_wants);
}
return MediaStreamTrack<VideoTrackInterface>::set_enabled(enable);
}
bool VideoTrack::enabled() const {
RTC_DCHECK_RUN_ON(worker_thread_);
return MediaStreamTrack<VideoTrackInterface>::enabled();
}
MediaStreamTrackInterface::TrackState VideoTrack::state() const {
RTC_DCHECK_RUN_ON(worker_thread_);
return MediaStreamTrack<VideoTrackInterface>::state();
}
void VideoTrack::OnChanged() {
RTC_DCHECK_RUN_ON(&signaling_thread_);
worker_thread_->Invoke<void>(
RTC_FROM_HERE, [this, state = video_source_->state()]() {
// TODO(tommi): Calling set_state() this way isn't ideal since we're
// currently blocking the signaling thread and set_state() may
// internally fire notifications via `FireOnChanged()` which may further
// amplify the blocking effect on the signaling thread.
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
set_state(state == MediaSourceInterface::kEnded ? kEnded : kLive);
});
}
rtc::scoped_refptr<VideoTrack> VideoTrack::Create(
const std::string& id,
VideoTrackSourceInterface* source,
rtc::Thread* worker_thread) {
return rtc::make_ref_counted<VideoTrack>(id, source, worker_thread);
}
} // namespace webrtc