| // Copyright 2018 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 "content/browser/media/in_process_audio_loopback_stream_creator.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/task/post_task.h" |
| #include "content/browser/browser_main_loop.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/common/media/renderer_audio_input_stream_factory.mojom.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "media/audio/audio_device_description.h" |
| #include "media/base/user_input_monitor.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // A mojom::RendererAudioInputStreamFactoryClient that holds a |
| // AudioLoopbackStreamCreator::StreamCreatedCallback. The callback runs when the |
| // requested audio stream is created. |
| class StreamCreatedCallbackAdapter final |
| : public mojom::RendererAudioInputStreamFactoryClient { |
| public: |
| explicit StreamCreatedCallbackAdapter( |
| const AudioLoopbackStreamCreator::StreamCreatedCallback& callback) |
| : callback_(callback) { |
| DCHECK(callback_); |
| } |
| |
| ~StreamCreatedCallbackAdapter() override {} |
| |
| // mojom::RendererAudioInputStreamFactoryClient implementation. |
| void StreamCreated( |
| media::mojom::AudioInputStreamPtr stream, |
| media::mojom::AudioInputStreamClientRequest client_request, |
| media::mojom::ReadOnlyAudioDataPipePtr data_pipe, |
| bool initially_muted, |
| const base::Optional<base::UnguessableToken>& stream_id) override { |
| DCHECK(!initially_muted); // Loopback streams shouldn't be started muted. |
| callback_.Run(std::move(stream), std::move(client_request), |
| std::move(data_pipe)); |
| } |
| |
| private: |
| const AudioLoopbackStreamCreator::StreamCreatedCallback callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StreamCreatedCallbackAdapter); |
| }; |
| |
| void CreateLoopbackStreamHelper( |
| ForwardingAudioStreamFactory::Core* factory, |
| AudioStreamBroker::LoopbackSource* loopback_source, |
| const media::AudioParameters& params, |
| uint32_t total_segments, |
| mojom::RendererAudioInputStreamFactoryClientPtrInfo client_ptr_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| const bool mute_source = true; |
| mojom::RendererAudioInputStreamFactoryClientPtr client; |
| client.Bind(std::move(client_ptr_info)); |
| |
| factory->CreateLoopbackStream(-1, -1, loopback_source, params, total_segments, |
| mute_source, std::move(client)); |
| } |
| |
| void CreateSystemWideLoopbackStreamHelper( |
| ForwardingAudioStreamFactory::Core* factory, |
| const media::AudioParameters& params, |
| uint32_t total_segments, |
| mojom::RendererAudioInputStreamFactoryClientPtrInfo client_ptr_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| const bool enable_agc = false; |
| mojom::RendererAudioInputStreamFactoryClientPtr client; |
| client.Bind(std::move(client_ptr_info)); |
| |
| factory->CreateInputStream( |
| -1, -1, media::AudioDeviceDescription::kLoopbackWithMuteDeviceId, params, |
| total_segments, enable_agc, nullptr /* processing_config */, |
| std::move(client)); |
| } |
| |
| } // namespace |
| |
| InProcessAudioLoopbackStreamCreator::InProcessAudioLoopbackStreamCreator() |
| : factory_(nullptr, |
| BrowserMainLoop::GetInstance() |
| ? static_cast<media::UserInputMonitorBase*>( |
| BrowserMainLoop::GetInstance()->user_input_monitor()) |
| : nullptr, |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->Clone(), |
| AudioStreamBrokerFactory::CreateImpl()) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| InProcessAudioLoopbackStreamCreator::~InProcessAudioLoopbackStreamCreator() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| void InProcessAudioLoopbackStreamCreator::CreateLoopbackStream( |
| WebContents* loopback_source, |
| const media::AudioParameters& params, |
| uint32_t total_segments, |
| const StreamCreatedCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| mojom::RendererAudioInputStreamFactoryClientPtrInfo client; |
| mojo::MakeStrongBinding( |
| std::make_unique<StreamCreatedCallbackAdapter>(callback), |
| mojo::MakeRequest(&client)); |
| // Deletion of factory_.core() is posted to the IO thread when |factory_| is |
| // destroyed, so Unretained is safe below. |
| if (loopback_source) { |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&CreateLoopbackStreamHelper, factory_.core(), |
| static_cast<WebContentsImpl*>(loopback_source) |
| ->GetAudioStreamFactory() |
| ->core(), |
| params, total_segments, std::move(client))); |
| return; |
| } |
| // A null |frame_of_source_web_contents| requests system-wide loopback. |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&CreateSystemWideLoopbackStreamHelper, factory_.core(), |
| params, total_segments, std::move(client))); |
| } |
| |
| } // namespace content |