blob: cc241faa10650b43f92d9e44c586b9c7fe6422ae [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/media_stream_dispatcher.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "content/common/media/media_stream_messages.h"
#include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "url/gurl.h"
namespace content {
// A request is identified by pair (request_id, handler), or ipc_request.
// There could be multiple clients making requests and each has its own
// request_id sequence.
// The ipc_request is garanteed to be unique when it's created in
// MediaStreamDispatcher.
struct MediaStreamDispatcher::Request {
Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
int request_id,
int ipc_request)
: handler(handler),
request_id(request_id),
ipc_request(ipc_request) {
}
bool IsThisRequest(
int request_id1,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
return (request_id1 == request_id && handler1.get() == handler.get());
}
base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
int request_id;
int ipc_request;
};
struct MediaStreamDispatcher::Stream {
Stream() {}
~Stream() {}
base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
StreamDeviceInfoArray audio_array;
StreamDeviceInfoArray video_array;
};
MediaStreamDispatcher::EnumerationRequest::EnumerationRequest(
const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
int request_id)
: handler(handler),
request_id(request_id) {
}
MediaStreamDispatcher::EnumerationRequest::~EnumerationRequest() {}
bool MediaStreamDispatcher::EnumerationRequest::IsThisRequest(
int request_id1,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
return (request_id1 == request_id && handler1.get() == handler.get());
}
MediaStreamDispatcher::EnumerationState::EnumerationState()
: ipc_id(-1) {
}
MediaStreamDispatcher::EnumerationState::~EnumerationState() {}
struct MediaStreamDispatcher::EnumerationState::CachedDevices {
CachedDevices(const std::string& label,
const StreamDeviceInfoArray& device_array)
: label(label),
devices(device_array) {
}
~CachedDevices() {}
std::string label;
StreamDeviceInfoArray devices;
};
MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view)
: RenderViewObserver(render_view),
main_loop_(base::MessageLoopProxy::current()),
next_ipc_id_(0) {
}
MediaStreamDispatcher::~MediaStreamDispatcher() {}
void MediaStreamDispatcher::GenerateStream(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
const StreamOptions& components,
const GURL& security_origin) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")";
requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
Send(new MediaStreamHostMsg_GenerateStream(routing_id(),
next_ipc_id_++,
components,
security_origin));
}
void MediaStreamDispatcher::CancelGenerateStream(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
<< ", {request_id = " << request_id << "}";
RequestList::iterator it = requests_.begin();
for (; it != requests_.end(); ++it) {
if (it->IsThisRequest(request_id, event_handler)) {
int ipc_request = it->ipc_request;
requests_.erase(it);
Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
ipc_request));
break;
}
}
}
void MediaStreamDispatcher::StopStream(const std::string& label) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::StopStream"
<< ", {label = " << label << "}";
LabelStreamMap::iterator it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return;
Send(new MediaStreamHostMsg_StopGeneratedStream(routing_id(), label));
label_stream_map_.erase(it);
}
void MediaStreamDispatcher::EnumerateDevices(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
MediaStreamType type,
const GURL& security_origin) {
DCHECK(main_loop_->BelongsToCurrentThread());
DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
type == MEDIA_DEVICE_VIDEO_CAPTURE);
DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
<< request_id << ")";
EnumerationState* state =
(type == MEDIA_DEVICE_AUDIO_CAPTURE ?
&audio_enumeration_state_ : &video_enumeration_state_);
state->requests.push_back(EnumerationRequest(event_handler, request_id));
if (state->cached_devices) {
event_handler->OnDevicesEnumerated(
request_id, state->cached_devices->devices);
} else if (state->ipc_id < 0) {
Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
next_ipc_id_,
type,
security_origin));
state->ipc_id = next_ipc_id_++;
}
}
void MediaStreamDispatcher::StopEnumerateDevices(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
<< request_id << ")";
// Remove the request.
RemoveEnumerationRequest(
request_id, event_handler, &audio_enumeration_state_);
RemoveEnumerationRequest(
request_id, event_handler, &video_enumeration_state_);
}
void MediaStreamDispatcher::RemoveEnumerationRequest(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
EnumerationState* state) {
EnumerationRequestList* requests = &state->requests;
for (EnumerationRequestList::iterator it = requests->begin();
it != requests->end(); ++it) {
if (it->IsThisRequest(request_id, event_handler)) {
requests->erase(it);
if (requests->empty() && state->cached_devices) {
// No more request and has a label, try to stop the label
// and invalidate the state.
Send(new MediaStreamHostMsg_StopGeneratedStream(
routing_id(), state->cached_devices->label));
state->ipc_id = -1;
state->cached_devices.reset();
}
return;
}
}
}
void MediaStreamDispatcher::OpenDevice(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
const std::string& device_id,
MediaStreamType type,
const GURL& security_origin) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";
requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
next_ipc_id_++,
device_id,
type,
security_origin));
}
void MediaStreamDispatcher::CancelOpenDevice(
int request_id,
const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
CancelGenerateStream(request_id, event_handler);
}
void MediaStreamDispatcher::CloseDevice(const std::string& label) {
DCHECK(main_loop_->BelongsToCurrentThread());
DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
<< ", {label = " << label << "}";
StopStream(label);
}
bool MediaStreamDispatcher::Send(IPC::Message* message) {
if (!RenderThread::Get()) {
delete message;
return false;
}
return RenderThread::Get()->Send(message);
}
bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
OnStreamGenerated)
IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
OnStreamGenerationFailed)
IPC_MESSAGE_HANDLER(MediaStreamMsg_StopGeneratedStream,
OnStopGeneratedStream)
IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
OnDevicesEnumerated)
IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerationFailed,
OnDevicesEnumerationFailed)
IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
OnDeviceOpened)
IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
OnDeviceOpenFailed)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void MediaStreamDispatcher::OnStreamGenerated(
int request_id,
const std::string& label,
const StreamDeviceInfoArray& audio_array,
const StreamDeviceInfoArray& video_array) {
DCHECK(main_loop_->BelongsToCurrentThread());
for (RequestList::iterator it = requests_.begin();
it != requests_.end(); ++it) {
Request& request = *it;
if (request.ipc_request == request_id) {
Stream new_stream;
new_stream.handler = request.handler;
new_stream.audio_array = audio_array;
new_stream.video_array = video_array;
label_stream_map_[label] = new_stream;
if (request.handler.get()) {
request.handler->OnStreamGenerated(
request.request_id, label, audio_array, video_array);
DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
<< request.request_id << ", " << label << ")";
}
requests_.erase(it);
break;
}
}
}
void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) {
DCHECK(main_loop_->BelongsToCurrentThread());
for (RequestList::iterator it = requests_.begin();
it != requests_.end(); ++it) {
Request& request = *it;
if (request.ipc_request == request_id) {
if (request.handler.get()) {
request.handler->OnStreamGenerationFailed(request.request_id);
DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
<< request.request_id << ")\n";
}
requests_.erase(it);
break;
}
}
}
void MediaStreamDispatcher::OnStopGeneratedStream(const std::string& label) {
DCHECK(main_loop_->BelongsToCurrentThread());
LabelStreamMap::iterator it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return;
if (it->second.handler.get()) {
it->second.handler->OnStopGeneratedStream(label);
DVLOG(1) << "MediaStreamDispatcher::OnStopGeneratedStream("
<< label << ")\n";
}
label_stream_map_.erase(it);
}
void MediaStreamDispatcher::OnDevicesEnumerated(
int request_id,
const std::string& label,
const StreamDeviceInfoArray& device_array) {
DCHECK(main_loop_->BelongsToCurrentThread());
DCHECK_GE(request_id, 0);
EnumerationState* state;
if (request_id == audio_enumeration_state_.ipc_id) {
state = &audio_enumeration_state_;
} else if (request_id == video_enumeration_state_.ipc_id) {
state = &video_enumeration_state_;
} else {
// This could happen when requester has stopped enumeration while some
// enumerated response is on the way. Have to stop the |label| because
// this might be the first enumerated device list is received. This also
// lead to same label being stopped multiple times.
Send(new MediaStreamHostMsg_StopGeneratedStream(routing_id(), label));
return;
}
DCHECK(!label.empty());
state->cached_devices.reset(new EnumerationState::CachedDevices(
label, device_array));
for (EnumerationRequestList::iterator it = state->requests.begin();
it != state->requests.end(); ++it) {
if (it->handler.get()) {
it->handler->OnDevicesEnumerated(it->request_id, device_array);
DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerated("
<< it->request_id << ")";
}
}
}
void MediaStreamDispatcher::OnDevicesEnumerationFailed(int request_id) {
DCHECK(main_loop_->BelongsToCurrentThread());
for (RequestList::iterator it = requests_.begin();
it != requests_.end(); ++it) {
Request& request = *it;
if (request.ipc_request == request_id) {
if (request.handler.get()) {
request.handler->OnDevicesEnumerationFailed(request.request_id);
DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerationFailed("
<< request.request_id << ")\n";
}
requests_.erase(it);
break;
}
}
}
void MediaStreamDispatcher::OnDeviceOpened(
int request_id,
const std::string& label,
const StreamDeviceInfo& device_info) {
DCHECK(main_loop_->BelongsToCurrentThread());
for (RequestList::iterator it = requests_.begin();
it != requests_.end(); ++it) {
Request& request = *it;
if (request.ipc_request == request_id) {
Stream new_stream;
new_stream.handler = request.handler;
if (IsAudioMediaType(device_info.device.type)) {
new_stream.audio_array.push_back(device_info);
} else if (IsVideoMediaType(device_info.device.type)) {
new_stream.video_array.push_back(device_info);
} else {
NOTREACHED();
}
label_stream_map_[label] = new_stream;
if (request.handler.get()) {
request.handler->OnDeviceOpened(request.request_id, label, device_info);
DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
<< request.request_id << ", " << label << ")";
}
requests_.erase(it);
break;
}
}
}
void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
DCHECK(main_loop_->BelongsToCurrentThread());
for (RequestList::iterator it = requests_.begin();
it != requests_.end(); ++it) {
Request& request = *it;
if (request.ipc_request == request_id) {
if (request.handler.get()) {
request.handler->OnDeviceOpenFailed(request.request_id);
DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
<< request.request_id << ")\n";
}
requests_.erase(it);
break;
}
}
}
int MediaStreamDispatcher::audio_session_id(const std::string& label,
int index) {
LabelStreamMap::iterator it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return StreamDeviceInfo::kNoId;
DCHECK_GT(it->second.audio_array.size(), static_cast<size_t>(index));
return it->second.audio_array[index].session_id;
}
bool MediaStreamDispatcher::IsStream(const std::string& label) {
return label_stream_map_.find(label) != label_stream_map_.end();
}
int MediaStreamDispatcher::video_session_id(const std::string& label,
int index) {
LabelStreamMap::iterator it = label_stream_map_.find(label);
if (it == label_stream_map_.end())
return StreamDeviceInfo::kNoId;
DCHECK_GT(it->second.video_array.size(), static_cast<size_t>(index));
return it->second.video_array[index].session_id;
}
} // namespace content