Home | History | Annotate | Download | only in media
      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 "chrome/browser/media/media_capture_devices_dispatcher.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/logging.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/sha1.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
     15 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h"
     16 #include "chrome/browser/media/audio_stream_indicator.h"
     17 #include "chrome/browser/media/media_stream_capture_indicator.h"
     18 #include "chrome/browser/media/media_stream_infobar_delegate.h"
     19 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/ui/screen_capture_notification_ui.h"
     22 #include "chrome/browser/ui/simple_message_box.h"
     23 #include "chrome/common/chrome_switches.h"
     24 #include "chrome/common/extensions/extension.h"
     25 #include "chrome/common/pref_names.h"
     26 #include "components/user_prefs/pref_registry_syncable.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "content/public/browser/media_devices_monitor.h"
     29 #include "content/public/browser/notification_service.h"
     30 #include "content/public/browser/notification_source.h"
     31 #include "content/public/browser/notification_types.h"
     32 #include "content/public/browser/web_contents.h"
     33 #include "content/public/common/desktop_media_id.h"
     34 #include "content/public/common/media_stream_request.h"
     35 #include "extensions/common/constants.h"
     36 #include "grit/generated_resources.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 
     39 #if defined(USE_CRAS)
     40 #include "media/audio/cras/audio_manager_cras.h"
     41 #endif
     42 
     43 using content::BrowserThread;
     44 using content::MediaStreamDevices;
     45 
     46 namespace {
     47 
     48 // Finds a device in |devices| that has |device_id|, or NULL if not found.
     49 const content::MediaStreamDevice* FindDeviceWithId(
     50     const content::MediaStreamDevices& devices,
     51     const std::string& device_id) {
     52   content::MediaStreamDevices::const_iterator iter = devices.begin();
     53   for (; iter != devices.end(); ++iter) {
     54     if (iter->id == device_id) {
     55       return &(*iter);
     56     }
     57   }
     58   return NULL;
     59 };
     60 
     61 // This is a short-term solution to allow testing of the the Screen Capture API
     62 // with Google Hangouts in M27.
     63 // TODO(sergeyu): Remove this whitelist as soon as possible.
     64 bool IsOriginWhitelistedForScreenCapture(const GURL& origin) {
     65 #if defined(OFFICIAL_BUILD)
     66   if (// Google Hangouts.
     67       (origin.SchemeIs("https") &&
     68        EndsWith(origin.spec(), ".talkgadget.google.com/", true)) ||
     69       origin.spec() == "https://plus.google.com/" ||
     70       origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/" ||
     71       origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
     72       origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
     73       origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/") {
     74     return true;
     75   }
     76   // Check against hashed origins.
     77   const std::string origin_hash = base::SHA1HashString(origin.spec());
     78   DCHECK_EQ(origin_hash.length(), base::kSHA1Length);
     79   const std::string hexencoded_origin_hash =
     80       base::HexEncode(origin_hash.data(), origin_hash.length());
     81   return
     82       hexencoded_origin_hash == "3C2705BC432E7C51CA8553FDC5BEE873FF2468EE" ||
     83       hexencoded_origin_hash == "50F02B8A668CAB274527D58356F07C2143080FCC";
     84 #else
     85   return false;
     86 #endif
     87 }
     88 
     89 }  // namespace
     90 
     91 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
     92     const content::MediaStreamRequest& request,
     93     const content::MediaResponseCallback& callback)
     94     : request(request),
     95       callback(callback) {
     96 }
     97 
     98 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
     99 
    100 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
    101   return Singleton<MediaCaptureDevicesDispatcher>::get();
    102 }
    103 
    104 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
    105     : devices_enumerated_(false),
    106       is_device_enumeration_disabled_(false),
    107       media_stream_capture_indicator_(new MediaStreamCaptureIndicator()),
    108       audio_stream_indicator_(new AudioStreamIndicator()) {
    109   // MediaCaptureDevicesDispatcher is a singleton. It should be created on
    110   // UI thread. Otherwise, it will not receive
    111   // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
    112   // possible use after free.
    113   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    114   notifications_registrar_.Add(
    115       this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    116       content::NotificationService::AllSources());
    117 }
    118 
    119 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
    120 
    121 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
    122     user_prefs::PrefRegistrySyncable* registry) {
    123   registry->RegisterStringPref(
    124       prefs::kDefaultAudioCaptureDevice,
    125       std::string(),
    126       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    127   registry->RegisterStringPref(
    128       prefs::kDefaultVideoCaptureDevice,
    129       std::string(),
    130       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    131 }
    132 
    133 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
    134   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    135   if (!observers_.HasObserver(observer))
    136     observers_.AddObserver(observer);
    137 }
    138 
    139 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
    140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    141   observers_.RemoveObserver(observer);
    142 }
    143 
    144 const MediaStreamDevices&
    145 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
    146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    147   if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
    148     content::EnsureMonitorCaptureDevices();
    149     devices_enumerated_ = true;
    150   }
    151   return audio_devices_;
    152 }
    153 
    154 const MediaStreamDevices&
    155 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
    156   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    157   if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
    158     content::EnsureMonitorCaptureDevices();
    159     devices_enumerated_ = true;
    160   }
    161   return video_devices_;
    162 }
    163 
    164 void MediaCaptureDevicesDispatcher::Observe(
    165     int type,
    166     const content::NotificationSource& source,
    167     const content::NotificationDetails& details) {
    168   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    169   if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    170     content::WebContents* web_contents =
    171         content::Source<content::WebContents>(source).ptr();
    172     pending_requests_.erase(web_contents);
    173   }
    174 }
    175 
    176 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
    177     content::WebContents* web_contents,
    178     const content::MediaStreamRequest& request,
    179     const content::MediaResponseCallback& callback,
    180     const extensions::Extension* extension) {
    181   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    182 
    183   if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE ||
    184       request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE) {
    185     ProcessScreenCaptureAccessRequest(
    186         web_contents, request, callback, extension);
    187   } else if (extension) {
    188     // For extensions access is approved based on extension permissions.
    189     ProcessMediaAccessRequestFromExtension(
    190         web_contents, request, callback, extension);
    191   } else {
    192     ProcessRegularMediaAccessRequest(web_contents, request, callback);
    193   }
    194 }
    195 
    196 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
    197     content::WebContents* web_contents,
    198     const content::MediaStreamRequest& request,
    199     const content::MediaResponseCallback& callback,
    200     const extensions::Extension* extension) {
    201   content::MediaStreamDevices devices;
    202   scoped_ptr<content::MediaStreamUI> ui;
    203 
    204   if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
    205     callback.Run(devices, ui.Pass());
    206     return;
    207   }
    208 
    209   content::DesktopMediaID media_id =
    210       content::DesktopMediaID::Parse(request.requested_video_device_id);
    211   if (media_id.is_null()) {
    212     LOG(ERROR) << "Invalid desktop media ID: "
    213                << request.requested_video_device_id;
    214     callback.Run(devices, ui.Pass());
    215     return;
    216   }
    217 
    218   const bool system_audio_capture_requested =
    219       request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE;
    220 
    221 #if defined(USE_CRAS)
    222   const bool system_audio_capture_supported = true;
    223 #else
    224   const bool system_audio_capture_supported = false;
    225 #endif
    226 
    227   // Reject request when audio capture was requested but is not supported on
    228   // this system.
    229   if (system_audio_capture_requested && !system_audio_capture_supported) {
    230     callback.Run(devices, ui.Pass());
    231     return;
    232   }
    233 
    234   const bool component_extension =
    235       extension && extension->location() == extensions::Manifest::COMPONENT;
    236 
    237   const bool screen_capture_enabled =
    238       CommandLine::ForCurrentProcess()->HasSwitch(
    239           switches::kEnableUserMediaScreenCapturing) ||
    240       IsOriginWhitelistedForScreenCapture(request.security_origin);
    241 
    242   const bool origin_is_secure =
    243       request.security_origin.SchemeIsSecure() ||
    244       request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
    245       CommandLine::ForCurrentProcess()->HasSwitch(
    246           switches::kAllowHttpScreenCapture);
    247 
    248   // Approve request only when the following conditions are met:
    249   //  1. Screen capturing is enabled via command line switch or white-listed for
    250   //     the given origin.
    251   //  2. Request comes from a page with a secure origin or from an extension.
    252   if (screen_capture_enabled && origin_is_secure) {
    253     // For component extensions, bypass message box.
    254     bool user_approved = false;
    255     if (!component_extension) {
    256       string16 application_name = UTF8ToUTF16(request.security_origin.spec());
    257       string16 confirmation_text = l10n_util::GetStringFUTF16(
    258           request.audio_type == content::MEDIA_NO_SERVICE ?
    259               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
    260               IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
    261           application_name);
    262       chrome::MessageBoxResult result = chrome::ShowMessageBox(
    263           NULL,
    264           l10n_util::GetStringFUTF16(
    265               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
    266           confirmation_text,
    267           chrome::MESSAGE_BOX_TYPE_QUESTION);
    268       user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
    269     }
    270 
    271     if (user_approved || component_extension) {
    272       devices.push_back(content::MediaStreamDevice(
    273           content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
    274       if (system_audio_capture_requested) {
    275 #if defined(USE_CRAS)
    276         // Use the special loopback device ID for system audio capture.
    277         devices.push_back(content::MediaStreamDevice(
    278             content::MEDIA_SYSTEM_AUDIO_CAPTURE,
    279             media::AudioManagerCras::kLoopbackDeviceId,
    280             "System Audio"));
    281 #endif
    282       }
    283     }
    284   }
    285 
    286   // Unless we're being invoked from a component extension, register to display
    287   // the notification for stream capture.
    288   if (!devices.empty() && !component_extension) {
    289     // Use extension name as title for extensions and origin for drive-by web.
    290     std::string title;
    291     if (extension) {
    292       title = extension->name();
    293     } else {
    294       title = web_contents->GetURL().GetOrigin().spec();
    295     }
    296 
    297     ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
    298         IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, UTF8ToUTF16(title)));
    299   }
    300 
    301   callback.Run(devices, ui.Pass());
    302 }
    303 
    304 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequestFromExtension(
    305     content::WebContents* web_contents,
    306     const content::MediaStreamRequest& request,
    307     const content::MediaResponseCallback& callback,
    308     const extensions::Extension* extension) {
    309   content::MediaStreamDevices devices;
    310   Profile* profile =
    311       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    312 
    313 #if defined(OS_ANDROID)
    314   // Tab capture is not supported on Android.
    315   bool tab_capture_allowed = false;
    316 #else
    317   extensions::TabCaptureRegistry* tab_capture_registry =
    318       extensions::TabCaptureRegistryFactory::GetForProfile(profile);
    319   bool tab_capture_allowed =
    320       tab_capture_registry->VerifyRequest(request.render_process_id,
    321                                           request.render_view_id);
    322 #endif
    323 
    324   if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
    325       tab_capture_allowed &&
    326       extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
    327     devices.push_back(content::MediaStreamDevice(
    328         content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
    329   } else if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
    330              extension->HasAPIPermission(
    331                  extensions::APIPermission::kAudioCapture)) {
    332     GetDefaultDevicesForProfile(profile, true, false, &devices);
    333   }
    334 
    335   if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
    336       tab_capture_allowed &&
    337       extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
    338     devices.push_back(content::MediaStreamDevice(
    339         content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
    340   } else if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
    341        extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
    342     GetDefaultDevicesForProfile(profile, false, true, &devices);
    343   }
    344 
    345   scoped_ptr<content::MediaStreamUI> ui;
    346   if (!devices.empty()) {
    347     ui = media_stream_capture_indicator_->RegisterMediaStream(
    348         web_contents, devices);
    349   }
    350   callback.Run(devices, ui.Pass());
    351 }
    352 
    353 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
    354     content::WebContents* web_contents,
    355     const content::MediaStreamRequest& request,
    356     const content::MediaResponseCallback& callback) {
    357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    358 
    359   RequestsQueue& queue = pending_requests_[web_contents];
    360   queue.push_back(PendingAccessRequest(request, callback));
    361 
    362   // If this is the only request then show the infobar.
    363   if (queue.size() == 1)
    364     ProcessQueuedAccessRequest(web_contents);
    365 }
    366 
    367 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
    368     content::WebContents* web_contents) {
    369   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    370 
    371   std::map<content::WebContents*, RequestsQueue>::iterator it =
    372       pending_requests_.find(web_contents);
    373 
    374   if (it == pending_requests_.end() || it->second.empty()) {
    375     // Don't do anything if the tab was was closed.
    376     return;
    377   }
    378 
    379   DCHECK(!it->second.empty());
    380 
    381   MediaStreamInfoBarDelegate::Create(
    382       web_contents, it->second.front().request,
    383       base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
    384                  base::Unretained(this), web_contents));
    385 }
    386 
    387 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
    388     content::WebContents* web_contents,
    389     const content::MediaStreamDevices& devices,
    390     scoped_ptr<content::MediaStreamUI> ui) {
    391   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    392 
    393   std::map<content::WebContents*, RequestsQueue>::iterator it =
    394       pending_requests_.find(web_contents);
    395   if (it == pending_requests_.end()) {
    396     // WebContents has been destroyed. Don't need to do anything.
    397     return;
    398   }
    399 
    400   RequestsQueue& queue(it->second);
    401   if (queue.empty())
    402     return;
    403 
    404   content::MediaResponseCallback callback = queue.front().callback;
    405   queue.pop_front();
    406 
    407   if (!queue.empty()) {
    408     // Post a task to process next queued request. It has to be done
    409     // asynchronously to make sure that calling infobar is not destroyed until
    410     // after this function returns.
    411     BrowserThread::PostTask(
    412         BrowserThread::UI, FROM_HERE,
    413         base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
    414                    base::Unretained(this), web_contents));
    415   }
    416 
    417   callback.Run(devices, ui.Pass());
    418 }
    419 
    420 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
    421     Profile* profile,
    422     bool audio,
    423     bool video,
    424     content::MediaStreamDevices* devices) {
    425   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    426   DCHECK(audio || video);
    427 
    428   PrefService* prefs = profile->GetPrefs();
    429   std::string default_device;
    430   if (audio) {
    431     default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
    432     const content::MediaStreamDevice* device =
    433         GetRequestedAudioDevice(default_device);
    434     if (!device)
    435       device = GetFirstAvailableAudioDevice();
    436     if (device)
    437       devices->push_back(*device);
    438   }
    439 
    440   if (video) {
    441     default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
    442     const content::MediaStreamDevice* device =
    443         GetRequestedVideoDevice(default_device);
    444     if (!device)
    445       device = GetFirstAvailableVideoDevice();
    446     if (device)
    447       devices->push_back(*device);
    448   }
    449 }
    450 
    451 const content::MediaStreamDevice*
    452 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
    453     const std::string& requested_audio_device_id) {
    454   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    455   const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
    456   const content::MediaStreamDevice* const device =
    457       FindDeviceWithId(audio_devices, requested_audio_device_id);
    458   return device;
    459 }
    460 
    461 const content::MediaStreamDevice*
    462 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
    463   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    464   const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
    465   if (audio_devices.empty())
    466     return NULL;
    467   return &(*audio_devices.begin());
    468 }
    469 
    470 const content::MediaStreamDevice*
    471 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
    472     const std::string& requested_video_device_id) {
    473   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    474   const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
    475   const content::MediaStreamDevice* const device =
    476       FindDeviceWithId(video_devices, requested_video_device_id);
    477   return device;
    478 }
    479 
    480 const content::MediaStreamDevice*
    481 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
    482   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    483   const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
    484   if (video_devices.empty())
    485     return NULL;
    486   return &(*video_devices.begin());
    487 }
    488 
    489 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
    490   is_device_enumeration_disabled_ = true;
    491 }
    492 
    493 scoped_refptr<MediaStreamCaptureIndicator>
    494     MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
    495   return media_stream_capture_indicator_;
    496 }
    497 
    498 scoped_refptr<AudioStreamIndicator>
    499 MediaCaptureDevicesDispatcher::GetAudioStreamIndicator() {
    500   return audio_stream_indicator_;
    501 }
    502 
    503 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged(
    504     const content::MediaStreamDevices& devices) {
    505   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    506   BrowserThread::PostTask(
    507       BrowserThread::UI, FROM_HERE,
    508       base::Bind(&MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread,
    509                  base::Unretained(this), devices));
    510 }
    511 
    512 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged(
    513     const content::MediaStreamDevices& devices) {
    514   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    515   BrowserThread::PostTask(
    516       BrowserThread::UI, FROM_HERE,
    517       base::Bind(&MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread,
    518                  base::Unretained(this), devices));
    519 }
    520 
    521 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
    522     int render_process_id,
    523     int render_view_id,
    524     int page_request_id,
    525     const content::MediaStreamDevice& device,
    526     content::MediaRequestState state) {
    527   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    528   BrowserThread::PostTask(
    529       BrowserThread::UI, FROM_HERE,
    530       base::Bind(
    531           &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
    532           base::Unretained(this), render_process_id, render_view_id,
    533           page_request_id, device, state));
    534 
    535 }
    536 
    537 void MediaCaptureDevicesDispatcher::OnAudioStreamPlayingChanged(
    538     int render_process_id, int render_view_id, int stream_id,
    539     bool is_playing, float power_dbfs, bool clipped) {
    540   audio_stream_indicator_->UpdateWebContentsStatus(
    541       render_process_id, render_view_id, stream_id,
    542       is_playing, power_dbfs, clipped);
    543 }
    544 
    545 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
    546     int render_process_id,
    547     int render_view_id) {
    548   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    549   BrowserThread::PostTask(
    550       BrowserThread::UI, FROM_HERE,
    551       base::Bind(
    552           &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
    553           base::Unretained(this), render_process_id, render_view_id));
    554 }
    555 
    556 void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
    557     const content::MediaStreamDevices& devices) {
    558   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    559   devices_enumerated_ = true;
    560   audio_devices_ = devices;
    561   FOR_EACH_OBSERVER(Observer, observers_,
    562                     OnUpdateAudioDevices(audio_devices_));
    563 }
    564 
    565 void MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread(
    566     const content::MediaStreamDevices& devices){
    567   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    568   devices_enumerated_ = true;
    569   video_devices_ = devices;
    570   FOR_EACH_OBSERVER(Observer, observers_,
    571                     OnUpdateVideoDevices(video_devices_));
    572 }
    573 
    574 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
    575     int render_process_id,
    576     int render_view_id,
    577     int page_request_id,
    578     const content::MediaStreamDevice& device,
    579     content::MediaRequestState state) {
    580   // Cancel the request.
    581   if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
    582     bool found = false;
    583     for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
    584          rqs_it != pending_requests_.end(); ++rqs_it) {
    585       RequestsQueue& queue = rqs_it->second;
    586       for (RequestsQueue::iterator it = queue.begin();
    587            it != queue.end(); ++it) {
    588         if (it->request.render_process_id == render_process_id &&
    589             it->request.render_view_id == render_view_id &&
    590             it->request.page_request_id == page_request_id) {
    591           queue.erase(it);
    592           found = true;
    593           break;
    594         }
    595       }
    596       if (found)
    597         break;
    598     }
    599   }
    600 
    601   FOR_EACH_OBSERVER(Observer, observers_,
    602                     OnRequestUpdate(render_process_id,
    603                                     render_view_id,
    604                                     device,
    605                                     state));
    606 }
    607 
    608 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
    609     int render_process_id,
    610     int render_view_id) {
    611   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    612   FOR_EACH_OBSERVER(Observer, observers_,
    613                     OnCreatingAudioStream(render_process_id, render_view_id));
    614 }
    615