- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / media / media_stream_dispatcher.cc
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
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 {
39   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   }
46   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 {
57   Stream() {}
58   ~Stream() {}
59   base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
60   StreamDeviceInfoArray audio_array;
61   StreamDeviceInfoArray video_array;
62 };
63
64 MediaStreamDispatcher::EnumerationRequest::EnumerationRequest(
65     const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler,
66     int request_id)
67     : handler(handler),
68       request_id(request_id) {
69 }
70
71 MediaStreamDispatcher::EnumerationRequest::~EnumerationRequest() {}
72
73 bool MediaStreamDispatcher::EnumerationRequest::IsThisRequest(
74     int request_id1,
75     const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
76   return (request_id1 == request_id && handler1.get() == handler.get());
77 }
78
79 MediaStreamDispatcher::EnumerationState::EnumerationState()
80     : ipc_id(-1) {
81 }
82
83 MediaStreamDispatcher::EnumerationState::~EnumerationState() {}
84
85 struct MediaStreamDispatcher::EnumerationState::CachedDevices {
86   CachedDevices(const std::string& label,
87                 const StreamDeviceInfoArray& device_array)
88       : label(label),
89         devices(device_array) {
90   }
91   ~CachedDevices() {}
92
93   std::string label;
94   StreamDeviceInfoArray devices;
95 };
96
97 MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view)
98     : RenderViewObserver(render_view),
99       main_loop_(base::MessageLoopProxy::current()),
100       next_ipc_id_(0) {
101 }
102
103 MediaStreamDispatcher::~MediaStreamDispatcher() {}
104
105 void MediaStreamDispatcher::GenerateStream(
106     int request_id,
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 << ")";
112
113   requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
114   Send(new MediaStreamHostMsg_GenerateStream(routing_id(),
115                                              next_ipc_id_++,
116                                              components,
117                                              security_origin));
118 }
119
120 void MediaStreamDispatcher::CancelGenerateStream(
121     int request_id,
122     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
123   DCHECK(main_loop_->BelongsToCurrentThread());
124   DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
125            << ", {request_id = " << request_id << "}";
126
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;
131       requests_.erase(it);
132       Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
133                                                        ipc_request));
134       break;
135     }
136   }
137 }
138
139 void MediaStreamDispatcher::StopStreamDevice(
140     const StreamDeviceInfo& device_info) {
141   DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
142            << ", {device_id = " << device_info.device.id << "}";
143
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;
150
151     if (RemoveStreamDeviceFromArray(device_info, &audio_array) ||
152         RemoveStreamDeviceFromArray(device_info, &video_array)) {
153       device_found = true;
154       if (audio_array.empty() && video_array.empty()) {
155         label_stream_map_.erase(stream_it++);
156         continue;
157       }
158     }
159     ++stream_it;
160   }
161   DCHECK(device_found);
162
163   Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
164                                                device_info.device.id));
165 }
166
167 void MediaStreamDispatcher::EnumerateDevices(
168     int request_id,
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 << ")";
177
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));
182
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(),
188                                                  next_ipc_id_,
189                                                  type,
190                                                  security_origin));
191     state->ipc_id = next_ipc_id_++;
192   }
193 }
194
195 void MediaStreamDispatcher::StopEnumerateDevices(
196     int request_id,
197     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
198   DCHECK(main_loop_->BelongsToCurrentThread());
199   DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
200            << request_id << ")";
201
202   // Remove the request.
203   RemoveEnumerationRequest(
204       request_id, event_handler, &audio_enumeration_state_);
205   RemoveEnumerationRequest(
206       request_id, event_handler, &video_enumeration_state_);
207 }
208
209 void MediaStreamDispatcher::RemoveEnumerationRequest(
210     int request_id,
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)) {
217       requests->erase(it);
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));
223         state->ipc_id = -1;
224         state->cached_devices.reset();
225       }
226       return;
227     }
228   }
229 }
230
231 void MediaStreamDispatcher::OpenDevice(
232     int request_id,
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 << ")";
239
240   requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
241   Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
242                                          next_ipc_id_++,
243                                          device_id,
244                                          type,
245                                          security_origin));
246 }
247
248 void MediaStreamDispatcher::CancelOpenDevice(
249     int request_id,
250     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
251   CancelGenerateStream(request_id, event_handler);
252 }
253
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 << "}";
259
260   LabelStreamMap::iterator it = label_stream_map_.find(label);
261   if (it == label_stream_map_.end())
262     return;
263   label_stream_map_.erase(it);
264
265   Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
266 }
267
268 bool MediaStreamDispatcher::Send(IPC::Message* message) {
269   if (!RenderThread::Get()) {
270     delete message;
271     return false;
272   }
273
274   return RenderThread::Get()->Send(message);
275 }
276
277 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
278   bool handled = true;
279   IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
280     IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
281                         OnStreamGenerated)
282     IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
283                         OnStreamGenerationFailed)
284     IPC_MESSAGE_HANDLER(MediaStreamMsg_StopGeneratedStream,
285                         OnStopGeneratedStream)
286     IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
287                         OnDevicesEnumerated)
288     IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerationFailed,
289                         OnDevicesEnumerationFailed)
290     IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
291                         OnDeviceOpened)
292     IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
293                         OnDeviceOpenFailed)
294     IPC_MESSAGE_UNHANDLED(handled = false)
295   IPC_END_MESSAGE_MAP()
296   return handled;
297 }
298
299 void MediaStreamDispatcher::OnStreamGenerated(
300     int request_id,
301     const std::string& label,
302     const StreamDeviceInfoArray& audio_array,
303     const StreamDeviceInfoArray& video_array) {
304   DCHECK(main_loop_->BelongsToCurrentThread());
305
306   for (RequestList::iterator it = requests_.begin();
307        it != requests_.end(); ++it) {
308     Request& request = *it;
309     if (request.ipc_request == request_id) {
310       Stream new_stream;
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 << ")";
320       }
321       requests_.erase(it);
322       break;
323     }
324   }
325 }
326
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";
337       }
338       requests_.erase(it);
339       break;
340     }
341   }
342 }
343
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())
348     return;
349
350   if (it->second.handler.get()) {
351     it->second.handler->OnStopGeneratedStream(label);
352     DVLOG(1) << "MediaStreamDispatcher::OnStopGeneratedStream("
353              << label << ")\n";
354   }
355   label_stream_map_.erase(it);
356 }
357
358 void MediaStreamDispatcher::OnDevicesEnumerated(
359     int request_id,
360     const std::string& label,
361     const StreamDeviceInfoArray& device_array) {
362   DCHECK(main_loop_->BelongsToCurrentThread());
363   DCHECK_GE(request_id, 0);
364
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_;
370   } else {
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));
376     return;
377   }
378
379   DCHECK(!label.empty());
380   state->cached_devices.reset(new EnumerationState::CachedDevices(
381       label, device_array));
382
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 << ")";
389     }
390   }
391 }
392
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";
403       }
404       requests_.erase(it);
405       break;
406     }
407   }
408 }
409
410 void MediaStreamDispatcher::OnDeviceOpened(
411     int request_id,
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) {
419       Stream new_stream;
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);
425       } else {
426         NOTREACHED();
427       }
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 << ")";
433       }
434       requests_.erase(it);
435       break;
436     }
437   }
438 }
439
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";
450       }
451       requests_.erase(it);
452       break;
453     }
454   }
455 }
456
457 int MediaStreamDispatcher::audio_session_id(const std::string& label,
458                                             int index) {
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;
463   }
464   return it->second.audio_array[index].session_id;
465 }
466
467 bool MediaStreamDispatcher::IsStream(const std::string& label) {
468   return label_stream_map_.find(label) != label_stream_map_.end();
469 }
470
471 int MediaStreamDispatcher::video_session_id(const std::string& label,
472                                             int index) {
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;
477   }
478   return it->second.video_array[index].session_id;
479 }
480
481 }  // namespace content