blob: 80a3e5df96266abaccf7b8f0d018fac18403cdba [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/media/stream/media_stream_device_observer.h"
#include <stddef.h>
#include <utility>
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "content/child/child_thread_impl.h"
#include "content/public/common/service_names.mojom.h"
#include "content/public/renderer/render_frame.h"
#include "content/renderer/media/stream/media_stream_dispatcher_eventhandler.h"
#include "url/origin.h"
namespace content {
namespace {
bool RemoveStreamDeviceFromArray(const MediaStreamDevice& device,
MediaStreamDevices* devices) {
for (MediaStreamDevices::iterator device_it = devices->begin();
device_it != devices->end(); ++device_it) {
if (device_it->IsSameDevice(device)) {
devices->erase(device_it);
return true;
}
}
return false;
}
} // namespace
struct MediaStreamDeviceObserver::Stream {
Stream() {}
~Stream() {}
base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
MediaStreamDevices audio_devices;
MediaStreamDevices video_devices;
};
MediaStreamDeviceObserver::MediaStreamDeviceObserver(RenderFrame* render_frame)
: RenderFrameObserver(render_frame), binding_(this) {
registry_.AddInterface(base::Bind(
&MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest,
base::Unretained(this)));
}
MediaStreamDeviceObserver::~MediaStreamDeviceObserver() {}
MediaStreamDevices MediaStreamDeviceObserver::GetNonScreenCaptureDevices() {
MediaStreamDevices video_devices;
for (const auto& stream_it : label_stream_map_) {
for (const auto& video_device : stream_it.second.video_devices) {
if (!IsScreenCaptureMediaType(video_device.type))
video_devices.push_back(video_device);
}
}
return video_devices;
}
void MediaStreamDeviceObserver::OnInterfaceRequestForFrame(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle* interface_pipe) {
registry_.TryBindInterface(interface_name, interface_pipe);
}
void MediaStreamDeviceObserver::OnDestruct() {
// Do not self-destruct. UserMediaClientImpl owns |this|.
}
void MediaStreamDeviceObserver::OnDeviceStopped(
const std::string& label,
const MediaStreamDevice& device) {
DVLOG(1) << __func__ << " label=" << label << " device_id=" << device.id;
DCHECK(thread_checker_.CalledOnValidThread());
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end()) {
// This can happen if a user happen stop a the device from JS at the same
// time as the underlying media device is unplugged from the system.
return;
}
Stream* stream = &it->second;
if (IsAudioInputMediaType(device.type))
RemoveStreamDeviceFromArray(device, &stream->audio_devices);
else
RemoveStreamDeviceFromArray(device, &stream->video_devices);
if (stream->handler.get())
stream->handler->OnDeviceStopped(device);
// |it| could have already been invalidated in the function call above. So we
// need to check if |label| is still in |label_stream_map_| again.
// Note: this is a quick fix to the crash caused by erasing the invalidated
// iterator from |label_stream_map_| (crbug.com/616884). Future work needs to
// be done to resolve this re-entrancy issue.
it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return;
stream = &it->second;
if (stream->audio_devices.empty() && stream->video_devices.empty())
label_stream_map_.erase(it);
}
void MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest(
mojom::MediaStreamDeviceObserverRequest request) {
binding_.Bind(std::move(request));
}
void MediaStreamDeviceObserver::AddStream(
const std::string& label,
const MediaStreamDevices& audio_devices,
const MediaStreamDevices& video_devices,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
DCHECK(thread_checker_.CalledOnValidThread());
Stream stream;
stream.handler = event_handler;
stream.audio_devices = audio_devices;
stream.video_devices = video_devices;
label_stream_map_[label] = stream;
}
void MediaStreamDeviceObserver::AddStream(const std::string& label,
const MediaStreamDevice& device) {
DCHECK(thread_checker_.CalledOnValidThread());
Stream stream;
if (IsAudioInputMediaType(device.type))
stream.audio_devices.push_back(device);
else if (IsVideoInputMediaType(device.type))
stream.video_devices.push_back(device);
else
NOTREACHED();
label_stream_map_[label] = stream;
}
bool MediaStreamDeviceObserver::RemoveStream(const std::string& label) {
DCHECK(thread_checker_.CalledOnValidThread());
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return false;
label_stream_map_.erase(it);
return true;
}
void MediaStreamDeviceObserver::RemoveStreamDevice(
const MediaStreamDevice& device) {
DCHECK(thread_checker_.CalledOnValidThread());
// Remove |device| from all streams in |label_stream_map_|.
bool device_found = false;
auto stream_it = label_stream_map_.begin();
while (stream_it != label_stream_map_.end()) {
MediaStreamDevices& audio_devices = stream_it->second.audio_devices;
MediaStreamDevices& video_devices = stream_it->second.video_devices;
if (RemoveStreamDeviceFromArray(device, &audio_devices) ||
RemoveStreamDeviceFromArray(device, &video_devices)) {
device_found = true;
if (audio_devices.empty() && video_devices.empty()) {
label_stream_map_.erase(stream_it++);
continue;
}
}
++stream_it;
}
DCHECK(device_found);
}
int MediaStreamDeviceObserver::audio_session_id(const std::string& label) {
DCHECK(thread_checker_.CalledOnValidThread());
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end() || it->second.audio_devices.empty())
return MediaStreamDevice::kNoId;
return it->second.audio_devices[0].session_id;
}
int MediaStreamDeviceObserver::video_session_id(const std::string& label) {
DCHECK(thread_checker_.CalledOnValidThread());
auto it = label_stream_map_.find(label);
if (it == label_stream_map_.end() || it->second.video_devices.empty())
return MediaStreamDevice::kNoId;
return it->second.video_devices[0].session_id;
}
} // namespace content