// 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 { namespace { bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info, StreamDeviceInfoArray* array) { for (StreamDeviceInfoArray::iterator device_it = array->begin(); device_it != array->end(); ++device_it) { if (StreamDeviceInfo::IsEqual(*device_it, device_info)) { array->erase(device_it); return true; } } return false; } } // namespace // 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::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::StopStreamDevice( const StreamDeviceInfo& device_info) { DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice" << ", {device_id = " << device_info.device.id << "}"; // Remove |device_info| from all streams in |label_stream_map_|. bool device_found = false; LabelStreamMap::iterator stream_it = label_stream_map_.begin(); while (stream_it != label_stream_map_.end()) { StreamDeviceInfoArray& audio_array = stream_it->second.audio_array; StreamDeviceInfoArray& video_array = stream_it->second.video_array; if (RemoveStreamDeviceFromArray(device_info, &audio_array) || RemoveStreamDeviceFromArray(device_info, &video_array)) { device_found = true; if (audio_array.empty() && video_array.empty()) { label_stream_map_.erase(stream_it++); continue; } } ++stream_it; } DCHECK(device_found); Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(), device_info.device.id)); } 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 << ")"; for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { DCHECK(!it->IsThisRequest(request_id, event_handler)); } requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), next_ipc_id_++, type, security_origin)); } void MediaStreamDispatcher::StopEnumerateDevices( int request_id, const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices(" << request_id << ")"; for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { if (it->IsThisRequest(request_id, event_handler)) { Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(), it->ipc_request)); requests_.erase(it); break; } } } 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()); DCHECK(!label.empty()); DVLOG(1) << "MediaStreamDispatcher::CloseDevice" << ", {label = " << label << "}"; LabelStreamMap::iterator it = label_stream_map_.find(label); if (it == label_stream_map_.end()) return; label_stream_map_.erase(it); Send(new MediaStreamHostMsg_CloseDevice(routing_id(), 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_DeviceStopped, OnDeviceStopped) IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated, OnDevicesEnumerated) 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::OnDeviceStopped( const std::string& label, const StreamDeviceInfo& device_info) { DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped(" << "{label = " << label << "})" << ", {device_id = " << device_info.device.id << "})"; LabelStreamMap::iterator 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 (IsAudioMediaType(device_info.device.type)) RemoveStreamDeviceFromArray(device_info, &stream->audio_array); else RemoveStreamDeviceFromArray(device_info, &stream->video_array); if (stream->handler.get()) stream->handler->OnDeviceStopped(label, device_info); if (stream->audio_array.empty() && stream->video_array.empty()) label_stream_map_.erase(it); } void MediaStreamDispatcher::OnDevicesEnumerated( int request_id, const StreamDeviceInfoArray& device_array) { DCHECK(main_loop_->BelongsToCurrentThread()); DCHECK_GE(request_id, 0); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { if (it->ipc_request == request_id && it->handler.get()) { it->handler->OnDevicesEnumerated(it->request_id, device_array); 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() || it->second.audio_array.size() <= static_cast<size_t>(index)) { return StreamDeviceInfo::kNoId; } 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() || it->second.video_array.size() <= static_cast<size_t>(index)) { return StreamDeviceInfo::kNoId; } return it->second.video_array[index].session_id; } } // namespace content