blob: 52a61a7aac04e7b5d9f90ccd37d423e004bc21b7 [file] [log] [blame]
// 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/renderer_host/media/render_frame_audio_input_stream_factory.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "content/browser/media/forwarding_audio_stream_factory.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/media_stream_manager.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/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_media_capture_id.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_service_manager_context.h"
#include "content/public/test/test_utils.h"
#include "media/audio/audio_system_impl.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/fake_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "media/base/audio_parameters.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/audio/public/cpp/fake_stream_factory.h"
#include "services/audio/public/mojom/constants.mojom.h"
#include "services/audio/public/mojom/stream_factory.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
class RenderFrameAudioInputStreamFactoryTest
: public RenderViewHostTestHarness {
public:
RenderFrameAudioInputStreamFactoryTest()
: RenderViewHostTestHarness(),
test_service_manager_context_(
std::make_unique<TestServiceManagerContext>()),
audio_manager_(std::make_unique<media::TestAudioThread>(),
&log_factory_),
audio_system_(media::AudioSystemImpl::CreateInstance()),
media_stream_manager_(std::make_unique<MediaStreamManager>(
audio_system_.get(),
base::CreateSingleThreadTaskRunnerWithTraits(
{BrowserThread::UI}))) {}
~RenderFrameAudioInputStreamFactoryTest() override {}
void SetUp() override {
RenderViewHostTestHarness::SetUp();
RenderFrameHostTester::For(main_rfh())->InitializeRenderFrameIfNeeded();
// Set up the ForwardingAudioStreamFactory.
service_manager::Connector* connector =
ForwardingAudioStreamFactory::ForFrame(main_rfh())
->core()
->get_connector_for_testing();
connector->OverrideBinderForTesting(
service_manager::ServiceFilter::ByName(audio::mojom::kServiceName),
audio::mojom::StreamFactory::Name_,
base::BindRepeating(
&RenderFrameAudioInputStreamFactoryTest::BindFactory,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
}
void TearDown() override {
audio_manager_.Shutdown();
test_service_manager_context_.reset();
RenderViewHostTestHarness::TearDown();
}
void BindFactory(mojo::ScopedMessagePipeHandle factory_request) {
audio_service_stream_factory_.binding_.Bind(
audio::mojom::StreamFactoryRequest(std::move(factory_request)));
}
class MockStreamFactory : public audio::FakeStreamFactory {
public:
MockStreamFactory() : binding_(this) {}
~MockStreamFactory() override {}
void CreateInputStream(
media::mojom::AudioInputStreamRequest stream_request,
media::mojom::AudioInputStreamClientPtr client,
media::mojom::AudioInputStreamObserverPtr observer,
media::mojom::AudioLogPtr log,
const std::string& device_id,
const media::AudioParameters& params,
uint32_t shared_memory_count,
bool enable_agc,
mojo::ScopedSharedBufferHandle key_press_count_buffer,
audio::mojom::AudioProcessingConfigPtr processing_config,
CreateInputStreamCallback created_callback) override {
last_created_callback = std::move(created_callback);
}
void CreateLoopbackStream(
media::mojom::AudioInputStreamRequest stream_request,
media::mojom::AudioInputStreamClientPtr client,
media::mojom::AudioInputStreamObserverPtr observer,
const media::AudioParameters& params,
uint32_t shared_memory_count,
const base::UnguessableToken& group_id,
CreateLoopbackStreamCallback created_callback) override {
last_created_loopback_callback = std::move(created_callback);
}
CreateInputStreamCallback last_created_callback;
CreateLoopbackStreamCallback last_created_loopback_callback;
mojo::Binding<audio::mojom::StreamFactory> binding_;
};
class FakeRendererAudioInputStreamFactoryClient
: public mojom::RendererAudioInputStreamFactoryClient {
public:
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 {}
};
AudioInputDeviceManager* audio_input_device_manager() {
return media_stream_manager_->audio_input_device_manager();
}
class StreamOpenedWaiter : public MediaStreamProviderListener {
public:
explicit StreamOpenedWaiter(scoped_refptr<AudioInputDeviceManager> aidm,
base::OnceClosure callback)
: aidm_(aidm), cb_(std::move(callback)) {
aidm->RegisterListener(this);
}
~StreamOpenedWaiter() override { aidm_->UnregisterListener(this); }
void Opened(MediaStreamType stream_type, int capture_session_id) override {
std::move(cb_).Run();
}
void Closed(MediaStreamType stream_type, int capture_session_id) override {}
void Aborted(MediaStreamType stream_type, int capture_session_id) override {
}
private:
scoped_refptr<AudioInputDeviceManager> aidm_;
base::OnceClosure cb_;
};
void CallOpenWithTestDeviceAndStoreSessionIdOnIO(int* session_id) {
*session_id = audio_input_device_manager()->Open(
MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, kDeviceId, kDeviceName));
}
const media::AudioParameters kParams =
media::AudioParameters::UnavailableDeviceParams();
const std::string kDeviceId = "test id";
const std::string kDeviceName = "test name";
const bool kAGC = false;
const uint32_t kSharedMemoryCount = 123;
MockStreamFactory audio_service_stream_factory_;
std::unique_ptr<TestServiceManagerContext> test_service_manager_context_;
media::FakeAudioLogFactory log_factory_;
media::FakeAudioManager audio_manager_;
std::unique_ptr<media::AudioSystem> audio_system_;
std::unique_ptr<MediaStreamManager> media_stream_manager_;
};
TEST_F(RenderFrameAudioInputStreamFactoryTest, ConstructDestruct) {
mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
RenderFrameAudioInputStreamFactory factory(
mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
}
TEST_F(RenderFrameAudioInputStreamFactoryTest,
CreateOpenedStream_ForwardsCall) {
mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
RenderFrameAudioInputStreamFactory factory(
mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
int session_id = audio_input_device_manager()->Open(
MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, kDeviceId, kDeviceName));
base::RunLoop().RunUntilIdle();
mojom::RendererAudioInputStreamFactoryClientPtr client;
mojo::MakeRequest(&client);
factory_ptr->CreateStream(std::move(client), session_id, kParams, kAGC,
kSharedMemoryCount, nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(!!audio_service_stream_factory_.last_created_callback);
}
TEST_F(RenderFrameAudioInputStreamFactoryTest,
CreateWebContentsCapture_ForwardsCall) {
std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
RenderFrameAudioInputStreamFactory factory(
mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
RenderFrameHost* main_frame = source_contents->GetMainFrame();
WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
main_frame->GetRoutingID());
int session_id = audio_input_device_manager()->Open(MediaStreamDevice(
MEDIA_GUM_TAB_AUDIO_CAPTURE, capture_id.ToString(), kDeviceName));
base::RunLoop().RunUntilIdle();
mojom::RendererAudioInputStreamFactoryClientPtr client;
mojo::MakeRequest(&client);
factory_ptr->CreateStream(std::move(client), session_id, kParams, kAGC,
kSharedMemoryCount, nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(!!audio_service_stream_factory_.last_created_loopback_callback);
}
TEST_F(RenderFrameAudioInputStreamFactoryTest,
CreateWebContentsCaptureAfterCaptureSourceDestructed_Fails) {
std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
RenderFrameAudioInputStreamFactory factory(
mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
RenderFrameHost* main_frame = source_contents->GetMainFrame();
WebContentsMediaCaptureId capture_id(main_frame->GetProcess()->GetID(),
main_frame->GetRoutingID());
int session_id = audio_input_device_manager()->Open(MediaStreamDevice(
MEDIA_GUM_TAB_AUDIO_CAPTURE, capture_id.ToString(), kDeviceName));
base::RunLoop().RunUntilIdle();
source_contents.reset();
mojom::RendererAudioInputStreamFactoryClientPtr client;
mojo::MakeRequest(&client);
factory_ptr->CreateStream(std::move(client), session_id, kParams, kAGC,
kSharedMemoryCount, nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(!!audio_service_stream_factory_.last_created_loopback_callback);
}
TEST_F(RenderFrameAudioInputStreamFactoryTest,
CreateStreamWithoutValidSessionId_Fails) {
mojom::RendererAudioInputStreamFactoryPtr factory_ptr;
RenderFrameAudioInputStreamFactory factory(
mojo::MakeRequest(&factory_ptr), media_stream_manager_.get(), main_rfh());
int session_id = 123;
mojom::RendererAudioInputStreamFactoryClientPtr client;
mojo::MakeRequest(&client);
factory_ptr->CreateStream(std::move(client), session_id, kParams, kAGC,
kSharedMemoryCount, nullptr);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(!!audio_service_stream_factory_.last_created_callback);
}
} // namespace content