1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/media_stream_dispatcher.h"
7 #include "base/logging.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "content/common/media/media_stream_messages.h"
10 #include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
11 #include "content/renderer/render_thread_impl.h"
12 #include "content/renderer/render_view_impl.h"
19 bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info,
20 StreamDeviceInfoArray* array) {
21 for (StreamDeviceInfoArray::iterator device_it = array->begin();
22 device_it != array->end(); ++device_it) {
23 if (StreamDeviceInfo::IsEqual(*device_it, device_info)) {
24 array->erase(device_it);
33 // A request is identified by pair (request_id, handler), or ipc_request.
34 // There could be multiple clients making requests and each has its own
35 // request_id sequence.
36 // The ipc_request is garanteed to be unique when it's created in
37 // MediaStreamDispatcher.
38 struct MediaStreamDispatcher::Request {
39 Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
43 request_id(request_id),
44 ipc_request(ipc_request) {
48 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
49 return (request_id1 == request_id && handler1.get() == handler.get());
51 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
56 struct MediaStreamDispatcher::Stream {
59 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
60 StreamDeviceInfoArray audio_array;
61 StreamDeviceInfoArray video_array;
64 MediaStreamDispatcher::EnumerationRequest::EnumerationRequest(
65 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
68 request_id(request_id) {
71 MediaStreamDispatcher::EnumerationRequest::~EnumerationRequest() {}
73 bool MediaStreamDispatcher::EnumerationRequest::IsThisRequest(
75 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
76 return (request_id1 == request_id && handler1.get() == handler.get());
79 MediaStreamDispatcher::EnumerationState::EnumerationState()
83 MediaStreamDispatcher::EnumerationState::~EnumerationState() {}
85 struct MediaStreamDispatcher::EnumerationState::CachedDevices {
86 CachedDevices(const std::string& label,
87 const StreamDeviceInfoArray& device_array)
89 devices(device_array) {
94 StreamDeviceInfoArray devices;
97 MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view)
98 : RenderViewObserver(render_view),
99 main_loop_(base::MessageLoopProxy::current()),
103 MediaStreamDispatcher::~MediaStreamDispatcher() {}
105 void MediaStreamDispatcher::GenerateStream(
107 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
108 const StreamOptions& components,
109 const GURL& security_origin) {
110 DCHECK(main_loop_->BelongsToCurrentThread());
111 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")";
113 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
114 Send(new MediaStreamHostMsg_GenerateStream(routing_id(),
120 void MediaStreamDispatcher::CancelGenerateStream(
122 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
123 DCHECK(main_loop_->BelongsToCurrentThread());
124 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
125 << ", {request_id = " << request_id << "}";
127 RequestList::iterator it = requests_.begin();
128 for (; it != requests_.end(); ++it) {
129 if (it->IsThisRequest(request_id, event_handler)) {
130 int ipc_request = it->ipc_request;
132 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
139 void MediaStreamDispatcher::StopStreamDevice(
140 const StreamDeviceInfo& device_info) {
141 DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
142 << ", {device_id = " << device_info.device.id << "}";
144 // Remove |device_info| from all streams in |label_stream_map_|.
145 bool device_found = false;
146 LabelStreamMap::iterator stream_it = label_stream_map_.begin();
147 while (stream_it != label_stream_map_.end()) {
148 StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
149 StreamDeviceInfoArray& video_array = stream_it->second.video_array;
151 if (RemoveStreamDeviceFromArray(device_info, &audio_array) ||
152 RemoveStreamDeviceFromArray(device_info, &video_array)) {
154 if (audio_array.empty() && video_array.empty()) {
155 label_stream_map_.erase(stream_it++);
161 DCHECK(device_found);
163 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
164 device_info.device.id));
167 void MediaStreamDispatcher::EnumerateDevices(
169 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
170 MediaStreamType type,
171 const GURL& security_origin) {
172 DCHECK(main_loop_->BelongsToCurrentThread());
173 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
174 type == MEDIA_DEVICE_VIDEO_CAPTURE);
175 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
176 << request_id << ")";
178 EnumerationState* state =
179 (type == MEDIA_DEVICE_AUDIO_CAPTURE ?
180 &audio_enumeration_state_ : &video_enumeration_state_);
181 state->requests.push_back(EnumerationRequest(event_handler, request_id));
183 if (state->cached_devices) {
184 event_handler->OnDevicesEnumerated(
185 request_id, state->cached_devices->devices);
186 } else if (state->ipc_id < 0) {
187 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
191 state->ipc_id = next_ipc_id_++;
195 void MediaStreamDispatcher::StopEnumerateDevices(
197 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
198 DCHECK(main_loop_->BelongsToCurrentThread());
199 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
200 << request_id << ")";
202 // Remove the request.
203 RemoveEnumerationRequest(
204 request_id, event_handler, &audio_enumeration_state_);
205 RemoveEnumerationRequest(
206 request_id, event_handler, &video_enumeration_state_);
209 void MediaStreamDispatcher::RemoveEnumerationRequest(
211 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
212 EnumerationState* state) {
213 EnumerationRequestList* requests = &state->requests;
214 for (EnumerationRequestList::iterator it = requests->begin();
215 it != requests->end(); ++it) {
216 if (it->IsThisRequest(request_id, event_handler)) {
218 if (requests->empty() && state->cached_devices) {
219 // No more request and has a label, try to stop the label
220 // and invalidate the state.
221 Send(new MediaStreamHostMsg_CancelEnumerateDevices(
222 routing_id(), state->cached_devices->label));
224 state->cached_devices.reset();
231 void MediaStreamDispatcher::OpenDevice(
233 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
234 const std::string& device_id,
235 MediaStreamType type,
236 const GURL& security_origin) {
237 DCHECK(main_loop_->BelongsToCurrentThread());
238 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";
240 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
241 Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
248 void MediaStreamDispatcher::CancelOpenDevice(
250 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
251 CancelGenerateStream(request_id, event_handler);
254 void MediaStreamDispatcher::CloseDevice(const std::string& label) {
255 DCHECK(main_loop_->BelongsToCurrentThread());
256 DCHECK(!label.empty());
257 DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
258 << ", {label = " << label << "}";
260 LabelStreamMap::iterator it = label_stream_map_.find(label);
261 if (it == label_stream_map_.end())
263 label_stream_map_.erase(it);
265 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
268 bool MediaStreamDispatcher::Send(IPC::Message* message) {
269 if (!RenderThread::Get()) {
274 return RenderThread::Get()->Send(message);
277 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
279 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
280 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
282 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
283 OnStreamGenerationFailed)
284 IPC_MESSAGE_HANDLER(MediaStreamMsg_StopGeneratedStream,
285 OnStopGeneratedStream)
286 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
288 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerationFailed,
289 OnDevicesEnumerationFailed)
290 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
292 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
294 IPC_MESSAGE_UNHANDLED(handled = false)
295 IPC_END_MESSAGE_MAP()
299 void MediaStreamDispatcher::OnStreamGenerated(
301 const std::string& label,
302 const StreamDeviceInfoArray& audio_array,
303 const StreamDeviceInfoArray& video_array) {
304 DCHECK(main_loop_->BelongsToCurrentThread());
306 for (RequestList::iterator it = requests_.begin();
307 it != requests_.end(); ++it) {
308 Request& request = *it;
309 if (request.ipc_request == request_id) {
311 new_stream.handler = request.handler;
312 new_stream.audio_array = audio_array;
313 new_stream.video_array = video_array;
314 label_stream_map_[label] = new_stream;
315 if (request.handler.get()) {
316 request.handler->OnStreamGenerated(
317 request.request_id, label, audio_array, video_array);
318 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
319 << request.request_id << ", " << label << ")";
327 void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) {
328 DCHECK(main_loop_->BelongsToCurrentThread());
329 for (RequestList::iterator it = requests_.begin();
330 it != requests_.end(); ++it) {
331 Request& request = *it;
332 if (request.ipc_request == request_id) {
333 if (request.handler.get()) {
334 request.handler->OnStreamGenerationFailed(request.request_id);
335 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
336 << request.request_id << ")\n";
344 void MediaStreamDispatcher::OnStopGeneratedStream(const std::string& label) {
345 DCHECK(main_loop_->BelongsToCurrentThread());
346 LabelStreamMap::iterator it = label_stream_map_.find(label);
347 if (it == label_stream_map_.end())
350 if (it->second.handler.get()) {
351 it->second.handler->OnStopGeneratedStream(label);
352 DVLOG(1) << "MediaStreamDispatcher::OnStopGeneratedStream("
355 label_stream_map_.erase(it);
358 void MediaStreamDispatcher::OnDevicesEnumerated(
360 const std::string& label,
361 const StreamDeviceInfoArray& device_array) {
362 DCHECK(main_loop_->BelongsToCurrentThread());
363 DCHECK_GE(request_id, 0);
365 EnumerationState* state;
366 if (request_id == audio_enumeration_state_.ipc_id) {
367 state = &audio_enumeration_state_;
368 } else if (request_id == video_enumeration_state_.ipc_id) {
369 state = &video_enumeration_state_;
371 // This could happen when requester has stopped enumeration while some
372 // enumerated response is on the way. Have to stop the |label| because
373 // this might be the first enumerated device list is received. This also
374 // lead to same label being stopped multiple times.
375 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(), label));
379 DCHECK(!label.empty());
380 state->cached_devices.reset(new EnumerationState::CachedDevices(
381 label, device_array));
383 for (EnumerationRequestList::iterator it = state->requests.begin();
384 it != state->requests.end(); ++it) {
385 if (it->handler.get()) {
386 it->handler->OnDevicesEnumerated(it->request_id, device_array);
387 DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerated("
388 << it->request_id << ")";
393 void MediaStreamDispatcher::OnDevicesEnumerationFailed(int request_id) {
394 DCHECK(main_loop_->BelongsToCurrentThread());
395 for (RequestList::iterator it = requests_.begin();
396 it != requests_.end(); ++it) {
397 Request& request = *it;
398 if (request.ipc_request == request_id) {
399 if (request.handler.get()) {
400 request.handler->OnDevicesEnumerationFailed(request.request_id);
401 DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerationFailed("
402 << request.request_id << ")\n";
410 void MediaStreamDispatcher::OnDeviceOpened(
412 const std::string& label,
413 const StreamDeviceInfo& device_info) {
414 DCHECK(main_loop_->BelongsToCurrentThread());
415 for (RequestList::iterator it = requests_.begin();
416 it != requests_.end(); ++it) {
417 Request& request = *it;
418 if (request.ipc_request == request_id) {
420 new_stream.handler = request.handler;
421 if (IsAudioMediaType(device_info.device.type)) {
422 new_stream.audio_array.push_back(device_info);
423 } else if (IsVideoMediaType(device_info.device.type)) {
424 new_stream.video_array.push_back(device_info);
428 label_stream_map_[label] = new_stream;
429 if (request.handler.get()) {
430 request.handler->OnDeviceOpened(request.request_id, label, device_info);
431 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
432 << request.request_id << ", " << label << ")";
440 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
441 DCHECK(main_loop_->BelongsToCurrentThread());
442 for (RequestList::iterator it = requests_.begin();
443 it != requests_.end(); ++it) {
444 Request& request = *it;
445 if (request.ipc_request == request_id) {
446 if (request.handler.get()) {
447 request.handler->OnDeviceOpenFailed(request.request_id);
448 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
449 << request.request_id << ")\n";
457 int MediaStreamDispatcher::audio_session_id(const std::string& label,
459 LabelStreamMap::iterator it = label_stream_map_.find(label);
460 if (it == label_stream_map_.end() ||
461 it->second.audio_array.size() <= static_cast<size_t>(index)) {
462 return StreamDeviceInfo::kNoId;
464 return it->second.audio_array[index].session_id;
467 bool MediaStreamDispatcher::IsStream(const std::string& label) {
468 return label_stream_map_.find(label) != label_stream_map_.end();
471 int MediaStreamDispatcher::video_session_id(const std::string& label,
473 LabelStreamMap::iterator it = label_stream_map_.find(label);
474 if (it == label_stream_map_.end() ||
475 it->second.video_array.size() <= static_cast<size_t>(index)) {
476 return StreamDeviceInfo::kNoId;
478 return it->second.video_array[index].session_id;
481 } // namespace content