blob: d23f0f050e90c30d00eee9e1de724d7b3f3fd715 [file] [log] [blame]
// Copyright (c) 2012 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/renderer/pepper/pepper_media_device_manager.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/common/content_features.h"
#include "content/renderer/media/stream/media_stream_device_observer.h"
#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
#include "content/renderer/render_frame_impl.h"
#include "media/media_buildflags.h"
#include "ppapi/shared_impl/ppb_device_ref_shared.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
namespace content {
namespace {
const char kPepperInsecureOriginMessage[] =
"Microphone and Camera access no longer works on insecure origins. To use "
"this feature, you should consider switching your application to a "
"secure origin, such as HTTPS. See https://goo.gl/rStTGz for more "
"details.";
PP_DeviceType_Dev FromMediaDeviceType(blink::MediaDeviceType type) {
switch (type) {
case blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT:
return PP_DEVICETYPE_DEV_AUDIOCAPTURE;
case blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT:
return PP_DEVICETYPE_DEV_VIDEOCAPTURE;
case blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
return PP_DEVICETYPE_DEV_AUDIOOUTPUT;
default:
NOTREACHED();
return PP_DEVICETYPE_DEV_INVALID;
}
}
blink::MediaDeviceType ToMediaDeviceType(PP_DeviceType_Dev type) {
switch (type) {
case PP_DEVICETYPE_DEV_AUDIOCAPTURE:
return blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT;
case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
return blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT;
case PP_DEVICETYPE_DEV_AUDIOOUTPUT:
return blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
default:
NOTREACHED();
return blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
}
}
ppapi::DeviceRefData FromMediaDeviceInfo(
blink::MediaDeviceType type,
const blink::WebMediaDeviceInfo& info) {
ppapi::DeviceRefData data;
data.id = info.device_id;
// Some Flash content can't handle an empty string, so stick a space in to
// make them happy. See crbug.com/408404.
data.name = info.label.empty() ? std::string(" ") : info.label;
data.type = FromMediaDeviceType(type);
return data;
}
std::vector<ppapi::DeviceRefData> FromMediaDeviceInfoArray(
blink::MediaDeviceType type,
const blink::WebMediaDeviceInfoArray& device_infos) {
std::vector<ppapi::DeviceRefData> devices;
devices.reserve(device_infos.size());
for (const auto& device_info : device_infos)
devices.push_back(FromMediaDeviceInfo(type, device_info));
return devices;
}
} // namespace
base::WeakPtr<PepperMediaDeviceManager>
PepperMediaDeviceManager::GetForRenderFrame(
RenderFrame* render_frame) {
PepperMediaDeviceManager* handler =
PepperMediaDeviceManager::Get(render_frame);
if (!handler)
handler = new PepperMediaDeviceManager(render_frame);
return handler->AsWeakPtr();
}
PepperMediaDeviceManager::PepperMediaDeviceManager(RenderFrame* render_frame)
: RenderFrameObserver(render_frame),
RenderFrameObserverTracker<PepperMediaDeviceManager>(render_frame) {}
PepperMediaDeviceManager::~PepperMediaDeviceManager() {
DCHECK(open_callbacks_.empty());
}
void PepperMediaDeviceManager::EnumerateDevices(
PP_DeviceType_Dev type,
const DevicesCallback& callback) {
bool request_audio_input = type == PP_DEVICETYPE_DEV_AUDIOCAPTURE;
bool request_video_input = type == PP_DEVICETYPE_DEV_VIDEOCAPTURE;
bool request_audio_output = type == PP_DEVICETYPE_DEV_AUDIOOUTPUT;
CHECK(request_audio_input || request_video_input || request_audio_output);
GetMediaDevicesDispatcher()->EnumerateDevices(
request_audio_input, request_video_input, request_audio_output,
false /* request_video_input_capabilities */,
base::BindOnce(&PepperMediaDeviceManager::DevicesEnumerated, AsWeakPtr(),
callback, ToMediaDeviceType(type)));
}
size_t PepperMediaDeviceManager::StartMonitoringDevices(
PP_DeviceType_Dev type,
const DevicesCallback& callback) {
bool subscribe_audio_input = type == PP_DEVICETYPE_DEV_AUDIOCAPTURE;
bool subscribe_video_input = type == PP_DEVICETYPE_DEV_VIDEOCAPTURE;
bool subscribe_audio_output = type == PP_DEVICETYPE_DEV_AUDIOOUTPUT;
CHECK(subscribe_audio_input || subscribe_video_input ||
subscribe_audio_output);
blink::mojom::MediaDevicesListenerPtr listener;
size_t subscription_id =
bindings_.AddBinding(this, mojo::MakeRequest(&listener));
GetMediaDevicesDispatcher()->AddMediaDevicesListener(
subscribe_audio_input, subscribe_video_input, subscribe_audio_output,
std::move(listener));
SubscriptionList& subscriptions =
device_change_subscriptions_[ToMediaDeviceType(type)];
subscriptions.push_back(Subscription{subscription_id, callback});
return subscription_id;
}
void PepperMediaDeviceManager::StopMonitoringDevices(PP_DeviceType_Dev type,
size_t subscription_id) {
SubscriptionList& subscriptions =
device_change_subscriptions_[ToMediaDeviceType(type)];
base::EraseIf(subscriptions,
[subscription_id](const Subscription& subscription) {
return subscription.first == subscription_id;
});
bindings_.RemoveBinding(subscription_id);
}
int PepperMediaDeviceManager::OpenDevice(PP_DeviceType_Dev type,
const std::string& device_id,
PP_Instance pp_instance,
const OpenDeviceCallback& callback) {
open_callbacks_[next_id_] = callback;
int request_id = next_id_++;
RendererPpapiHostImpl* host =
RendererPpapiHostImpl::GetForPPInstance(pp_instance);
if (!host->IsSecureContext(pp_instance)) {
RenderFrame* render_frame = host->GetRenderFrameForInstance(pp_instance);
if (render_frame) {
render_frame->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning,
kPepperInsecureOriginMessage);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&PepperMediaDeviceManager::OnDeviceOpened,
AsWeakPtr(), request_id, false, std::string(),
blink::MediaStreamDevice()));
return request_id;
}
GetMediaStreamDispatcherHost()->OpenDevice(
request_id, device_id,
PepperMediaDeviceManager::FromPepperDeviceType(type),
base::BindOnce(&PepperMediaDeviceManager::OnDeviceOpened, AsWeakPtr(),
request_id));
return request_id;
}
void PepperMediaDeviceManager::CancelOpenDevice(int request_id) {
open_callbacks_.erase(request_id);
GetMediaStreamDispatcherHost()->CancelRequest(request_id);
}
void PepperMediaDeviceManager::CloseDevice(const std::string& label) {
if (!GetMediaStreamDeviceObserver()->RemoveStream(label))
return;
GetMediaStreamDispatcherHost()->CloseDevice(label);
}
int PepperMediaDeviceManager::GetSessionID(PP_DeviceType_Dev type,
const std::string& label) {
switch (type) {
case PP_DEVICETYPE_DEV_AUDIOCAPTURE:
return GetMediaStreamDeviceObserver()->audio_session_id(label);
case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
return GetMediaStreamDeviceObserver()->video_session_id(label);
default:
NOTREACHED();
return 0;
}
}
// static
blink::MediaStreamType PepperMediaDeviceManager::FromPepperDeviceType(
PP_DeviceType_Dev type) {
switch (type) {
case PP_DEVICETYPE_DEV_INVALID:
return blink::MEDIA_NO_SERVICE;
case PP_DEVICETYPE_DEV_AUDIOCAPTURE:
return blink::MEDIA_DEVICE_AUDIO_CAPTURE;
case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
return blink::MEDIA_DEVICE_VIDEO_CAPTURE;
default:
NOTREACHED();
return blink::MEDIA_NO_SERVICE;
}
}
void PepperMediaDeviceManager::OnDevicesChanged(
blink::MediaDeviceType type,
const blink::WebMediaDeviceInfoArray& device_infos) {
std::vector<ppapi::DeviceRefData> devices =
FromMediaDeviceInfoArray(type, device_infos);
SubscriptionList& subscriptions = device_change_subscriptions_[type];
for (auto& subscription : subscriptions)
subscription.second.Run(devices);
}
void PepperMediaDeviceManager::OnDeviceOpened(
int request_id,
bool success,
const std::string& label,
const blink::MediaStreamDevice& device) {
auto iter = open_callbacks_.find(request_id);
if (iter == open_callbacks_.end()) {
// The callback may have been unregistered.
return;
}
if (success)
GetMediaStreamDeviceObserver()->AddStream(label, device);
OpenDeviceCallback callback = iter->second;
open_callbacks_.erase(iter);
std::move(callback).Run(request_id, success, success ? label : std::string());
}
void PepperMediaDeviceManager::DevicesEnumerated(
const DevicesCallback& client_callback,
blink::MediaDeviceType type,
const std::vector<blink::WebMediaDeviceInfoArray>& enumeration,
std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr>
video_input_capabilities) {
client_callback.Run(FromMediaDeviceInfoArray(type, enumeration[type]));
}
const blink::mojom::MediaStreamDispatcherHostPtr&
PepperMediaDeviceManager::GetMediaStreamDispatcherHost() {
if (!dispatcher_host_) {
CHECK(render_frame());
CHECK(render_frame()->GetRemoteInterfaces());
render_frame()->GetRemoteInterfaces()->GetInterface(
mojo::MakeRequest(&dispatcher_host_));
}
return dispatcher_host_;
}
MediaStreamDeviceObserver*
PepperMediaDeviceManager::GetMediaStreamDeviceObserver() const {
DCHECK(render_frame());
MediaStreamDeviceObserver* const observer =
static_cast<RenderFrameImpl*>(render_frame())
->GetMediaStreamDeviceObserver();
DCHECK(observer);
return observer;
}
const blink::mojom::MediaDevicesDispatcherHostPtr&
PepperMediaDeviceManager::GetMediaDevicesDispatcher() {
if (!media_devices_dispatcher_) {
CHECK(render_frame());
CHECK(render_frame()->GetRemoteInterfaces());
render_frame()->GetRemoteInterfaces()->GetInterface(
mojo::MakeRequest(&media_devices_dispatcher_));
}
return media_devices_dispatcher_;
}
void PepperMediaDeviceManager::OnDestruct() {
delete this;
}
} // namespace content