| // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter.h" |
| |
| #include "base/strings/stringprintf.h" |
| #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" |
| #include "third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h" |
| #include "third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.h" |
| #include "third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.h" |
| #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" |
| #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" |
| |
| namespace { |
| |
| void SendLogMessage(const std::string& message) { |
| blink::WebRtcLogMessage("WRMSTA::" + message); |
| } |
| |
| } // namespace |
| |
| namespace blink { |
| |
| // static |
| scoped_refptr<WebRtcMediaStreamTrackAdapter> |
| WebRtcMediaStreamTrackAdapter::CreateLocalTrackAdapter( |
| blink::PeerConnectionDependencyFactory* factory, |
| const scoped_refptr<base::SingleThreadTaskRunner>& main_thread, |
| MediaStreamComponent* component) { |
| DCHECK(factory); |
| DCHECK(main_thread->BelongsToCurrentThread()); |
| DCHECK(component); |
| scoped_refptr<WebRtcMediaStreamTrackAdapter> local_track_adapter( |
| base::AdoptRef(new WebRtcMediaStreamTrackAdapter(factory, main_thread))); |
| if (component->Source()->GetType() == MediaStreamSource::kTypeAudio) { |
| local_track_adapter->InitializeLocalAudioTrack(component); |
| } else { |
| DCHECK_EQ(component->Source()->GetType(), MediaStreamSource::kTypeVideo); |
| local_track_adapter->InitializeLocalVideoTrack(component); |
| } |
| return local_track_adapter; |
| } |
| |
| // static |
| scoped_refptr<WebRtcMediaStreamTrackAdapter> |
| WebRtcMediaStreamTrackAdapter::CreateRemoteTrackAdapter( |
| blink::PeerConnectionDependencyFactory* factory, |
| const scoped_refptr<base::SingleThreadTaskRunner>& main_thread, |
| const scoped_refptr<webrtc::MediaStreamTrackInterface>& webrtc_track) { |
| DCHECK(factory); |
| DCHECK(!main_thread->BelongsToCurrentThread()); |
| DCHECK(webrtc_track); |
| scoped_refptr<WebRtcMediaStreamTrackAdapter> remote_track_adapter( |
| base::AdoptRef(new WebRtcMediaStreamTrackAdapter(factory, main_thread))); |
| if (webrtc_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { |
| remote_track_adapter->InitializeRemoteAudioTrack(base::WrapRefCounted( |
| static_cast<webrtc::AudioTrackInterface*>(webrtc_track.get()))); |
| } else { |
| DCHECK_EQ(webrtc_track->kind(), |
| webrtc::MediaStreamTrackInterface::kVideoKind); |
| remote_track_adapter->InitializeRemoteVideoTrack(base::WrapRefCounted( |
| static_cast<webrtc::VideoTrackInterface*>(webrtc_track.get()))); |
| } |
| return remote_track_adapter; |
| } |
| |
| WebRtcMediaStreamTrackAdapter::WebRtcMediaStreamTrackAdapter( |
| blink::PeerConnectionDependencyFactory* factory, |
| const scoped_refptr<base::SingleThreadTaskRunner>& main_thread) |
| : factory_(factory), |
| main_thread_(main_thread), |
| remote_track_can_complete_initialization_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| is_initialized_(false), |
| is_disposed_(false) { |
| DCHECK(factory_); |
| DCHECK(main_thread_); |
| } |
| |
| WebRtcMediaStreamTrackAdapter::~WebRtcMediaStreamTrackAdapter() { |
| DCHECK(!remote_track_can_complete_initialization_.IsSignaled()); |
| DCHECK(is_disposed_); |
| // Ensured by destructor traits. |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| } |
| |
| // static |
| void WebRtcMediaStreamTrackAdapterTraits::Destruct( |
| const WebRtcMediaStreamTrackAdapter* adapter) { |
| if (!adapter->main_thread_->BelongsToCurrentThread()) { |
| PostCrossThreadTask( |
| *adapter->main_thread_.get(), FROM_HERE, |
| CrossThreadBindOnce(&WebRtcMediaStreamTrackAdapterTraits::Destruct, |
| CrossThreadUnretained(adapter))); |
| return; |
| } |
| delete adapter; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::Dispose() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(is_initialized_); |
| if (is_disposed_) |
| return; |
| remote_track_can_complete_initialization_.Reset(); |
| is_disposed_ = true; |
| if (component_->Source()->GetType() == MediaStreamSource::kTypeAudio) { |
| if (local_track_audio_sink_) |
| DisposeLocalAudioTrack(); |
| else |
| DisposeRemoteAudioTrack(); |
| } else { |
| DCHECK_EQ(component_->Source()->GetType(), MediaStreamSource::kTypeVideo); |
| if (local_track_video_sink_) |
| DisposeLocalVideoTrack(); |
| else |
| DisposeRemoteVideoTrack(); |
| } |
| } |
| |
| bool WebRtcMediaStreamTrackAdapter::is_initialized() const { |
| return is_initialized_; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::InitializeOnMainThread() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| if (is_initialized_) |
| return; |
| // TODO(hbos): Only ever initialize explicitly, |
| // remove EnsureTrackIsInitialized(). https://crbug.com/857458 |
| EnsureTrackIsInitialized(); |
| } |
| |
| MediaStreamComponent* WebRtcMediaStreamTrackAdapter::track() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| EnsureTrackIsInitialized(); |
| DCHECK(component_); |
| return component_.Get(); |
| } |
| |
| webrtc::MediaStreamTrackInterface* |
| WebRtcMediaStreamTrackAdapter::webrtc_track() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(webrtc_track_); |
| EnsureTrackIsInitialized(); |
| return webrtc_track_.get(); |
| } |
| |
| bool WebRtcMediaStreamTrackAdapter::IsEqual(MediaStreamComponent* component) { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| EnsureTrackIsInitialized(); |
| return component_->GetPlatformTrack() == component->GetPlatformTrack(); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::InitializeLocalAudioTrack( |
| MediaStreamComponent* component) { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(!is_initialized_); |
| DCHECK(component); |
| DCHECK_EQ(component->Source()->GetType(), MediaStreamSource::kTypeAudio); |
| SendLogMessage(base::StringPrintf("InitializeLocalAudioTrack({id=%s})", |
| component->Id().Utf8().c_str())); |
| component_ = component; |
| auto* native_track = MediaStreamAudioTrack::From(component_); |
| DCHECK(native_track); |
| |
| // Non-WebRtc remote sources and local sources do not provide an instance of |
| // the webrtc::AudioSourceInterface, and also do not need references to the |
| // audio level calculator or audio processor passed to the sink. |
| webrtc::AudioSourceInterface* source_interface = nullptr; |
| local_track_audio_sink_ = std::make_unique<blink::WebRtcAudioSink>( |
| component_->Id().Utf8(), source_interface, |
| factory_->GetWebRtcSignalingTaskRunner(), main_thread_); |
| |
| if (auto* media_stream_source = blink::ProcessedLocalAudioSource::From( |
| blink::MediaStreamAudioSource::From(component_->Source()))) { |
| local_track_audio_sink_->SetLevel(media_stream_source->audio_level()); |
| // The sink only grabs stats from the audio processor. Stats are only |
| // available if audio processing is turned on. Therefore, only provide the |
| // sink a reference to the processor if audio processing is turned on. |
| if (media_stream_source->HasAudioProcessing()) { |
| local_track_audio_sink_->SetAudioProcessor( |
| media_stream_source->GetAudioProcessor()); |
| } |
| } |
| native_track->AddSink(local_track_audio_sink_.get()); |
| webrtc_track_ = local_track_audio_sink_->webrtc_audio_track(); |
| DCHECK(webrtc_track_); |
| is_initialized_ = true; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::InitializeLocalVideoTrack( |
| MediaStreamComponent* component) { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(!is_initialized_); |
| DCHECK(component); |
| DCHECK_EQ(component->Source()->GetType(), MediaStreamSource::kTypeVideo); |
| component_ = component; |
| local_track_video_sink_ = std::make_unique<blink::MediaStreamVideoWebRtcSink>( |
| component_, factory_, main_thread_); |
| webrtc_track_ = local_track_video_sink_->webrtc_video_track(); |
| DCHECK(webrtc_track_); |
| is_initialized_ = true; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::InitializeRemoteAudioTrack( |
| const scoped_refptr<webrtc::AudioTrackInterface>& webrtc_audio_track) { |
| DCHECK(!main_thread_->BelongsToCurrentThread()); |
| DCHECK(!is_initialized_); |
| DCHECK(!remote_track_can_complete_initialization_.IsSignaled()); |
| DCHECK(webrtc_audio_track); |
| DCHECK_EQ(webrtc_audio_track->kind(), |
| webrtc::MediaStreamTrackInterface::kAudioKind); |
| SendLogMessage( |
| base::StringPrintf("InitializeRemoteAudioTrack([this=%p])", this)); |
| remote_audio_track_adapter_ = |
| base::MakeRefCounted<blink::RemoteAudioTrackAdapter>( |
| main_thread_, webrtc_audio_track.get()); |
| webrtc_track_ = webrtc_audio_track; |
| // Set the initial volume to zero. When the track is put in an audio tag for |
| // playout, its volume is set to that of the tag. Without this, we could end |
| // up playing out audio that's not attached to any tag, see: |
| // http://crbug.com/810848 |
| webrtc_audio_track->GetSource()->SetVolume(0); |
| remote_track_can_complete_initialization_.Signal(); |
| PostCrossThreadTask( |
| *main_thread_.get(), FROM_HERE, |
| CrossThreadBindOnce(&WebRtcMediaStreamTrackAdapter:: |
| FinalizeRemoteTrackInitializationOnMainThread, |
| WrapRefCounted(this))); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::InitializeRemoteVideoTrack( |
| const scoped_refptr<webrtc::VideoTrackInterface>& webrtc_video_track) { |
| DCHECK(!main_thread_->BelongsToCurrentThread()); |
| DCHECK(webrtc_video_track); |
| DCHECK_EQ(webrtc_video_track->kind(), |
| webrtc::MediaStreamTrackInterface::kVideoKind); |
| remote_video_track_adapter_ = |
| base::MakeRefCounted<blink::RemoteVideoTrackAdapter>( |
| main_thread_, webrtc_video_track.get()); |
| webrtc_track_ = webrtc_video_track; |
| remote_track_can_complete_initialization_.Signal(); |
| PostCrossThreadTask( |
| *main_thread_.get(), FROM_HERE, |
| CrossThreadBindOnce(&WebRtcMediaStreamTrackAdapter:: |
| FinalizeRemoteTrackInitializationOnMainThread, |
| WrapRefCounted(this))); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter:: |
| FinalizeRemoteTrackInitializationOnMainThread() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(remote_audio_track_adapter_ || remote_video_track_adapter_); |
| if (is_initialized_) |
| return; |
| |
| if (remote_audio_track_adapter_) { |
| remote_audio_track_adapter_->Initialize(); |
| component_ = remote_audio_track_adapter_->track(); |
| } else { |
| remote_video_track_adapter_->Initialize(); |
| component_ = remote_video_track_adapter_->track(); |
| } |
| is_initialized_ = true; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::EnsureTrackIsInitialized() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| if (is_initialized_) |
| return; |
| |
| // Remote tracks may not be fully initialized yet, since they are partly |
| // initialized on the signaling thread. |
| remote_track_can_complete_initialization_.Wait(); |
| FinalizeRemoteTrackInitializationOnMainThread(); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::DisposeLocalAudioTrack() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(local_track_audio_sink_); |
| DCHECK_EQ(component_->Source()->GetType(), MediaStreamSource::kTypeAudio); |
| auto* audio_track = MediaStreamAudioTrack::From(component_); |
| DCHECK(audio_track); |
| audio_track->RemoveSink(local_track_audio_sink_.get()); |
| local_track_audio_sink_.reset(); |
| webrtc_track_ = nullptr; |
| component_ = nullptr; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::DisposeLocalVideoTrack() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(local_track_video_sink_); |
| DCHECK_EQ(component_->Source()->GetType(), MediaStreamSource::kTypeVideo); |
| local_track_video_sink_.reset(); |
| webrtc_track_ = nullptr; |
| component_ = nullptr; |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::DisposeRemoteAudioTrack() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(remote_audio_track_adapter_); |
| DCHECK_EQ(component_->Source()->GetType(), MediaStreamSource::kTypeAudio); |
| PostCrossThreadTask( |
| *factory_->GetWebRtcSignalingTaskRunner().get(), FROM_HERE, |
| CrossThreadBindOnce( |
| &WebRtcMediaStreamTrackAdapter:: |
| UnregisterRemoteAudioTrackAdapterOnSignalingThread, |
| WrapRefCounted(this))); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::DisposeRemoteVideoTrack() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(remote_video_track_adapter_); |
| DCHECK_EQ(component_->Source()->GetType(), MediaStreamSource::kTypeVideo); |
| FinalizeRemoteTrackDisposingOnMainThread(); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter:: |
| UnregisterRemoteAudioTrackAdapterOnSignalingThread() { |
| DCHECK(!main_thread_->BelongsToCurrentThread()); |
| DCHECK(remote_audio_track_adapter_); |
| remote_audio_track_adapter_->Unregister(); |
| PostCrossThreadTask( |
| *main_thread_.get(), FROM_HERE, |
| CrossThreadBindOnce(&WebRtcMediaStreamTrackAdapter:: |
| FinalizeRemoteTrackDisposingOnMainThread, |
| WrapRefCounted(this))); |
| } |
| |
| void WebRtcMediaStreamTrackAdapter::FinalizeRemoteTrackDisposingOnMainThread() { |
| DCHECK(main_thread_->BelongsToCurrentThread()); |
| DCHECK(is_disposed_); |
| remote_audio_track_adapter_ = nullptr; |
| remote_video_track_adapter_ = nullptr; |
| webrtc_track_ = nullptr; |
| component_ = nullptr; |
| } |
| |
| } // namespace blink |