blob: e00058891b2dd3cfd5b37ac18b2f1fb5c6130283 [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 "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/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 VirtualDeviceType { kSharedMemory, kTexture };
struct TestParams {
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() = default;
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());
GetVideoCaptureService().ConnectToVideoSourceProvider(
video_source_provider_.BindNewPipeAndPassReceiver());
video_source_provider_->RegisterVirtualDevicesChangedObserver(
std::move(observer),
false /*raise_event_if_virtual_devices_already_present*/);
}
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());
video_source_provider_->AddSharedMemoryVirtualDevice(
info, std::move(producer),
virtual_device.InitWithNewPipeAndPassReceiver());
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;
video_source_provider_->AddTextureVirtualDevice(
info, virtual_device.InitWithNewPipeAndPassReceiver());
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() { video_source_provider_.reset(); }
void EnumerateDevicesInRendererAndVerifyDeviceCount(
int expected_device_count) {
const std::string javascript_to_execute = base::StringPrintf(
kEnumerateVideoCaptureDevicesAndVerify, expected_device_count);
ASSERT_TRUE(ExecJs(shell(), javascript_to_execute));
}
void RegisterForDeviceChangeEventInRenderer() {
ASSERT_TRUE(ExecJs(shell(), kRegisterForDeviceChangeEvent));
}
void WaitForDeviceChangeEventInRenderer() {
ASSERT_TRUE(ExecJs(shell(), kWaitForDeviceChangeEvent));
}
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};
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();
}
// TODO(https://crbug.com/352672009): Flaky on Mac.
// TODO(https://crbug.com/352092989): Flaky on Windows.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#define MAYBE_RemoveVirtualDeviceAfterItHasBeenEnumerated \
DISABLED_RemoveVirtualDeviceAfterItHasBeenEnumerated
#else
#define MAYBE_RemoveVirtualDeviceAfterItHasBeenEnumerated \
RemoveVirtualDeviceAfterItHasBeenEnumerated
#endif
IN_PROC_BROWSER_TEST_P(WebRtcVideoCaptureServiceEnumerationBrowserTest,
MAYBE_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.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange \
DISABLED_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange
#else
#define MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange \
AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange
#endif
IN_PROC_BROWSER_TEST_P(
WebRtcVideoCaptureServiceEnumerationBrowserTest,
MAYBE_AddingAndRemovingVirtualDeviceTriggersMediaElementOnDeviceChange) {
Initialize();
ConnectToService();
RegisterForDeviceChangeEventInRenderer();
// Waiting for enumeration ensures that the browser everything is primed for
// device change events.
EnumerateDevicesInRendererAndVerifyDeviceCount(0);
// Exercise
AddVirtualDevice("test");
WaitForDeviceChangeEventInRenderer();
ResetHasReceivedChangedEventFlag();
RemoveVirtualDevice("test");
WaitForDeviceChangeEventInRenderer();
// Tear down
DisconnectFromService();
}
INSTANTIATE_TEST_SUITE_P(
All,
WebRtcVideoCaptureServiceEnumerationBrowserTest,
::testing::Values(TestParams{VirtualDeviceType::kSharedMemory},
TestParams{VirtualDeviceType::kTexture}));
} // namespace content