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
15namespace content {
16
17namespace {
18
19bool 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.
38struct 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
56struct MediaStreamDispatcher::Stream {
57  Stream() {}
58  ~Stream() {}
59  base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
60  StreamDeviceInfoArray audio_array;
61  StreamDeviceInfoArray video_array;
62};
63
64MediaStreamDispatcher::MediaStreamDispatcher(RenderFrame* render_frame)
65    : RenderFrameObserver(render_frame),
66      next_ipc_id_(0) {
67}
68
69MediaStreamDispatcher::~MediaStreamDispatcher() {}
70
71void 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
85void 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
104void 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  DCHECK(device_found);
127
128  Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
129                                               device_info.device.id));
130}
131
132void MediaStreamDispatcher::EnumerateDevices(
133    int request_id,
134    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
135    MediaStreamType type,
136    const GURL& security_origin) {
137  DCHECK(thread_checker_.CalledOnValidThread());
138  DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
139         type == MEDIA_DEVICE_VIDEO_CAPTURE ||
140         type == MEDIA_DEVICE_AUDIO_OUTPUT);
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
156void MediaStreamDispatcher::StopEnumerateDevices(
157    int request_id,
158    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
159  DCHECK(thread_checker_.CalledOnValidThread());
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
173void 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(thread_checker_.CalledOnValidThread());
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
190void MediaStreamDispatcher::CancelOpenDevice(
191    int request_id,
192    const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
193  CancelGenerateStream(request_id, event_handler);
194}
195
196void MediaStreamDispatcher::CloseDevice(const std::string& label) {
197  DCHECK(thread_checker_.CalledOnValidThread());
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
210void MediaStreamDispatcher::OnDestruct() {
211  // Do not self-destruct. UserMediaClientImpl owns |this|.
212}
213
214bool MediaStreamDispatcher::Send(IPC::Message* message) {
215  if (!RenderThread::Get()) {
216    delete message;
217    return false;
218  }
219
220  return RenderThread::Get()->Send(message);
221}
222
223bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
224  bool handled = true;
225  IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
226    IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
227                        OnStreamGenerated)
228    IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
229                        OnStreamGenerationFailed)
230    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
231                        OnDeviceStopped)
232    IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
233                        OnDevicesEnumerated)
234    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
235                        OnDeviceOpened)
236    IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
237                        OnDeviceOpenFailed)
238    IPC_MESSAGE_UNHANDLED(handled = false)
239  IPC_END_MESSAGE_MAP()
240  return handled;
241}
242
243void MediaStreamDispatcher::OnStreamGenerated(
244    int request_id,
245    const std::string& label,
246    const StreamDeviceInfoArray& audio_array,
247    const StreamDeviceInfoArray& video_array) {
248  DCHECK(thread_checker_.CalledOnValidThread());
249
250  for (RequestList::iterator it = requests_.begin();
251       it != requests_.end(); ++it) {
252    Request& request = *it;
253    if (request.ipc_request == request_id) {
254      Stream new_stream;
255      new_stream.handler = request.handler;
256      new_stream.audio_array = audio_array;
257      new_stream.video_array = video_array;
258      label_stream_map_[label] = new_stream;
259      if (request.handler.get()) {
260        request.handler->OnStreamGenerated(
261            request.request_id, label, audio_array, video_array);
262        DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
263                 << request.request_id << ", " << label << ")";
264      }
265      requests_.erase(it);
266      break;
267    }
268  }
269}
270
271void MediaStreamDispatcher::OnStreamGenerationFailed(
272    int request_id,
273    content::MediaStreamRequestResult result) {
274  DCHECK(thread_checker_.CalledOnValidThread());
275  for (RequestList::iterator it = requests_.begin();
276       it != requests_.end(); ++it) {
277    Request& request = *it;
278    if (request.ipc_request == request_id) {
279      if (request.handler.get()) {
280        request.handler->OnStreamGenerationFailed(request.request_id, result);
281        DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
282                 << request.request_id << ")\n";
283      }
284      requests_.erase(it);
285      break;
286    }
287  }
288}
289
290void MediaStreamDispatcher::OnDeviceStopped(
291    const std::string& label,
292    const StreamDeviceInfo& device_info) {
293  DCHECK(thread_checker_.CalledOnValidThread());
294  DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
295           << "{label = " << label << "})"
296           << ", {device_id = " << device_info.device.id << "})";
297
298  LabelStreamMap::iterator it = label_stream_map_.find(label);
299  if (it == label_stream_map_.end()) {
300    // This can happen if a user happen stop a the device from JS at the same
301    // time as the underlying media device is unplugged from the system.
302    return;
303  }
304  Stream* stream = &it->second;
305  if (IsAudioInputMediaType(device_info.device.type))
306    RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
307  else
308    RemoveStreamDeviceFromArray(device_info, &stream->video_array);
309
310  if (stream->handler.get())
311    stream->handler->OnDeviceStopped(label, device_info);
312
313  if (stream->audio_array.empty() && stream->video_array.empty())
314    label_stream_map_.erase(it);
315}
316
317void MediaStreamDispatcher::OnDevicesEnumerated(
318    int request_id,
319    const StreamDeviceInfoArray& device_array) {
320  DCHECK(thread_checker_.CalledOnValidThread());
321  DCHECK_GE(request_id, 0);
322
323  for (RequestList::iterator it = requests_.begin(); it != requests_.end();
324       ++it) {
325    if (it->ipc_request == request_id && it->handler.get()) {
326      it->handler->OnDevicesEnumerated(it->request_id, device_array);
327      break;
328    }
329  }
330}
331
332void MediaStreamDispatcher::OnDeviceOpened(
333    int request_id,
334    const std::string& label,
335    const StreamDeviceInfo& device_info) {
336  DCHECK(thread_checker_.CalledOnValidThread());
337  for (RequestList::iterator it = requests_.begin();
338       it != requests_.end(); ++it) {
339    Request& request = *it;
340    if (request.ipc_request == request_id) {
341      Stream new_stream;
342      new_stream.handler = request.handler;
343      if (IsAudioInputMediaType(device_info.device.type)) {
344        new_stream.audio_array.push_back(device_info);
345      } else if (IsVideoMediaType(device_info.device.type)) {
346        new_stream.video_array.push_back(device_info);
347      } else {
348        NOTREACHED();
349      }
350      label_stream_map_[label] = new_stream;
351      if (request.handler.get()) {
352        request.handler->OnDeviceOpened(request.request_id, label, device_info);
353        DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
354                 << request.request_id << ", " << label << ")";
355      }
356      requests_.erase(it);
357      break;
358    }
359  }
360}
361
362void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
363  DCHECK(thread_checker_.CalledOnValidThread());
364  for (RequestList::iterator it = requests_.begin();
365       it != requests_.end(); ++it) {
366    Request& request = *it;
367    if (request.ipc_request == request_id) {
368      if (request.handler.get()) {
369        request.handler->OnDeviceOpenFailed(request.request_id);
370        DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
371                 << request.request_id << ")\n";
372      }
373      requests_.erase(it);
374      break;
375    }
376  }
377}
378
379int MediaStreamDispatcher::audio_session_id(const std::string& label,
380                                            int index) {
381  DCHECK(thread_checker_.CalledOnValidThread());
382  LabelStreamMap::iterator it = label_stream_map_.find(label);
383  if (it == label_stream_map_.end() ||
384      it->second.audio_array.size() <= static_cast<size_t>(index)) {
385    return StreamDeviceInfo::kNoId;
386  }
387  return it->second.audio_array[index].session_id;
388}
389
390bool MediaStreamDispatcher::IsStream(const std::string& label) {
391  DCHECK(thread_checker_.CalledOnValidThread());
392  return label_stream_map_.find(label) != label_stream_map_.end();
393}
394
395int MediaStreamDispatcher::video_session_id(const std::string& label,
396                                            int index) {
397  DCHECK(thread_checker_.CalledOnValidThread());
398  LabelStreamMap::iterator it = label_stream_map_.find(label);
399  if (it == label_stream_map_.end() ||
400      it->second.video_array.size() <= static_cast<size_t>(index)) {
401    return StreamDeviceInfo::kNoId;
402  }
403  return it->second.video_array[index].session_id;
404}
405
406bool MediaStreamDispatcher::IsAudioDuckingActive() const {
407  DCHECK(thread_checker_.CalledOnValidThread());
408  LabelStreamMap::const_iterator stream_it = label_stream_map_.begin();
409  while (stream_it != label_stream_map_.end()) {
410    const StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
411    for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin();
412         device_it != audio_array.end(); ++device_it) {
413      if (device_it->device.input.effects & media::AudioParameters::DUCKING)
414        return true;
415    }
416    ++stream_it;
417  }
418  return false;
419}
420
421}  // namespace content
422