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.
4
5 #include "content/renderer/media/media_stream_dispatcher.h"
6
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"
13 #include "url/gurl.h"
14
15 namespace content {
16
17 namespace {
18
RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info,StreamDeviceInfoArray * array)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);
25 return true;
26 }
27 }
28 return false;
29 }
30
31 } // namespace
32
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 {
Requestcontent::MediaStreamDispatcher::Request39 Request(const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
40 int request_id,
41 int ipc_request)
42 : handler(handler),
43 request_id(request_id),
44 ipc_request(ipc_request) {
45 }
IsThisRequestcontent::MediaStreamDispatcher::Request46 bool IsThisRequest(
47 int request_id1,
48 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
49 return (request_id1 == request_id && handler1.get() == handler.get());
50 }
51 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
52 int request_id;
53 int ipc_request;
54 };
55
56 struct MediaStreamDispatcher::Stream {
Streamcontent::MediaStreamDispatcher::Stream57 Stream() {}
~Streamcontent::MediaStreamDispatcher::Stream58 ~Stream() {}
59 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
60 StreamDeviceInfoArray audio_array;
61 StreamDeviceInfoArray video_array;
62 };
63
MediaStreamDispatcher(RenderViewImpl * render_view)64 MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view)
65 : RenderViewObserver(render_view),
66 main_loop_(base::MessageLoopProxy::current()),
67 next_ipc_id_(0) {
68 }
69
~MediaStreamDispatcher()70 MediaStreamDispatcher::~MediaStreamDispatcher() {}
71
GenerateStream(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler,const StreamOptions & components,const GURL & security_origin)72 void MediaStreamDispatcher::GenerateStream(
73 int request_id,
74 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
75 const StreamOptions& components,
76 const GURL& security_origin) {
77 DCHECK(main_loop_->BelongsToCurrentThread());
78 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")";
79
80 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
81 Send(new MediaStreamHostMsg_GenerateStream(routing_id(),
82 next_ipc_id_++,
83 components,
84 security_origin));
85 }
86
CancelGenerateStream(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler)87 void MediaStreamDispatcher::CancelGenerateStream(
88 int request_id,
89 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
90 DCHECK(main_loop_->BelongsToCurrentThread());
91 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
92 << ", {request_id = " << request_id << "}";
93
94 RequestList::iterator it = requests_.begin();
95 for (; it != requests_.end(); ++it) {
96 if (it->IsThisRequest(request_id, event_handler)) {
97 int ipc_request = it->ipc_request;
98 requests_.erase(it);
99 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
100 ipc_request));
101 break;
102 }
103 }
104 }
105
StopStreamDevice(const StreamDeviceInfo & device_info)106 void MediaStreamDispatcher::StopStreamDevice(
107 const StreamDeviceInfo& device_info) {
108 DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
109 << ", {device_id = " << device_info.device.id << "}";
110 // Remove |device_info| from all streams in |label_stream_map_|.
111 bool device_found = false;
112 LabelStreamMap::iterator stream_it = label_stream_map_.begin();
113 while (stream_it != label_stream_map_.end()) {
114 StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
115 StreamDeviceInfoArray& video_array = stream_it->second.video_array;
116
117 if (RemoveStreamDeviceFromArray(device_info, &audio_array) ||
118 RemoveStreamDeviceFromArray(device_info, &video_array)) {
119 device_found = true;
120 if (audio_array.empty() && video_array.empty()) {
121 label_stream_map_.erase(stream_it++);
122 continue;
123 }
124 }
125 ++stream_it;
126 }
127 DCHECK(device_found);
128
129 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
130 device_info.device.id));
131 }
132
EnumerateDevices(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler,MediaStreamType type,const GURL & security_origin)133 void MediaStreamDispatcher::EnumerateDevices(
134 int request_id,
135 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
136 MediaStreamType type,
137 const GURL& security_origin) {
138 DCHECK(main_loop_->BelongsToCurrentThread());
139 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
140 type == MEDIA_DEVICE_VIDEO_CAPTURE);
141 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
142 << request_id << ")";
143
144 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
145 ++it) {
146 DCHECK(!it->IsThisRequest(request_id, event_handler));
147 }
148
149 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
150 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
151 next_ipc_id_++,
152 type,
153 security_origin));
154 }
155
StopEnumerateDevices(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler)156 void MediaStreamDispatcher::StopEnumerateDevices(
157 int request_id,
158 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
159 DCHECK(main_loop_->BelongsToCurrentThread());
160 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
161 << request_id << ")";
162 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
163 ++it) {
164 if (it->IsThisRequest(request_id, event_handler)) {
165 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
166 it->ipc_request));
167 requests_.erase(it);
168 break;
169 }
170 }
171 }
172
OpenDevice(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler,const std::string & device_id,MediaStreamType type,const GURL & security_origin)173 void MediaStreamDispatcher::OpenDevice(
174 int request_id,
175 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
176 const std::string& device_id,
177 MediaStreamType type,
178 const GURL& security_origin) {
179 DCHECK(main_loop_->BelongsToCurrentThread());
180 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";
181
182 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
183 Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
184 next_ipc_id_++,
185 device_id,
186 type,
187 security_origin));
188 }
189
CancelOpenDevice(int request_id,const base::WeakPtr<MediaStreamDispatcherEventHandler> & event_handler)190 void MediaStreamDispatcher::CancelOpenDevice(
191 int request_id,
192 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
193 CancelGenerateStream(request_id, event_handler);
194 }
195
CloseDevice(const std::string & label)196 void MediaStreamDispatcher::CloseDevice(const std::string& label) {
197 DCHECK(main_loop_->BelongsToCurrentThread());
198 DCHECK(!label.empty());
199 DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
200 << ", {label = " << label << "}";
201
202 LabelStreamMap::iterator it = label_stream_map_.find(label);
203 if (it == label_stream_map_.end())
204 return;
205 label_stream_map_.erase(it);
206
207 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
208 }
209
Send(IPC::Message * message)210 bool MediaStreamDispatcher::Send(IPC::Message* message) {
211 if (!RenderThread::Get()) {
212 delete message;
213 return false;
214 }
215
216 return RenderThread::Get()->Send(message);
217 }
218
OnMessageReceived(const IPC::Message & message)219 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
220 bool handled = true;
221 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
222 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
223 OnStreamGenerated)
224 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
225 OnStreamGenerationFailed)
226 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
227 OnDeviceStopped)
228 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
229 OnDevicesEnumerated)
230 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
231 OnDeviceOpened)
232 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
233 OnDeviceOpenFailed)
234 IPC_MESSAGE_UNHANDLED(handled = false)
235 IPC_END_MESSAGE_MAP()
236 return handled;
237 }
238
OnStreamGenerated(int request_id,const std::string & label,const StreamDeviceInfoArray & audio_array,const StreamDeviceInfoArray & video_array)239 void MediaStreamDispatcher::OnStreamGenerated(
240 int request_id,
241 const std::string& label,
242 const StreamDeviceInfoArray& audio_array,
243 const StreamDeviceInfoArray& video_array) {
244 DCHECK(main_loop_->BelongsToCurrentThread());
245
246 for (RequestList::iterator it = requests_.begin();
247 it != requests_.end(); ++it) {
248 Request& request = *it;
249 if (request.ipc_request == request_id) {
250 Stream new_stream;
251 new_stream.handler = request.handler;
252 new_stream.audio_array = audio_array;
253 new_stream.video_array = video_array;
254 label_stream_map_[label] = new_stream;
255 if (request.handler.get()) {
256 request.handler->OnStreamGenerated(
257 request.request_id, label, audio_array, video_array);
258 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
259 << request.request_id << ", " << label << ")";
260 }
261 requests_.erase(it);
262 break;
263 }
264 }
265 }
266
OnStreamGenerationFailed(int request_id)267 void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) {
268 DCHECK(main_loop_->BelongsToCurrentThread());
269 for (RequestList::iterator it = requests_.begin();
270 it != requests_.end(); ++it) {
271 Request& request = *it;
272 if (request.ipc_request == request_id) {
273 if (request.handler.get()) {
274 request.handler->OnStreamGenerationFailed(request.request_id);
275 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
276 << request.request_id << ")\n";
277 }
278 requests_.erase(it);
279 break;
280 }
281 }
282 }
283
OnDeviceStopped(const std::string & label,const StreamDeviceInfo & device_info)284 void MediaStreamDispatcher::OnDeviceStopped(
285 const std::string& label,
286 const StreamDeviceInfo& device_info) {
287 DCHECK(main_loop_->BelongsToCurrentThread());
288 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
289 << "{label = " << label << "})"
290 << ", {device_id = " << device_info.device.id << "})";
291
292 LabelStreamMap::iterator it = label_stream_map_.find(label);
293 if (it == label_stream_map_.end()) {
294 // This can happen if a user happen stop a the device from JS at the same
295 // time as the underlying media device is unplugged from the system.
296 return;
297 }
298 Stream* stream = &it->second;
299 if (IsAudioMediaType(device_info.device.type))
300 RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
301 else
302 RemoveStreamDeviceFromArray(device_info, &stream->video_array);
303
304 if (stream->handler.get())
305 stream->handler->OnDeviceStopped(label, device_info);
306
307 if (stream->audio_array.empty() && stream->video_array.empty())
308 label_stream_map_.erase(it);
309 }
310
OnDevicesEnumerated(int request_id,const StreamDeviceInfoArray & device_array)311 void MediaStreamDispatcher::OnDevicesEnumerated(
312 int request_id,
313 const StreamDeviceInfoArray& device_array) {
314 DCHECK(main_loop_->BelongsToCurrentThread());
315 DCHECK_GE(request_id, 0);
316
317 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
318 ++it) {
319 if (it->ipc_request == request_id && it->handler.get()) {
320 it->handler->OnDevicesEnumerated(it->request_id, device_array);
321 break;
322 }
323 }
324 }
325
OnDeviceOpened(int request_id,const std::string & label,const StreamDeviceInfo & device_info)326 void MediaStreamDispatcher::OnDeviceOpened(
327 int request_id,
328 const std::string& label,
329 const StreamDeviceInfo& device_info) {
330 DCHECK(main_loop_->BelongsToCurrentThread());
331 for (RequestList::iterator it = requests_.begin();
332 it != requests_.end(); ++it) {
333 Request& request = *it;
334 if (request.ipc_request == request_id) {
335 Stream new_stream;
336 new_stream.handler = request.handler;
337 if (IsAudioMediaType(device_info.device.type)) {
338 new_stream.audio_array.push_back(device_info);
339 } else if (IsVideoMediaType(device_info.device.type)) {
340 new_stream.video_array.push_back(device_info);
341 } else {
342 NOTREACHED();
343 }
344 label_stream_map_[label] = new_stream;
345 if (request.handler.get()) {
346 request.handler->OnDeviceOpened(request.request_id, label, device_info);
347 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
348 << request.request_id << ", " << label << ")";
349 }
350 requests_.erase(it);
351 break;
352 }
353 }
354 }
355
OnDeviceOpenFailed(int request_id)356 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
357 DCHECK(main_loop_->BelongsToCurrentThread());
358 for (RequestList::iterator it = requests_.begin();
359 it != requests_.end(); ++it) {
360 Request& request = *it;
361 if (request.ipc_request == request_id) {
362 if (request.handler.get()) {
363 request.handler->OnDeviceOpenFailed(request.request_id);
364 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
365 << request.request_id << ")\n";
366 }
367 requests_.erase(it);
368 break;
369 }
370 }
371 }
372
audio_session_id(const std::string & label,int index)373 int MediaStreamDispatcher::audio_session_id(const std::string& label,
374 int index) {
375 LabelStreamMap::iterator it = label_stream_map_.find(label);
376 if (it == label_stream_map_.end() ||
377 it->second.audio_array.size() <= static_cast<size_t>(index)) {
378 return StreamDeviceInfo::kNoId;
379 }
380 return it->second.audio_array[index].session_id;
381 }
382
IsStream(const std::string & label)383 bool MediaStreamDispatcher::IsStream(const std::string& label) {
384 return label_stream_map_.find(label) != label_stream_map_.end();
385 }
386
video_session_id(const std::string & label,int index)387 int MediaStreamDispatcher::video_session_id(const std::string& label,
388 int index) {
389 LabelStreamMap::iterator it = label_stream_map_.find(label);
390 if (it == label_stream_map_.end() ||
391 it->second.video_array.size() <= static_cast<size_t>(index)) {
392 return StreamDeviceInfo::kNoId;
393 }
394 return it->second.video_array[index].session_id;
395 }
396
397 } // namespace content
398