media_stream_manager.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/browser/renderer_host/media/media_stream_manager.h"
6
7#include <list>
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/compiler_specific.h"
12#include "base/logging.h"
13#include "base/rand_util.h"
14#include "base/threading/thread.h"
15#include "content/browser/renderer_host/media/audio_input_device_manager.h"
16#include "content/browser/renderer_host/media/media_stream_requester.h"
17#include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
18#include "content/browser/renderer_host/media/video_capture_manager.h"
19#include "content/browser/renderer_host/media/web_contents_capture_util.h"
20#include "content/public/browser/browser_thread.h"
21#include "content/public/browser/content_browser_client.h"
22#include "content/public/browser/media_observer.h"
23#include "content/public/browser/media_request_state.h"
24#include "content/public/common/content_switches.h"
25#include "content/public/common/media_stream_request.h"
26#include "googleurl/src/gurl.h"
27#include "media/audio/audio_manager_base.h"
28#include "media/audio/audio_parameters.h"
29#include "media/base/channel_layout.h"
30
31#if defined(OS_WIN)
32#include "base/win/scoped_com_initializer.h"
33#endif
34
35namespace content {
36
37// Creates a random label used to identify requests.
38static std::string RandomLabel() {
39  // An earlier PeerConnection spec,
40  // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
41  // MediaStream::label alphabet as containing 36 characters from
42  // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
43  // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
44  // Here we use a safe subset.
45  static const char kAlphabet[] = "0123456789"
46      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
47
48  std::string label(36, ' ');
49  for (size_t i = 0; i < label.size(); ++i) {
50    int random_char = base::RandGenerator(sizeof(kAlphabet) - 1);
51    label[i] = kAlphabet[random_char];
52  }
53  return label;
54}
55
56// Helper to verify if a media stream type is part of options or not.
57static bool Requested(const MediaStreamRequest& request,
58                      MediaStreamType stream_type) {
59  return (request.audio_type == stream_type ||
60          request.video_type == stream_type);
61}
62
63// TODO(xians): Merge DeviceRequest with MediaStreamRequest.
64class MediaStreamManager::DeviceRequest {
65 public:
66  DeviceRequest(MediaStreamRequester* requester,
67                const MediaStreamRequest& request)
68      : requester(requester),
69        request(request),
70        state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) {
71  }
72
73  ~DeviceRequest() {}
74
75  // Update the request state and notify observers.
76  void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
77    state_[stream_type] = new_state;
78
79    if (request.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
80        request.audio_type != MEDIA_TAB_AUDIO_CAPTURE) {
81      return;
82    }
83
84    MediaObserver* media_observer =
85        GetContentClient()->browser()->GetMediaObserver();
86    if (media_observer == NULL)
87      return;
88
89    // If we appended a device_id scheme, we want to remove it when notifying
90    // observers which may be in different modules since this scheme is only
91    // used internally within the content module.
92    std::string device_id =
93        WebContentsCaptureUtil::StripWebContentsDeviceScheme(
94            request.requested_device_id);
95
96    media_observer->OnMediaRequestStateChanged(
97        request.render_process_id, request.render_view_id,
98        MediaStreamDevice(stream_type, device_id, device_id), new_state);
99  }
100
101  MediaRequestState state(MediaStreamType stream_type) const {
102    return state_[stream_type];
103  }
104
105  MediaStreamRequester* const requester;  // Can be NULL.
106  MediaStreamRequest request;
107
108  StreamDeviceInfoArray devices;
109
110  // Callback to the requester which audio/video devices have been selected.
111  // It can be null if the requester has no interest to know the result.
112  // Currently it is only used by |DEVICE_ACCESS| type.
113  MediaStreamManager::MediaRequestResponseCallback callback;
114
115  scoped_ptr<MediaStreamUIProxy> ui_proxy;
116
117 private:
118  std::vector<MediaRequestState> state_;
119};
120
121MediaStreamManager::EnumerationCache::EnumerationCache()
122    : valid(false) {
123}
124
125MediaStreamManager::EnumerationCache::~EnumerationCache() {
126}
127
128MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
129    : audio_manager_(audio_manager),
130      monitoring_started_(false),
131      io_loop_(NULL),
132      screen_capture_active_(false),
133      use_fake_ui_(false) {
134  DCHECK(audio_manager_);
135  memset(active_enumeration_ref_count_, 0,
136         sizeof(active_enumeration_ref_count_));
137
138  // Some unit tests create the MSM in the IO thread and assumes the
139  // initialization is done synchronously.
140  if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
141    InitializeDeviceManagersOnIOThread();
142  } else {
143    BrowserThread::PostTask(
144        BrowserThread::IO, FROM_HERE,
145        base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
146                   base::Unretained(this)));
147  }
148}
149
150MediaStreamManager::~MediaStreamManager() {
151  DCHECK(requests_.empty());
152  DCHECK(!device_thread_.get());
153  DCHECK(!io_loop_);
154}
155
156VideoCaptureManager* MediaStreamManager::video_capture_manager() {
157  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
158  DCHECK(video_capture_manager_.get());
159  return video_capture_manager_.get();
160}
161
162AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
163  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
164  DCHECK(audio_input_device_manager_.get());
165  return audio_input_device_manager_.get();
166}
167
168std::string MediaStreamManager::MakeMediaAccessRequest(
169    int render_process_id,
170    int render_view_id,
171    const StreamOptions& options,
172    const GURL& security_origin,
173    const MediaRequestResponseCallback& callback) {
174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
175  // Create a new request based on options.
176  MediaStreamRequest stream_request(
177      render_process_id, render_view_id, security_origin,
178      MEDIA_DEVICE_ACCESS, std::string(),
179      options.audio_type, options.video_type);
180  DeviceRequest* request = new DeviceRequest(NULL, stream_request);
181  const std::string& label = AddRequest(request);
182
183  request->callback = callback;
184
185  HandleRequest(label);
186
187  return label;
188}
189
190std::string MediaStreamManager::GenerateStream(
191    MediaStreamRequester* requester,
192    int render_process_id,
193    int render_view_id,
194    const StreamOptions& options,
195    const GURL& security_origin) {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
197  if (CommandLine::ForCurrentProcess()->HasSwitch(
198          switches::kUseFakeDeviceForMediaStream)) {
199    UseFakeDevice();
200  }
201
202  int target_render_process_id = render_process_id;
203  int target_render_view_id = render_view_id;
204  std::string requested_device_id;
205
206  // Customize options for a WebContents based capture.
207  if (options.audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
208      options.video_type == MEDIA_TAB_VIDEO_CAPTURE) {
209    // TODO(justinlin): Can't plumb audio mirroring using stream type right
210    // now, so plumbing by device_id. Will revisit once it's refactored.
211    // http://crbug.com/163100
212    requested_device_id =
213        WebContentsCaptureUtil::AppendWebContentsDeviceScheme(
214            !options.video_device_id.empty() ?
215            options.video_device_id : options.audio_device_id);
216
217    bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
218        requested_device_id, &target_render_process_id, &target_render_view_id);
219    if (!has_valid_device_id ||
220        (options.audio_type != MEDIA_TAB_AUDIO_CAPTURE &&
221         options.audio_type != MEDIA_NO_SERVICE) ||
222        (options.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
223         options.video_type != MEDIA_NO_SERVICE)) {
224      LOG(ERROR) << "Invalid request.";
225      return std::string();
226    }
227  }
228
229  if (options.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) {
230    if (options.audio_type != MEDIA_NO_SERVICE) {
231      // TODO(sergeyu): Surface error message to the calling JS code.
232      LOG(ERROR) << "Audio is not supported for screen capture streams.";
233      return std::string();
234    }
235
236    if (screen_capture_active_) {
237      // TODO(sergeyu): Implement support for more than one concurrent screen
238      // capture streams.
239      LOG(ERROR) << "Another screen capture stream is active.";
240      return std::string();
241    }
242
243    screen_capture_active_ = true;
244  }
245
246  // Create a new request based on options.
247  MediaStreamRequest stream_request(
248      target_render_process_id, target_render_view_id, security_origin,
249      MEDIA_GENERATE_STREAM, requested_device_id,
250      options.audio_type, options.video_type);
251  DeviceRequest* request = new DeviceRequest(requester, stream_request);
252  const std::string& label = AddRequest(request);
253  HandleRequest(label);
254  return label;
255}
256
257void MediaStreamManager::CancelRequest(const std::string& label) {
258  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
259
260  DeviceRequests::iterator it = requests_.find(label);
261  if (it != requests_.end()) {
262    if (!RequestDone(*it->second)) {
263      // TODO(xians): update the |state| to STATE_DONE to trigger a state
264      // changed notification to UI before deleting the request?
265      scoped_ptr<DeviceRequest> request(it->second);
266      RemoveRequest(it);
267      for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
268        const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
269        MediaStreamProvider* device_manager = GetDeviceManager(stream_type);
270        if (!device_manager)
271          continue;
272        if (request->state(stream_type) != MEDIA_REQUEST_STATE_OPENING) {
273          continue;
274        }
275        for (StreamDeviceInfoArray::const_iterator device_it =
276                 request->devices.begin();
277             device_it != request->devices.end(); ++device_it) {
278          if (device_it->device.type == stream_type) {
279            device_manager->Close(device_it->session_id);
280          }
281        }
282      }
283    } else {
284      StopGeneratedStream(label);
285    }
286  }
287}
288
289void MediaStreamManager::StopGeneratedStream(const std::string& label) {
290  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
291
292  // Find the request and close all open devices for the request.
293  DeviceRequests::iterator it = requests_.find(label);
294  if (it != requests_.end()) {
295    if (it->second->request.request_type == MEDIA_ENUMERATE_DEVICES) {
296      StopEnumerateDevices(label);
297      return;
298    }
299
300    scoped_ptr<DeviceRequest> request(it->second);
301    RemoveRequest(it);
302    for (StreamDeviceInfoArray::const_iterator device_it =
303             request->devices.begin();
304         device_it != request->devices.end(); ++device_it) {
305      GetDeviceManager(device_it->device.type)->Close(device_it->session_id);
306    }
307    if (request->request.request_type == MEDIA_GENERATE_STREAM &&
308        RequestDone(*request)) {
309      // Notify observers that this device is being closed.
310      for (int i = MEDIA_NO_SERVICE + 1; i != NUM_MEDIA_TYPES; ++i) {
311        if (request->state(static_cast<MediaStreamType>(i)) !=
312            MEDIA_REQUEST_STATE_NOT_REQUESTED) {
313          request->SetState(static_cast<MediaStreamType>(i),
314                            MEDIA_REQUEST_STATE_CLOSING);
315        }
316      }
317    }
318  }
319}
320
321std::string MediaStreamManager::EnumerateDevices(
322    MediaStreamRequester* requester,
323    int render_process_id,
324    int render_view_id,
325    MediaStreamType type,
326    const GURL& security_origin) {
327  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
328  DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
329         type == MEDIA_DEVICE_VIDEO_CAPTURE);
330
331  // When the requester is NULL, the request is made by the UI to ensure MSM
332  // starts monitoring devices.
333  if (!requester) {
334    if (!monitoring_started_)
335      StartMonitoring();
336
337    return std::string();
338  }
339
340  // Create a new request.
341  StreamOptions options;
342  EnumerationCache* cache = NULL;
343  if (type == MEDIA_DEVICE_AUDIO_CAPTURE) {
344    options.audio_type = type;
345    cache = &audio_enumeration_cache_;
346  } else if (type == MEDIA_DEVICE_VIDEO_CAPTURE) {
347    options.video_type = type;
348    cache = &video_enumeration_cache_;
349  } else {
350    NOTREACHED();
351    return std::string();
352  }
353
354  MediaStreamRequest stream_request(
355      render_process_id, render_view_id, security_origin,
356      MEDIA_ENUMERATE_DEVICES, std::string(),
357      options.audio_type, options.video_type);
358  DeviceRequest* request = new DeviceRequest(requester, stream_request);
359  const std::string& label = AddRequest(request);
360
361  if (cache->valid) {
362    // Cached device list of this type exists. Just send it out.
363    request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
364
365    // Need to post a task since the requester won't have label till
366    // this function returns.
367    BrowserThread::PostTask(
368        BrowserThread::IO, FROM_HERE,
369        base::Bind(&MediaStreamManager::SendCachedDeviceList,
370                   base::Unretained(this), cache, label));
371  } else {
372    StartEnumeration(request);
373  }
374
375  return label;
376}
377
378void MediaStreamManager::StopEnumerateDevices(const std::string& label) {
379  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
380
381  DeviceRequests::iterator it = requests_.find(label);
382  if (it != requests_.end()) {
383    DCHECK_EQ(it->second->request.request_type, MEDIA_ENUMERATE_DEVICES);
384    // Delete the DeviceRequest.
385    scoped_ptr<DeviceRequest> request(it->second);
386    RemoveRequest(it);
387  }
388}
389
390std::string MediaStreamManager::OpenDevice(
391    MediaStreamRequester* requester,
392    int render_process_id,
393    int render_view_id,
394    const std::string& device_id,
395    MediaStreamType type,
396    const GURL& security_origin) {
397  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398  DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
399         type == MEDIA_DEVICE_VIDEO_CAPTURE);
400
401  // Create a new request.
402  StreamOptions options;
403  if (IsAudioMediaType(type)) {
404    options.audio_type = type;
405  } else if (IsVideoMediaType(type)) {
406    options.video_type = type;
407  } else {
408    NOTREACHED();
409    return std::string();
410  }
411
412  MediaStreamRequest stream_request(
413      render_process_id, render_view_id, security_origin,
414      MEDIA_OPEN_DEVICE, device_id,
415      options.audio_type, options.video_type);
416  DeviceRequest* request = new DeviceRequest(requester, stream_request);
417  const std::string& label = AddRequest(request);
418  StartEnumeration(request);
419
420  return label;
421}
422
423void MediaStreamManager::SendCachedDeviceList(
424    EnumerationCache* cache,
425    const std::string& label) {
426  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
427  if (cache->valid) {
428    DeviceRequests::iterator it = requests_.find(label);
429    if (it != requests_.end()) {
430      it->second->requester->DevicesEnumerated(label, cache->devices);
431    }
432  }
433}
434
435void MediaStreamManager::StartMonitoring() {
436  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
437  if (!base::SystemMonitor::Get())
438    return;
439
440  if (!monitoring_started_) {
441    monitoring_started_ = true;
442    base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
443
444    // Enumerate both the audio and video devices to cache the device lists
445    // and send them to media observer.
446    ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
447    audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
448    ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
449    video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
450  }
451}
452
453void MediaStreamManager::StopMonitoring() {
454  DCHECK_EQ(base::MessageLoop::current(), io_loop_);
455  if (monitoring_started_) {
456    base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
457    monitoring_started_ = false;
458    ClearEnumerationCache(&audio_enumeration_cache_);
459    ClearEnumerationCache(&video_enumeration_cache_);
460  }
461}
462
463void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
464  DCHECK_EQ(base::MessageLoop::current(), io_loop_);
465  cache->valid = false;
466}
467
468void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
469  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
470
471  // Start monitoring the devices when doing the first enumeration.
472  if (!monitoring_started_ && base::SystemMonitor::Get()) {
473    StartMonitoring();
474  }
475
476  // Start enumeration for devices of all requested device types.
477  for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
478    const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
479    if (Requested(request->request, stream_type)) {
480      request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
481      DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
482      if (active_enumeration_ref_count_[stream_type] == 0) {
483        ++active_enumeration_ref_count_[stream_type];
484        GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
485      }
486    }
487  }
488}
489
490std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
491  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
492
493  // Create a label for this request and verify it is unique.
494  std::string unique_label;
495  do {
496    unique_label = RandomLabel();
497  } while (requests_.find(unique_label) != requests_.end());
498
499  requests_.insert(std::make_pair(unique_label, request));
500
501  return unique_label;
502}
503
504void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it) {
505  if (it->second->request.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) {
506    DCHECK(screen_capture_active_);
507    screen_capture_active_ = false;
508  }
509
510  requests_.erase(it);
511}
512
513void MediaStreamManager::PostRequestToUI(const std::string& label) {
514  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
515  DeviceRequest* request = requests_[label];
516
517  if (use_fake_ui_) {
518    if (!fake_ui_)
519      fake_ui_.reset(new FakeMediaStreamUIProxy());
520
521    MediaStreamDevices devices;
522    if (audio_enumeration_cache_.valid) {
523      for (StreamDeviceInfoArray::const_iterator it =
524               audio_enumeration_cache_.devices.begin();
525           it != audio_enumeration_cache_.devices.end(); ++it) {
526        devices.push_back(it->device);
527      }
528    }
529    if (video_enumeration_cache_.valid) {
530      for (StreamDeviceInfoArray::const_iterator it =
531               video_enumeration_cache_.devices.begin();
532           it != video_enumeration_cache_.devices.end(); ++it) {
533        devices.push_back(it->device);
534      }
535    }
536
537    fake_ui_->SetAvailableDevices(devices);
538
539    request->ui_proxy = fake_ui_.Pass();
540  } else {
541    request->ui_proxy = MediaStreamUIProxy::Create();
542  }
543
544  request->ui_proxy->RequestAccess(
545      request->request,
546      base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
547                 base::Unretained(this), label));
548}
549
550void MediaStreamManager::HandleRequest(const std::string& label) {
551  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
552  DeviceRequest* request = requests_[label];
553
554  const MediaStreamType audio_type = request->request.audio_type;
555  const MediaStreamType video_type = request->request.video_type;
556
557  bool is_web_contents_capture =
558      audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
559      video_type == MEDIA_TAB_VIDEO_CAPTURE;
560
561  bool is_screen_capure =
562      video_type == MEDIA_SCREEN_VIDEO_CAPTURE;
563
564  if (!is_web_contents_capture &&
565      !is_screen_capure &&
566      ((IsAudioMediaType(audio_type) && !audio_enumeration_cache_.valid) ||
567       (IsVideoMediaType(video_type) && !video_enumeration_cache_.valid))) {
568    // Enumerate the devices if there is no valid device lists to be used.
569    StartEnumeration(request);
570    return;
571  }
572
573  // No need to do new device enumerations, post the request to UI
574  // immediately.
575  if (IsAudioMediaType(audio_type))
576    request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
577  if (IsVideoMediaType(video_type))
578    request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
579
580  PostRequestToUI(label);
581}
582
583void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
584  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
585  if (device_thread_)
586    return;
587
588  device_thread_.reset(new base::Thread("MediaStreamDeviceThread"));
589#if defined(OS_WIN)
590  device_thread_->init_com_with_mta(true);
591#endif
592  CHECK(device_thread_->Start());
593
594  audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
595  audio_input_device_manager_->Register(
596      this, device_thread_->message_loop_proxy().get());
597
598  video_capture_manager_ = new VideoCaptureManager();
599  video_capture_manager_->Register(this,
600                                   device_thread_->message_loop_proxy().get());
601
602  // We want to be notified of IO message loop destruction to delete the thread
603  // and the device managers.
604  io_loop_ = base::MessageLoop::current();
605  io_loop_->AddDestructionObserver(this);
606}
607
608void MediaStreamManager::Opened(MediaStreamType stream_type,
609                                int capture_session_id) {
610  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
611
612  // Find the request containing this device and mark it as used.
613  DeviceRequest* request = NULL;
614  StreamDeviceInfoArray* devices = NULL;
615  std::string label;
616  for (DeviceRequests::iterator request_it = requests_.begin();
617       request_it != requests_.end() && request == NULL; ++request_it) {
618    devices = &(request_it->second->devices);
619    for (StreamDeviceInfoArray::iterator device_it = devices->begin();
620         device_it != devices->end(); ++device_it) {
621      if (device_it->device.type == stream_type &&
622          device_it->session_id == capture_session_id) {
623        // We've found the request.
624        device_it->in_use = true;
625        label = request_it->first;
626        request = request_it->second;
627        break;
628      }
629    }
630  }
631  if (request == NULL) {
632    // The request doesn't exist.
633    return;
634  }
635
636  DCHECK_NE(request->state(stream_type), MEDIA_REQUEST_STATE_REQUESTED);
637
638  // Check if all devices for this stream type are opened. Update the state if
639  // they are.
640  for (StreamDeviceInfoArray::iterator device_it = devices->begin();
641       device_it != devices->end(); ++device_it) {
642    if (device_it->device.type != stream_type) {
643      continue;
644    }
645    if (device_it->in_use == false) {
646      // Wait for more devices to be opened before we're done.
647      return;
648    }
649  }
650
651  request->SetState(stream_type, MEDIA_REQUEST_STATE_DONE);
652
653  if (!RequestDone(*request)) {
654    // This stream_type is done, but not the other type.
655    return;
656  }
657
658  switch (request->request.request_type) {
659    case MEDIA_OPEN_DEVICE:
660      request->requester->DeviceOpened(label, devices->front());
661      break;
662    case MEDIA_GENERATE_STREAM: {
663      // Partition the array of devices into audio vs video.
664      StreamDeviceInfoArray audio_devices, video_devices;
665      for (StreamDeviceInfoArray::iterator device_it = devices->begin();
666           device_it != devices->end(); ++device_it) {
667        if (IsAudioMediaType(device_it->device.type)) {
668          // Store the native audio parameters in the device struct.
669          // TODO(xians): Handle the tab capture sample rate/channel layout
670          // in AudioInputDeviceManager::Open().
671          if (device_it->device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
672            const StreamDeviceInfo* info =
673                audio_input_device_manager_->GetOpenedDeviceInfoById(
674                    device_it->session_id);
675            DCHECK_EQ(info->device.id, device_it->device.id);
676            device_it->device.sample_rate = info->device.sample_rate;
677            device_it->device.channel_layout = info->device.channel_layout;
678          }
679          audio_devices.push_back(*device_it);
680        } else if (IsVideoMediaType(device_it->device.type)) {
681          video_devices.push_back(*device_it);
682        } else {
683          NOTREACHED();
684        }
685      }
686
687      request->requester->StreamGenerated(label, audio_devices, video_devices);
688      request->ui_proxy->OnStarted(
689          base::Bind(&MediaStreamManager::StopStreamFromUI,
690                     base::Unretained(this), label));
691      break;
692    }
693    default:
694      NOTREACHED();
695      break;
696  }
697}
698
699void MediaStreamManager::Closed(MediaStreamType stream_type,
700                                int capture_session_id) {
701  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
702}
703
704void MediaStreamManager::DevicesEnumerated(
705    MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
706  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
707
708  // Only cache the device list when the device list has been changed.
709  bool need_update_clients = false;
710  EnumerationCache* cache =
711      stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
712      &audio_enumeration_cache_ : &video_enumeration_cache_;
713  if (!cache->valid ||
714      devices.size() != cache->devices.size() ||
715      !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
716                  StreamDeviceInfo::IsEqual)) {
717    cache->valid = true;
718    cache->devices = devices;
719    need_update_clients = true;
720  }
721
722  if (need_update_clients && monitoring_started_)
723    NotifyDevicesChanged(stream_type, devices);
724
725  // Publish the result for all requests waiting for device list(s).
726  // Find the requests waiting for this device list, store their labels and
727  // release the iterator before calling device settings. We might get a call
728  // back from device_settings that will need to iterate through devices.
729  std::list<std::string> label_list;
730  for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
731       ++it) {
732    if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
733        Requested(it->second->request, stream_type)) {
734      if (it->second->request.request_type != MEDIA_ENUMERATE_DEVICES)
735        it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
736      label_list.push_back(it->first);
737    }
738  }
739  for (std::list<std::string>::iterator it = label_list.begin();
740       it != label_list.end(); ++it) {
741    DeviceRequest* request = requests_[*it];
742    switch (request->request.request_type) {
743      case MEDIA_ENUMERATE_DEVICES:
744        if (need_update_clients && request->requester)
745          request->requester->DevicesEnumerated(*it, devices);
746        break;
747      default:
748        if (request->state(request->request.audio_type) ==
749                MEDIA_REQUEST_STATE_REQUESTED ||
750            request->state(request->request.video_type) ==
751                MEDIA_REQUEST_STATE_REQUESTED) {
752          // We are doing enumeration for other type of media, wait until it is
753          // all done before posting the request to UI because UI needs
754          // the device lists to handle the request.
755          break;
756        }
757
758        // Post the request to UI for permission approval.
759        PostRequestToUI(*it);
760        break;
761    }
762  }
763  label_list.clear();
764  --active_enumeration_ref_count_[stream_type];
765  DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
766}
767
768void MediaStreamManager::Error(MediaStreamType stream_type,
769                               int capture_session_id,
770                               MediaStreamProviderError error) {
771  // Find the device for the error call.
772  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
773
774  for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
775       ++it) {
776    StreamDeviceInfoArray& devices = it->second->devices;
777
778    // TODO(miu): BUG.  It's possible for the audio (or video) device array in
779    // the "requester" to become out-of-sync with the order of devices we have
780    // here.  See http://crbug.com/147650
781    int audio_device_idx = -1;
782    int video_device_idx = -1;
783    for (StreamDeviceInfoArray::iterator device_it = devices.begin();
784         device_it != devices.end(); ++device_it) {
785      if (IsAudioMediaType(device_it->device.type)) {
786        ++audio_device_idx;
787      } else if (IsVideoMediaType(device_it->device.type)) {
788        ++video_device_idx;
789      } else {
790        NOTREACHED();
791        continue;
792      }
793      if (device_it->device.type != stream_type ||
794          device_it->session_id != capture_session_id) {
795        continue;
796      }
797      // We've found the failing device. Find the error case:
798      // An error should only be reported to the MediaStreamManager if
799      // the request has not been fulfilled yet.
800      DCHECK(it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE);
801      if (it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE) {
802        // Request is not done, devices are not opened in this case.
803        if (devices.size() <= 1) {
804          scoped_ptr<DeviceRequest> request(it->second);
805          // 1. Device not opened and no other devices for this request ->
806          //    signal stream error and remove the request.
807          if (request->requester)
808            request->requester->StreamGenerationFailed(it->first);
809
810          RemoveRequest(it);
811        } else {
812          // 2. Not opened but other devices exists for this request -> remove
813          //    device from list, but don't signal an error.
814          devices.erase(device_it);  // NOTE: This invalidates device_it!
815        }
816      }
817      return;
818    }
819  }
820}
821
822void MediaStreamManager::HandleAccessRequestResponse(
823    const std::string& label,
824    const MediaStreamDevices& devices) {
825  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
826
827  DeviceRequests::iterator request_it = requests_.find(label);
828  if (request_it == requests_.end()) {
829    return;
830  }
831
832  // Handle the case when the request was denied.
833  if (devices.empty()) {
834    // Notify the users about the request result.
835    scoped_ptr<DeviceRequest> request(request_it->second);
836    if (request->requester)
837      request->requester->StreamGenerationFailed(label);
838
839    if (request->request.request_type == MEDIA_DEVICE_ACCESS &&
840        !request->callback.is_null()) {
841      request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
842    }
843
844    RemoveRequest(request_it);
845    return;
846  }
847
848  if (request_it->second->request.request_type == MEDIA_DEVICE_ACCESS) {
849    scoped_ptr<DeviceRequest> request(request_it->second);
850    if (!request->callback.is_null())
851      request->callback.Run(devices, request->ui_proxy.Pass());
852
853    // Delete the request since it is done.
854    RemoveRequest(request_it);
855    return;
856  }
857
858  // Process all newly-accepted devices for this request.
859  DeviceRequest* request = request_it->second;
860  bool found_audio = false;
861  bool found_video = false;
862  for (MediaStreamDevices::const_iterator device_it = devices.begin();
863       device_it != devices.end(); ++device_it) {
864    StreamDeviceInfo device_info;
865    device_info.device = *device_it;
866
867    // TODO(justinlin): Nicer way to do this?
868    // Re-append the device's id since we lost it when posting request to UI.
869    if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
870        device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
871      device_info.device.id = request->request.requested_device_id;
872
873      // Initialize the sample_rate and channel_layout here since for audio
874      // mirroring, we don't go through EnumerateDevices where these are usually
875      // initialized.
876      if (device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
877        const media::AudioParameters parameters =
878            audio_manager_->GetDefaultOutputStreamParameters();
879        int sample_rate = parameters.sample_rate();
880        // If we weren't able to get the native sampling rate or the sample_rate
881        // is outside the valid range for input devices set reasonable defaults.
882        if (sample_rate <= 0 || sample_rate > 96000)
883          sample_rate = 44100;
884
885        device_info.device.sample_rate = sample_rate;
886        device_info.device.channel_layout = media::CHANNEL_LAYOUT_STEREO;
887      }
888    }
889
890    // Set in_use to false to be able to track if this device has been
891    // opened. in_use might be true if the device type can be used in more
892    // than one session.
893    device_info.in_use = false;
894
895    device_info.session_id =
896        GetDeviceManager(device_info.device.type)->Open(device_info);
897    request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
898    request->devices.push_back(device_info);
899
900    if (device_info.device.type == request->request.audio_type) {
901      found_audio = true;
902    } else if (device_info.device.type == request->request.video_type) {
903      found_video = true;
904    }
905  }
906
907  // Check whether we've received all stream types requested.
908  if (!found_audio && IsAudioMediaType(request->request.audio_type))
909    request->SetState(request->request.audio_type, MEDIA_REQUEST_STATE_ERROR);
910
911  if (!found_video && IsVideoMediaType(request->request.video_type))
912    request->SetState(request->request.video_type, MEDIA_REQUEST_STATE_ERROR);
913}
914
915void MediaStreamManager::StopStreamFromUI(const std::string& label) {
916  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
917
918  DeviceRequests::iterator it = requests_.find(label);
919  if (it == requests_.end())
920    return;
921
922  // Notify renderers that the stream has been stopped.
923  if (it->second->requester)
924    it->second->requester->StreamGenerationFailed(label);
925
926  StopGeneratedStream(label);
927}
928
929void MediaStreamManager::UseFakeDevice() {
930  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
931  video_capture_manager()->UseFakeDevice();
932  audio_input_device_manager()->UseFakeDevice();
933  UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
934}
935
936void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
937  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
938  use_fake_ui_ = true;
939  fake_ui_ = fake_ui.Pass();
940}
941
942void MediaStreamManager::WillDestroyCurrentMessageLoop() {
943  DCHECK_EQ(base::MessageLoop::current(), io_loop_);
944  DCHECK(requests_.empty());
945  if (device_thread_) {
946    StopMonitoring();
947
948    video_capture_manager_->Unregister();
949    audio_input_device_manager_->Unregister();
950    device_thread_.reset();
951  }
952
953  audio_input_device_manager_ = NULL;
954  video_capture_manager_ = NULL;
955  io_loop_ = NULL;
956}
957
958void MediaStreamManager::NotifyDevicesChanged(
959    MediaStreamType stream_type,
960    const StreamDeviceInfoArray& devices) {
961  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
962  MediaObserver* media_observer =
963      GetContentClient()->browser()->GetMediaObserver();
964  if (media_observer == NULL)
965    return;
966
967  // Map the devices to MediaStreamDevices.
968  MediaStreamDevices new_devices;
969  for (StreamDeviceInfoArray::const_iterator it = devices.begin();
970       it != devices.end(); ++it) {
971    new_devices.push_back(it->device);
972  }
973
974  if (IsAudioMediaType(stream_type)) {
975    media_observer->OnAudioCaptureDevicesChanged(new_devices);
976  } else if (IsVideoMediaType(stream_type)) {
977    media_observer->OnVideoCaptureDevicesChanged(new_devices);
978  } else {
979    NOTREACHED();
980  }
981}
982
983bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
984  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
985
986  const bool requested_audio = IsAudioMediaType(request.request.audio_type);
987  const bool requested_video = IsVideoMediaType(request.request.video_type);
988
989  const bool audio_done =
990      !requested_audio ||
991      request.state(request.request.audio_type) ==
992      MEDIA_REQUEST_STATE_DONE ||
993      request.state(request.request.audio_type) ==
994      MEDIA_REQUEST_STATE_ERROR;
995  if (!audio_done)
996    return false;
997
998  const bool video_done =
999      !requested_video ||
1000      request.state(request.request.video_type) ==
1001      MEDIA_REQUEST_STATE_DONE ||
1002      request.state(request.request.video_type) ==
1003      MEDIA_REQUEST_STATE_ERROR;
1004  if (!video_done)
1005    return false;
1006
1007  for (StreamDeviceInfoArray::const_iterator it = request.devices.begin();
1008       it != request.devices.end(); ++it) {
1009    if (it->in_use == false)
1010      return false;
1011  }
1012
1013  return true;
1014}
1015
1016MediaStreamProvider* MediaStreamManager::GetDeviceManager(
1017    MediaStreamType stream_type) {
1018  if (IsVideoMediaType(stream_type)) {
1019    return video_capture_manager();
1020  } else if (IsAudioMediaType(stream_type)) {
1021    return audio_input_device_manager();
1022  }
1023  NOTREACHED();
1024  return NULL;
1025}
1026
1027void MediaStreamManager::OnDevicesChanged(
1028    base::SystemMonitor::DeviceType device_type) {
1029  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1030
1031  // NOTE: This method is only called in response to physical audio/video device
1032  // changes (from the operating system).
1033
1034  MediaStreamType stream_type;
1035  if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
1036    stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
1037  } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
1038    stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
1039  } else {
1040    return;  // Uninteresting device change.
1041  }
1042
1043  // Always do enumeration even though some enumeration is in progress,
1044  // because those enumeration commands could be sent before these devices
1045  // change.
1046  ++active_enumeration_ref_count_[stream_type];
1047  GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
1048}
1049
1050}  // namespace content
1051