blob: 710782a46bcd0c19aa9626594f9d0a8039d30136 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/video_capture_service.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "media/base/media_switches.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/video_capture/public/cpp/mock_producer.h"
#include "services/video_capture/public/mojom/device_factory.mojom.h"
#include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
#include "services/video_capture/public/mojom/producer.mojom.h"
#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
#include "services/video_capture/public/mojom/virtual_device.mojom.h"
namespace content {
namespace {
enum class ServiceApi { kSingleClient, kMultiClient };
enum class VirtualDeviceType { kSharedMemory, kTexture };
struct TestParams {
ServiceApi api_to_use;
VirtualDeviceType device_type;
};
static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
static const char kEnumerateVideoCaptureDevicesAndVerify[] =
"enumerateVideoCaptureDevicesAndVerifyCount(%d)";
static const char kRegisterForDeviceChangeEvent[] =
"registerForDeviceChangeEvent()";
static const char kWaitForDeviceChangeEvent[] = "waitForDeviceChangeEvent()";
static const char kResetHasReceivedChangedEventFlag[] =
"resetHasReceivedChangedEventFlag()";
} // anonymous namespace
// Integration test that obtains a connection to the video capture service via
// the Browser process' service manager. It then registers and unregisters
// virtual devices at the service and checks in JavaScript that the list of
// enumerated devices changes correspondingly.
class WebRtcVideoCaptureServiceEnumerationBrowserTest
: public ContentBrowserTest,
public testing::WithParamInterface<TestParams>,
public video_capture::mojom::DevicesChangedObserver {
public:
WebRtcVideoCaptureServiceEnumerationBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(features::kMojoVideoCapture);
}
WebRtcVideoCaptureServiceEnumerationBrowserTest(
const WebRtcVideoCaptureServiceEnumerationBrowserTest&) = delete;
WebRtcVideoCaptureServiceEnumerationBrowserTest& operator=(
const WebRtcVideoCaptureServiceEnumerationBrowserTest&) = delete;
~WebRtcVideoCaptureServiceEnumerationBrowserTest() override {}
void ConnectToService() {
mojo::PendingRemote<video_capture::mojom::DevicesChangedObserver> observer;
devices_changed_observer_receiver_.Bind(
observer.InitWithNewPipeAndPassReceiver());
switch (GetParam().api_to_use) {
case ServiceApi::kSingleClient:
GetVideoCaptureService().ConnectToDeviceFactory(
factory_.BindNewPipeAndPassReceiver());
factory_->RegisterVirtualDevicesChangedObserver(
std::move(observer),
false /*raise_event_if_virtual_devices_already_present*/);
break;
case ServiceApi::kMultiClient:
GetVideoCaptureService().ConnectToVideoSourceProvider(
video_source_provider_.BindNewPipeAndPassReceiver());
video_source_provider_->RegisterVirtualDevicesChangedObserver(
std::move(observer),
false /*raise_event_if_virtual_devices_already_present*/);
break;
}
}
void AddVirtualDevice(const std::string& device_id) {
media::VideoCaptureDeviceInfo info;
info.descriptor.device_id = device_id;
info.descriptor.set_display_name(device_id);
info.descriptor.capture_api = media::VideoCaptureApi::VIRTUAL_DEVICE;
base::RunLoop wait_loop;
closure_to_be_called_on_devices_changed_ = wait_loop.QuitClosure();
switch (GetParam().device_type) {
case VirtualDeviceType::kSharedMemory: {
mojo::PendingRemote<video_capture::mojom::SharedMemoryVirtualDevice>
virtual_device;
mojo::PendingRemote<video_capture::mojom::Producer> producer;
auto mock_producer = std::make_unique<video_capture::MockProducer>(
producer.InitWithNewPipeAndPassReceiver());
switch (GetParam().api_to_use) {
case ServiceApi::kSingleClient:
factory_->AddSharedMemoryVirtualDevice(
info, std::move(producer),
virtual_device.InitWithNewPipeAndPassReceiver());
break;
case ServiceApi::kMultiClient:
video_source_provider_->AddSharedMemoryVirtualDevice(
info, std::move(producer),
virtual_device.InitWithNewPipeAndPassReceiver());
break;
}
shared_memory_devices_by_id_.insert(std::make_pair(
device_id, std::make_pair(std::move(virtual_device),
std::move(mock_producer))));
break;
}
case VirtualDeviceType::kTexture: {
mojo::PendingRemote<video_capture::mojom::TextureVirtualDevice>
virtual_device;
switch (GetParam().api_to_use) {
case ServiceApi::kSingleClient:
factory_->AddTextureVirtualDevice(
info, virtual_device.InitWithNewPipeAndPassReceiver());
break;
case ServiceApi::kMultiClient:
video_source_provider_->AddTextureVirtualDevice(
info, virtual_device.InitWithNewPipeAndPassReceiver());
break;
}
texture_devices_by_id_.insert(
std::make_pair(device_id, std::move(virtual_device)));
break;
}
}
// Wait for confirmation from the service.
wait_loop.Run();
}
void RemoveVirtualDevice(const std::string& device_id) {
base::RunLoop wait_loop;
closure_to_be_called_on_devices_changed_ = wait_loop.QuitClosure();
switch (GetParam().device_type) {
case VirtualDeviceType::kSharedMemory:
shared_memory_devices_by_id_.erase(device_id);
break;
case VirtualDeviceType::kTexture:
texture_devices_by_id_.erase(device_id);
break;
}
// Wait for confirmation from the service.
wait_loop.Run();
}
void DisconnectFromService() {
factory_.reset();
video_source_provider_.reset();
}
void EnumerateDevicesInRendererAndVerifyDeviceCount(
int expected_device_count) {
const std::string javascript_to_execute = base::StringPrintf(
kEnumerateVideoCaptureDevicesAndVerify, expected_device_count);
ASSERT_EQ("OK", EvalJs(shell(), javascript_to_execute,
EXECUTE_SCRIPT_USE_MANUAL_REPLY));
}
void RegisterForDeviceChangeEventInRenderer() {
ASSERT_TRUE(ExecJs(shell(), kRegisterForDeviceChangeEvent));
}
void WaitForDeviceChangeEventInRenderer() {
ASSERT_EQ("OK", EvalJs(shell(), kWaitForDeviceChangeEvent,
EXECUTE_SCRIPT_USE_MANUAL_REPLY));
}
void ResetHasReceivedChangedEventFlag() {
ASSERT_TRUE(ExecJs(shell(), kResetHasReceivedChangedEventFlag));
}
// Implementation of video_capture::mojom::DevicesChangedObserver:
void OnDevicesChanged() override {
if (closure_to_be_called_on_devices_changed_) {
std::move(closure_to_be_called_on_devices_changed_).Run();
}
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
// Note: We are not planning to actually use any fake device, but we want
// to avoid enumerating or otherwise calling into real capture devices.
command_line->RemoveSwitch(switches::kUseFakeDeviceForMediaStream);
command_line->AppendSwitchASCII(switches::kUseFakeDeviceForMediaStream,
"device-count=0");
command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
}
void Initialize() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
embedded_test_server()->StartAcceptingConnections();
EXPECT_TRUE(NavigateToURL(
shell(), GURL(embedded_test_server()->GetURL(kVideoCaptureHtmlFile))));
}
std::map<std::string,
mojo::PendingRemote<video_capture::mojom::TextureVirtualDevice>>
texture_devices_by_id_;
std::map<std::string,
std::pair<mojo::PendingRemote<
video_capture::mojom::SharedMemoryVirtualDevice>,
std::unique_ptr<video_capture::MockProducer>>>
shared_memory_devices_by_id_;
private:
mojo::Receiver<video_capture::mojom::DevicesChangedObserver>
devices_changed_observer_receiver_{this};
base::test::ScopedFeatureList scoped_feature_list_;
mojo::Remote<video_capture::mojom::DeviceFactory> factory_;
mojo::Remote<video_capture::mojom::VideoSourceProvider>
video_source_provider_;
base::OnceClosure closure_to_be_called_on_devices_changed_;
};
IN_PROC_BROWSER_TEST_P(WebRtcVideoCaptureServiceEnumerationBrowserTest,
SingleAddedVirtualDeviceGetsEnumerated) {
Initialize();
ConnectToService();
// Exercise
AddVirtualDevice("test");
EnumerateDevicesInRendererAndVerifyDeviceCount(1);
// Tear down
RemoveVirtualDevice("test");
DisconnectFromService();
}
IN_PROC_BROWSER_TEST_P(WebRtcVideoCaptureServiceEnumerationBrowserTest,
RemoveVirtualDeviceAfterItHasBeenEnumerated) {
Initialize();
ConnectToService();
AddVirtualDevice("test_1");
AddVirtualDevice("test_2");
EnumerateDevicesInRendererAndVerifyDeviceCount(2);
RemoveVirtualDevice("test_1");
EnumerateDevicesInRendererAndVerifyDeviceCount(1);
RemoveVirtualDevice("test_2");
EnumerateDevicesInRendererAndVerifyDeviceCount(0);
// Tear down
DisconnectFromService();
}
// The mediadevices.ondevicechange event is currently not supported on Android.
// Flaky on ChromeOS. https://crbug.com/1126373
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || \
BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange \
DISABLED_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange
#else
#define MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange \
AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange
#endif
IN_PROC_BROWSER_TEST_P(
WebRtcVideoCaptureServiceEnumerationBrowserTest,
MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange) {
Initialize();
ConnectToService();
RegisterForDeviceChangeEventInRenderer();
// Exercise
AddVirtualDevice("test");
WaitForDeviceChangeEventInRenderer();
ResetHasReceivedChangedEventFlag();
RemoveVirtualDevice("test");
WaitForDeviceChangeEventInRenderer();
// Tear down
DisconnectFromService();
}
INSTANTIATE_TEST_SUITE_P(
All,
WebRtcVideoCaptureServiceEnumerationBrowserTest,
::testing::Values(
TestParams{ServiceApi::kSingleClient, VirtualDeviceType::kSharedMemory},
TestParams{ServiceApi::kSingleClient, VirtualDeviceType::kTexture},
TestParams{ServiceApi::kMultiClient, VirtualDeviceType::kSharedMemory},
TestParams{ServiceApi::kMultiClient, VirtualDeviceType::kTexture}));
} // namespace content