Upstream version 9.38.198.0
[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 "content/common/media/media_stream_messages.h"
9 #include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
10 #include "content/renderer/render_thread_impl.h"
11 #include "media/audio/audio_parameters.h"
12 #include "third_party/WebKit/public/web/WebUserGestureIndicator.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::MediaStreamDispatcher(RenderFrame* render_frame)
65     : RenderFrameObserver(render_frame),
66       next_ipc_id_(0) {
67 }
68
69 MediaStreamDispatcher::~MediaStreamDispatcher() {}
70
71 void MediaStreamDispatcher::GenerateStream(
72     int request_id,
73     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
74     const StreamOptions& components,
75     const GURL& security_origin) {
76   DCHECK(thread_checker_.CalledOnValidThread());
77   DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")";
78
79   requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
80   Send(new MediaStreamHostMsg_GenerateStream(
81       routing_id(), next_ipc_id_++, components, security_origin,
82       blink::WebUserGestureIndicator::isProcessingUserGesture()));
83 }
84
85 void MediaStreamDispatcher::CancelGenerateStream(
86     int request_id,
87     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
88   DCHECK(thread_checker_.CalledOnValidThread());
89   DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
90            << ", {request_id = " << request_id << "}";
91
92   RequestList::iterator it = requests_.begin();
93   for (; it != requests_.end(); ++it) {
94     if (it->IsThisRequest(request_id, event_handler)) {
95       int ipc_request = it->ipc_request;
96       requests_.erase(it);
97       Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
98                                                        ipc_request));
99       break;
100     }
101   }
102 }
103
104 void MediaStreamDispatcher::StopStreamDevice(
105     const StreamDeviceInfo& device_info) {
106   DCHECK(thread_checker_.CalledOnValidThread());
107   DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
108            << ", {device_id = " << device_info.device.id << "}";
109   // Remove |device_info| from all streams in |label_stream_map_|.
110   bool device_found = false;
111   LabelStreamMap::iterator stream_it = label_stream_map_.begin();
112   while (stream_it != label_stream_map_.end()) {
113     StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
114     StreamDeviceInfoArray& video_array = stream_it->second.video_array;
115
116     if (RemoveStreamDeviceFromArray(device_info, &audio_array) ||
117         RemoveStreamDeviceFromArray(device_info, &video_array)) {
118       device_found = true;
119       if (audio_array.empty() && video_array.empty()) {
120         label_stream_map_.erase(stream_it++);
121         continue;
122       }
123     }
124     ++stream_it;
125   }
126
127   if (!device_found) {
128     // TODO(perkj): Revisit this. It used to be true (but isn't anymore) that
129     // there was one MediaStreamDispatcher per RenderView, but one
130     // MediaStreamImpl per RenderFrame. Now both MediaStreamDispatcher and
131     // MediaStreamImpl are 1:1 per RenderFrame. http://crbug/368030.
132     return;
133   }
134
135   Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
136                                                device_info.device.id));
137 }
138
139 void MediaStreamDispatcher::EnumerateDevices(
140     int request_id,
141     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
142     MediaStreamType type,
143     const GURL& security_origin) {
144   DCHECK(thread_checker_.CalledOnValidThread());
145   DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
146          type == MEDIA_DEVICE_VIDEO_CAPTURE ||
147          type == MEDIA_DEVICE_AUDIO_OUTPUT);
148   DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
149            << request_id << ")";
150
151   for (RequestList::iterator it = requests_.begin(); it != requests_.end();
152        ++it) {
153     DCHECK(!it->IsThisRequest(request_id, event_handler));
154   }
155
156   requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
157   Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
158                                                next_ipc_id_++,
159                                                type,
160                                                security_origin));
161 }
162
163 void MediaStreamDispatcher::StopEnumerateDevices(
164     int request_id,
165     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
166   DCHECK(thread_checker_.CalledOnValidThread());
167   DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
168            << request_id << ")";
169   for (RequestList::iterator it = requests_.begin(); it != requests_.end();
170        ++it) {
171     if (it->IsThisRequest(request_id, event_handler)) {
172       Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
173                                                          it->ipc_request));
174       requests_.erase(it);
175       break;
176     }
177   }
178 }
179
180 void MediaStreamDispatcher::OpenDevice(
181     int request_id,
182     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
183     const std::string& device_id,
184     MediaStreamType type,
185     const GURL& security_origin) {
186   DCHECK(thread_checker_.CalledOnValidThread());
187   DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";
188
189   requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
190   Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
191                                          next_ipc_id_++,
192                                          device_id,
193                                          type,
194                                          security_origin));
195 }
196
197 void MediaStreamDispatcher::CancelOpenDevice(
198     int request_id,
199     const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
200   CancelGenerateStream(request_id, event_handler);
201 }
202
203 void MediaStreamDispatcher::CloseDevice(const std::string& label) {
204   DCHECK(thread_checker_.CalledOnValidThread());
205   DCHECK(!label.empty());
206   DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
207            << ", {label = " << label << "}";
208
209   LabelStreamMap::iterator it = label_stream_map_.find(label);
210   if (it == label_stream_map_.end())
211     return;
212   label_stream_map_.erase(it);
213
214   Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
215 }
216
217 void MediaStreamDispatcher::OnDestruct() {
218   // Do not self-destruct.  MediaStreamImpl owns |this|.
219 }
220
221 bool MediaStreamDispatcher::Send(IPC::Message* message) {
222   if (!RenderThread::Get()) {
223     delete message;
224     return false;
225   }
226
227   return RenderThread::Get()->Send(message);
228 }
229
230 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
231   bool handled = true;
232   IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
233     IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
234                         OnStreamGenerated)
235     IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
236                         OnStreamGenerationFailed)
237     IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
238                         OnDeviceStopped)
239     IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
240                         OnDevicesEnumerated)
241     IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
242                         OnDeviceOpened)
243     IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
244                         OnDeviceOpenFailed)
245     IPC_MESSAGE_UNHANDLED(handled = false)
246   IPC_END_MESSAGE_MAP()
247   return handled;
248 }
249
250 void MediaStreamDispatcher::OnStreamGenerated(
251     int request_id,
252     const std::string& label,
253     const StreamDeviceInfoArray& audio_array,
254     const StreamDeviceInfoArray& video_array) {
255   DCHECK(thread_checker_.CalledOnValidThread());
256
257   for (RequestList::iterator it = requests_.begin();
258        it != requests_.end(); ++it) {
259     Request& request = *it;
260     if (request.ipc_request == request_id) {
261       Stream new_stream;
262       new_stream.handler = request.handler;
263       new_stream.audio_array = audio_array;
264       new_stream.video_array = video_array;
265       label_stream_map_[label] = new_stream;
266       if (request.handler.get()) {
267         request.handler->OnStreamGenerated(
268             request.request_id, label, audio_array, video_array);
269         DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
270                  << request.request_id << ", " << label << ")";
271       }
272       requests_.erase(it);
273       break;
274     }
275   }
276 }
277
278 void MediaStreamDispatcher::OnStreamGenerationFailed(
279     int request_id,
280     content::MediaStreamRequestResult result) {
281   DCHECK(thread_checker_.CalledOnValidThread());
282   for (RequestList::iterator it = requests_.begin();
283        it != requests_.end(); ++it) {
284     Request& request = *it;
285     if (request.ipc_request == request_id) {
286       if (request.handler.get()) {
287         request.handler->OnStreamGenerationFailed(request.request_id, result);
288         DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
289                  << request.request_id << ")\n";
290       }
291       requests_.erase(it);
292       break;
293     }
294   }
295 }
296
297 void MediaStreamDispatcher::OnDeviceStopped(
298     const std::string& label,
299     const StreamDeviceInfo& device_info) {
300   DCHECK(thread_checker_.CalledOnValidThread());
301   DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
302            << "{label = " << label << "})"
303            << ", {device_id = " << device_info.device.id << "})";
304
305   LabelStreamMap::iterator it = label_stream_map_.find(label);
306   if (it == label_stream_map_.end()) {
307     // This can happen if a user happen stop a the device from JS at the same
308     // time as the underlying media device is unplugged from the system.
309     return;
310   }
311   Stream* stream = &it->second;
312   if (IsAudioInputMediaType(device_info.device.type))
313     RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
314   else
315     RemoveStreamDeviceFromArray(device_info, &stream->video_array);
316
317   if (stream->handler.get())
318     stream->handler->OnDeviceStopped(label, device_info);
319
320   if (stream->audio_array.empty() && stream->video_array.empty())
321     label_stream_map_.erase(it);
322 }
323
324 void MediaStreamDispatcher::OnDevicesEnumerated(
325     int request_id,
326     const StreamDeviceInfoArray& device_array) {
327   DCHECK(thread_checker_.CalledOnValidThread());
328   DCHECK_GE(request_id, 0);
329
330   for (RequestList::iterator it = requests_.begin(); it != requests_.end();
331        ++it) {
332     if (it->ipc_request == request_id && it->handler.get()) {
333       it->handler->OnDevicesEnumerated(it->request_id, device_array);
334       break;
335     }
336   }
337 }
338
339 void MediaStreamDispatcher::OnDeviceOpened(
340     int request_id,
341     const std::string& label,
342     const StreamDeviceInfo& device_info) {
343   DCHECK(thread_checker_.CalledOnValidThread());
344   for (RequestList::iterator it = requests_.begin();
345        it != requests_.end(); ++it) {
346     Request& request = *it;
347     if (request.ipc_request == request_id) {
348       Stream new_stream;
349       new_stream.handler = request.handler;
350       if (IsAudioInputMediaType(device_info.device.type)) {
351         new_stream.audio_array.push_back(device_info);
352       } else if (IsVideoMediaType(device_info.device.type)) {
353         new_stream.video_array.push_back(device_info);
354       } else {
355         NOTREACHED();
356       }
357       label_stream_map_[label] = new_stream;
358       if (request.handler.get()) {
359         request.handler->OnDeviceOpened(request.request_id, label, device_info);
360         DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
361                  << request.request_id << ", " << label << ")";
362       }
363       requests_.erase(it);
364       break;
365     }
366   }
367 }
368
369 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
370   DCHECK(thread_checker_.CalledOnValidThread());
371   for (RequestList::iterator it = requests_.begin();
372        it != requests_.end(); ++it) {
373     Request& request = *it;
374     if (request.ipc_request == request_id) {
375       if (request.handler.get()) {
376         request.handler->OnDeviceOpenFailed(request.request_id);
377         DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
378                  << request.request_id << ")\n";
379       }
380       requests_.erase(it);
381       break;
382     }
383   }
384 }
385
386 int MediaStreamDispatcher::audio_session_id(const std::string& label,
387                                             int index) {
388   DCHECK(thread_checker_.CalledOnValidThread());
389   LabelStreamMap::iterator it = label_stream_map_.find(label);
390   if (it == label_stream_map_.end() ||
391       it->second.audio_array.size() <= static_cast<size_t>(index)) {
392     return StreamDeviceInfo::kNoId;
393   }
394   return it->second.audio_array[index].session_id;
395 }
396
397 bool MediaStreamDispatcher::IsStream(const std::string& label) {
398   DCHECK(thread_checker_.CalledOnValidThread());
399   return label_stream_map_.find(label) != label_stream_map_.end();
400 }
401
402 int MediaStreamDispatcher::video_session_id(const std::string& label,
403                                             int index) {
404   DCHECK(thread_checker_.CalledOnValidThread());
405   LabelStreamMap::iterator it = label_stream_map_.find(label);
406   if (it == label_stream_map_.end() ||
407       it->second.video_array.size() <= static_cast<size_t>(index)) {
408     return StreamDeviceInfo::kNoId;
409   }
410   return it->second.video_array[index].session_id;
411 }
412
413 bool MediaStreamDispatcher::IsAudioDuckingActive() const {
414   DCHECK(thread_checker_.CalledOnValidThread());
415   LabelStreamMap::const_iterator stream_it = label_stream_map_.begin();
416   while (stream_it != label_stream_map_.end()) {
417     const StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
418     for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin();
419          device_it != audio_array.end(); ++device_it) {
420       if (device_it->device.input.effects & media::AudioParameters::DUCKING)
421         return true;
422     }
423     ++stream_it;
424   }
425   return false;
426 }
427
428 }  // namespace content