• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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