diff options
author | xians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-19 14:16:26 +0000 |
---|---|---|
committer | xians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-19 14:16:26 +0000 |
commit | c84ec38fc74f478c481d2e4ceb1c5fc5ff55b976 (patch) | |
tree | 8deb8cf27eb393de891e555a7f958624292f3264 /content | |
parent | 9950f28346a3e36f498324951609a192dfec5415 (diff) | |
download | chromium_src-c84ec38fc74f478c481d2e4ceb1c5fc5ff55b976.zip chromium_src-c84ec38fc74f478c481d2e4ceb1c5fc5ff55b976.tar.gz chromium_src-c84ec38fc74f478c481d2e4ceb1c5fc5ff55b976.tar.bz2 |
> Refactor media_stream_device_settings into media_stream_ui_controller.
> media_stream_ui_controller is responsible for posting the request to UI and send the result back.
> Use MediaRequstResponseCallback (will be used by speech)
> Added unittests for the media_stream_manager::MakeMediaAccessRequest
BUG=146306,154286
TEST=pyauto test: chrome/test/functional/webrtc_*;
content_unittests;
Review URL: https://codereview.chromium.org/11060005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162990 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
14 files changed, 944 insertions, 604 deletions
diff --git a/content/browser/renderer_host/media/media_stream_device_settings.cc b/content/browser/renderer_host/media/media_stream_device_settings.cc deleted file mode 100644 index d208508..0000000 --- a/content/browser/renderer_host/media/media_stream_device_settings.cc +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/renderer_host/media/media_stream_device_settings.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/stl_util.h" -#include "content/browser/renderer_host/media/media_stream_settings_requester.h" -#include "content/browser/renderer_host/render_view_host_delegate.h" -#include "content/browser/renderer_host/render_view_host_impl.h" -#include "content/common/media/media_stream_options.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/common/media_stream_request.h" -#include "googleurl/src/gurl.h" - -using content::BrowserThread; -using content::MediaStreamDevice; -using content::MediaStreamRequest; - -namespace { - -// Helper class to handle the callbacks to a MediaStreamDeviceSettings instance. -// This class will make sure that the call to PostResponse is executed on the IO -// thread (and that the instance of MediaStreamDeviceSettings still exists). -// This allows us to pass a simple base::Callback object to any class that needs -// to post a response to the MediaStreamDeviceSettings object. This logic cannot -// be implemented inside MediaStreamDeviceSettings::PostResponse since that -// would imply that the WeakPtr<MediaStreamDeviceSettings> pointer has been -// dereferenced already (which would cause an error in the ThreadChecker before -// we even get there). -class ResponseCallbackHelper - : public base::RefCountedThreadSafe<ResponseCallbackHelper> { - public: - explicit ResponseCallbackHelper( - base::WeakPtr<media_stream::MediaStreamDeviceSettings> settings) - : settings_(settings) { - } - - void PostResponse(const std::string& label, - const content::MediaStreamDevices& devices) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&media_stream::MediaStreamDeviceSettings::PostResponse, - settings_, label, devices)); - return; - } else if (settings_) { - settings_->PostResponse(label, devices); - } - } - - private: - friend class base::RefCountedThreadSafe<ResponseCallbackHelper>; - ~ResponseCallbackHelper() {} - - base::WeakPtr<media_stream::MediaStreamDeviceSettings> settings_; - - DISALLOW_COPY_AND_ASSIGN(ResponseCallbackHelper); -}; - -// Predicate used in find_if below to find a device with a given ID. -class DeviceIdEquals { - public: - explicit DeviceIdEquals(const std::string& device_id) - : device_id_(device_id) { - } - - bool operator() (const media_stream::StreamDeviceInfo& device) { - return (device.device_id == device_id_); - } - - private: - std::string device_id_; -}; - -} // namespace - -namespace media_stream { - -typedef std::map<MediaStreamType, StreamDeviceInfoArray> DeviceMap; - -// Device request contains all data needed to keep track of requests between the -// different calls. -struct MediaStreamDeviceSettingsRequest : public MediaStreamRequest { - public: - MediaStreamDeviceSettingsRequest( - int render_pid, - int render_vid, - const GURL& origin, - const StreamOptions& request_options) - : MediaStreamRequest(render_pid, render_vid, origin), - options(request_options), - posted_task(false) {} - - ~MediaStreamDeviceSettingsRequest() {} - - // Request options. - StreamOptions options; - // Map containing available devices for the requested capture types. - // Note, never call devices_full[stream_type].empty() before making sure - // that type of device has existed on the map, otherwise it will create an - // empty device entry on the map. - DeviceMap devices_full; - // Whether or not a task was posted to make the call to - // RequestMediaAccessPermission, to make sure that we never post twice to it. - bool posted_task; -}; - -namespace { - -// Sends the request to the appropriate WebContents. -void DoDeviceRequest(const MediaStreamDeviceSettingsRequest& request, - const content::MediaResponseCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Send the permission request to the web contents. - content::RenderViewHostImpl* host = - content::RenderViewHostImpl::FromID(request.render_process_id, - request.render_view_id); - - // Tab may have gone away. - if (!host || !host->GetDelegate()) { - callback.Run(content::MediaStreamDevices()); - return; - } - - host->GetDelegate()->RequestMediaAccessPermission(&request, callback); -} - -bool IsRequestReadyForView( - media_stream::MediaStreamDeviceSettingsRequest* request) { - if ((content::IsAudioMediaType(request->options.audio_type) && - request->devices_full.count(request->options.audio_type) == 0) || - (content::IsVideoMediaType(request->options.video_type) && - request->devices_full.count(request->options.video_type) == 0)) { - return false; - } - - // We have got all the requested devices, it is ready if it has not - // been posted for UI yet. - return !request->posted_task; -} - -} // namespace - -MediaStreamDeviceSettings::MediaStreamDeviceSettings( - SettingsRequester* requester) - : requester_(requester), - use_fake_ui_(false), - weak_ptr_factory_(this) { - DCHECK(requester_); -} - -MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { - STLDeleteValues(&requests_); -} - -void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( - const std::string& label, int render_process_id, int render_view_id, - const StreamOptions& request_options, const GURL& security_origin) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - // Create a new request. - if (!requests_.insert(std::make_pair(label, - new MediaStreamDeviceSettingsRequest( - render_process_id, - render_view_id, - security_origin, - request_options))).second) { - NOTREACHED(); - } -} - -void MediaStreamDeviceSettings::RemovePendingCaptureRequest( - const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - SettingsRequests::iterator request_it = requests_.find(label); - if (request_it != requests_.end()) { - // Proceed the next pending request for the same page. - MediaStreamDeviceSettingsRequest* request = request_it->second; - int render_view_id = request->render_view_id; - int render_process_id = request->render_process_id; - bool was_posted = request->posted_task; - - // TODO(xians): Post a cancel request on UI thread to dismiss the infobar - // if request has been sent to the UI. - // Remove the request from the queue. - requests_.erase(request_it); - delete request; - - // Simply return if the canceled request has not been brought to UI. - if (!was_posted) - return; - - // Process the next pending request to replace the old infobar on the same - // page. - ProcessNextRequestForView(render_view_id, render_process_id); - } -} - -void MediaStreamDeviceSettings::AvailableDevices( - const std::string& label, - MediaStreamType stream_type, - const StreamDeviceInfoArray& devices) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - SettingsRequests::iterator request_it = requests_.find(label); - if (request_it == requests_.end()) { - NOTREACHED(); - return; - } - - // Add the answer for the request. - MediaStreamDeviceSettingsRequest* request = request_it->second; - DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) - << "This request already has a list of devices for this stream type."; - request->devices_full[stream_type] = devices; - - if (IsRequestReadyForView(request)) { - if (use_fake_ui_) { - PostRequestToFakeUI(label); - return; - } - - if (IsUIBusy(request->render_view_id, request->render_process_id)) { - // The UI can handle only one request at the time, do not post the - // request to the view if the UI is handling any other request. - return; - } - - PostRequestToUI(label); - } -} - -void MediaStreamDeviceSettings::PostResponse( - const std::string& label, - const content::MediaStreamDevices& devices) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - SettingsRequests::iterator req = requests_.find(label); - // Return if the request has been removed. - if (req == requests_.end()) - return; - - DCHECK(requester_); - scoped_ptr<MediaStreamDeviceSettingsRequest> request(req->second); - requests_.erase(req); - - // Look for queued requests for the same view. If there is a pending request, - // post it for user approval. - ProcessNextRequestForView(request->render_view_id, - request->render_process_id); - - if (devices.size() > 0) { - // Build a list of "full" device objects for the accepted devices. - StreamDeviceInfoArray deviceList; - for (content::MediaStreamDevices::const_iterator dev = devices.begin(); - dev != devices.end(); ++dev) { - DeviceMap::iterator subList = request->devices_full.find(dev->type); - DCHECK(subList != request->devices_full.end()); - - deviceList.push_back(*std::find_if(subList->second.begin(), - subList->second.end(), DeviceIdEquals(dev->device_id))); - } - requester_->DevicesAccepted(label, deviceList); - } else { - requester_->SettingsError(label); - } -} - -void MediaStreamDeviceSettings::UseFakeUI() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - use_fake_ui_ = true; -} - -bool MediaStreamDeviceSettings::IsUIBusy(int render_view_id, - int render_process_id) { - for (SettingsRequests::iterator it = requests_.begin(); - it != requests_.end(); ++it) { - if (it->second->render_process_id == render_process_id && - it->second->render_view_id == render_view_id && - it->second->posted_task) { - return true; - } - } - return false; -} - -void MediaStreamDeviceSettings::ProcessNextRequestForView( - int render_view_id, int render_process_id) { - std::string new_label; - for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); - ++it) { - if (it->second->render_process_id == render_process_id && - it->second->render_view_id == render_view_id) { - // This request belongs to the given render view. - MediaStreamDeviceSettingsRequest* request = it->second; - if (IsRequestReadyForView(request)) { - new_label = it->first; - break; - } - } - } - - if (new_label.empty()) - return; - - if (use_fake_ui_) - PostRequestToFakeUI(new_label); - else - PostRequestToUI(new_label); -} - -void MediaStreamDeviceSettings::PostRequestToUI(const std::string& label) { - SettingsRequests::iterator request_it = requests_.find(label); - if (request_it == requests_.end()) { - NOTREACHED(); - return; - } - MediaStreamDeviceSettingsRequest* request = request_it->second; - DCHECK(request != NULL); - - request->posted_task = true; - - // Create the simplified list of devices. - for (DeviceMap::iterator it = request->devices_full.begin(); - it != request->devices_full.end(); ++it) { - request->devices[it->first].clear(); - for (StreamDeviceInfoArray::iterator device = it->second.begin(); - device != it->second.end(); ++device) { - request->devices[it->first].push_back(MediaStreamDevice( - it->first, device->device_id, device->name)); - } - } - - scoped_refptr<ResponseCallbackHelper> helper = - new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); - content::MediaResponseCallback callback = - base::Bind(&ResponseCallbackHelper::PostResponse, - helper.get(), label); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DoDeviceRequest, *request, callback)); -} - -void MediaStreamDeviceSettings::PostRequestToFakeUI(const std::string& label) { - SettingsRequests::iterator request_it = requests_.find(label); - DCHECK(request_it != requests_.end()); - MediaStreamDeviceSettingsRequest* request = request_it->second; - // Used to fake UI, which is needed for server based testing. - // Choose first non-opened device for each media type. - content::MediaStreamDevices devices_to_use; - for (DeviceMap::iterator it = request->devices_full.begin(); - it != request->devices_full.end(); ++it) { - StreamDeviceInfoArray::iterator device_it = it->second.begin(); - for (; device_it != it->second.end(); ++device_it) { - if (!device_it->in_use) { - devices_to_use.push_back(content::MediaStreamDevice( - device_it->stream_type, device_it->device_id, device_it->name)); - break; - } - } - - if (it->second.size() != 0 && device_it == it->second.end()) { - // Use the first capture device in the list if all the devices are - // being used. - devices_to_use.push_back( - content::MediaStreamDevice(it->second.begin()->stream_type, - it->second.begin()->device_id, - it->second.begin()->name)); - } - } - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&MediaStreamDeviceSettings::PostResponse, - weak_ptr_factory_.GetWeakPtr(), label, devices_to_use)); -} - -} // namespace media_stream diff --git a/content/browser/renderer_host/media/media_stream_device_settings.h b/content/browser/renderer_host/media/media_stream_device_settings.h deleted file mode 100644 index 4a3bd6d..0000000 --- a/content/browser/renderer_host/media/media_stream_device_settings.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// MediaStreamDeviceSettings is used to decide which of the available capture -// device to use as well as getting user permission to use the capture device. -// There will be one instance of MediaStreamDeviceSettings handling all -// requests. - -// Expected call flow: -// 1. RequestCaptureDeviceUsage() is called to create a new request for capture -// device usage. -// 2. AvailableDevices() is called with a list of currently available devices. -// 3. Pick device and get user confirmation. -// 4. Confirm by calling SettingsRequester::DevicesAccepted(). -// Repeat step 1 - 4 for new device requests. - -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_DEVICE_SETTINGS_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_DEVICE_SETTINGS_H_ - -#include <map> -#include <string> - -#include "base/basictypes.h" -#include "base/memory/weak_ptr.h" -#include "content/browser/renderer_host/media/media_stream_provider.h" - -namespace media_stream { - -class SettingsRequester; -struct MediaStreamDeviceSettingsRequest; - -// MediaStreamDeviceSettings is responsible for getting user permission to use -// a media capture device as well as selecting what device to use. -class CONTENT_EXPORT MediaStreamDeviceSettings { - public: - explicit MediaStreamDeviceSettings(SettingsRequester* requester); - virtual ~MediaStreamDeviceSettings(); - - // Called when a new request of capture device usage is made. - void RequestCaptureDeviceUsage(const std::string& label, - int render_process_id, - int render_view_id, - const StreamOptions& stream_components, - const GURL& security_origin); - - // Called to remove a pending request of capture device usage when the user - // has no action for the media stream InfoBar. - void RemovePendingCaptureRequest(const std::string& label); - - // Called to pass in an array of available devices for a request represented - // by |label|. There could be multiple calls for a request. - void AvailableDevices(const std::string& label, MediaStreamType stream_type, - const StreamDeviceInfoArray& devices); - - // Called by the InfoBar when the user grants/denies access to some devices - // to the webpage. This is placed here, so the request can be cleared from the - // list of pending requests, instead of letting the InfoBar itself respond to - // the requester. An empty list of devices means that access has been denied. - // This method must be called on the IO thread. - void PostResponse(const std::string& label, - const content::MediaStreamDevices& devices); - - // Used for testing only. This function is called to use faked UI, which is - // needed for server based tests. The first non-opened device(s) will be - // picked. - void UseFakeUI(); - - private: - typedef std::map<std::string, MediaStreamDeviceSettingsRequest*> - SettingsRequests; - - // Returns true if the UI is already processing a request for this render - // view. - bool IsUIBusy(int render_view_id, int render_process_id); - - // Process the next pending request and bring it up to the UI on the given - // page for user approval. - void ProcessNextRequestForView(int render_view_id, int render_process_id); - - // Posts a request to be approved/denied by UI. - void PostRequestToUI(const std::string& label); - - // Posts a request to fake UI which is used for testing purpose. - void PostRequestToFakeUI(const std::string& label); - - SettingsRequester* requester_; - SettingsRequests requests_; - - // See comment above for method UseFakeUI. Used for automated testing. - bool use_fake_ui_; - - base::WeakPtrFactory<MediaStreamDeviceSettings> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(MediaStreamDeviceSettings); -}; - -} // namespace media_stream - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_DEVICE_SETTINGS_H_ diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc index 6124adc..9ef1a2d 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc @@ -169,7 +169,7 @@ void MediaStreamDispatcherHost::OnCancelGenerateStream(int render_view_id, for (StreamMap::iterator it = streams_.begin(); it != streams_.end(); ++it) { if (it->second.render_view_id == render_view_id && it->second.page_request_id == page_request_id) { - GetManager()->CancelGenerateStream(it->first); + GetManager()->CancelRequest(it->first); } } } @@ -197,7 +197,7 @@ void MediaStreamDispatcherHost::OnEnumerateDevices( std::string label; GetManager()->EnumerateDevices(this, render_process_id_, render_view_id, - type, security_origin, &label); + type, security_origin, &label); DCHECK(!label.empty()); streams_[label] = StreamRequest(render_view_id, page_request_id); } @@ -217,7 +217,7 @@ void MediaStreamDispatcherHost::OnOpenDevice( std::string label; GetManager()->OpenDevice(this, render_process_id_, render_view_id, - device_id, type, security_origin, &label); + device_id, type, security_origin, &label); DCHECK(!label.empty()); streams_[label] = StreamRequest(render_view_id, page_request_id); } diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc index 9f8f502..ee5c0df 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -251,7 +251,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateThreeStreams) { EXPECT_EQ(host_->video_devices_.size(), 1u); std::string label2 = host_->label_; std::string device_id2 = host_->video_devices_.front().device_id; - EXPECT_NE(device_id1, device_id2); + EXPECT_EQ(device_id1, device_id2); EXPECT_NE(label1, label2); // Check that we now have two opened streams. @@ -273,7 +273,6 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateThreeStreams) { std::string label3 = host_->label_; std::string device_id3 = host_->video_devices_.front().device_id; EXPECT_EQ(device_id1, device_id3); - EXPECT_NE(device_id2, device_id3); EXPECT_NE(label1, label3); EXPECT_NE(label2, label3); diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 6bf60c9..c3245c8 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -11,14 +11,15 @@ #include "base/logging.h" #include "base/rand_util.h" #include "content/browser/renderer_host/media/audio_input_device_manager.h" -#include "content/browser/renderer_host/media/media_stream_device_settings.h" #include "content/browser/renderer_host/media/media_stream_requester.h" +#include "content/browser/renderer_host/media/media_stream_ui_controller.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_options.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/media_observer.h" #include "content/public/browser/media_request_state.h" +#include "content/public/common/media_stream_request.h" #include "googleurl/src/gurl.h" #if defined(OS_WIN) @@ -82,7 +83,8 @@ void DeviceThread::CleanUp() { class MediaStreamManager::DeviceRequest { public: enum RequestType { - GENERATE_STREAM = 0, + DEVICE_ACCESS = 0, + GENERATE_STREAM, ENUMERATE_DEVICES, OPEN_DEVICE }; @@ -98,31 +100,22 @@ class MediaStreamManager::DeviceRequest { DeviceRequest(MediaStreamRequester* requester, const StreamOptions& request_options, + RequestType request_type, int render_process_id, int render_view_id, const GURL& request_security_origin) : requester(requester), options(request_options), - type(GENERATE_STREAM), + type(request_type), render_process_id(render_process_id), render_view_id(render_view_id), security_origin(request_security_origin), state_(content::NUM_MEDIA_TYPES, content::MEDIA_REQUEST_STATE_NOT_REQUESTED) { - DCHECK(requester); } ~DeviceRequest() {} - MediaStreamRequester* requester; - StreamOptions options; - RequestType type; - int render_process_id; - int render_view_id; - GURL security_origin; - std::string requested_device_id; - StreamDeviceInfoArray devices; - // Update the request state and notify observers. void setState(MediaStreamType stream_type, content::MediaRequestState new_state) { @@ -149,6 +142,20 @@ class MediaStreamManager::DeviceRequest { return state_[stream_type]; } + MediaStreamRequester* requester; // Can be NULL. + StreamOptions options; + RequestType type; + int render_process_id; + int render_view_id; + GURL security_origin; + std::string requested_device_id; + StreamDeviceInfoArray devices; + + // Callback to the requester which audio/video devices have been selected. + // It can be null if the requester has no interest to know the result. + // Currently it is only used by |DEVICE_ACCESS| type. + media_stream::MediaRequestResponseCallback callback; + private: std::vector<content::MediaRequestState> state_; }; @@ -169,7 +176,7 @@ void MediaStreamManager::AlwaysUseFakeDevice() { MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager) : ALLOW_THIS_IN_INITIALIZER_LIST( - device_settings_(new MediaStreamDeviceSettings(this))), + ui_controller_(new content::MediaStreamUIController(this))), audio_manager_(audio_manager), monitoring_started_(false), io_loop_(NULL) { @@ -198,6 +205,31 @@ AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() { return audio_input_device_manager_; } +void MediaStreamManager::MakeMediaAccessRequest( + int render_process_id, + int render_view_id, + const StreamOptions& options, + const GURL& security_origin, + const MediaRequestResponseCallback& callback, + std::string* label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Create a new request based on options. + DeviceRequest new_request(NULL, + options, + DeviceRequest::DEVICE_ACCESS, + render_process_id, + render_view_id, + security_origin); + StartEnumeration(&new_request, label); + + // TODO(xians), remove this silly code after AddRequest does not copy + // the struct by value. + DeviceRequest& request = requests_[*label]; + request.callback = callback; + + PostRequestToUI(*label); +} + void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, int render_process_id, int render_view_id, @@ -210,17 +242,14 @@ void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, // Create a new request based on options. DeviceRequest new_request(requester, options, + DeviceRequest::GENERATE_STREAM, render_process_id, render_view_id, security_origin); StartEnumeration(&new_request, label); // Get user confirmation to use capture devices. - device_settings_->RequestCaptureDeviceUsage(*label, - render_process_id, - render_view_id, - options, - security_origin); + PostRequestToUI(*label); } void MediaStreamManager::GenerateStreamForDevice( @@ -231,6 +260,7 @@ void MediaStreamManager::GenerateStreamForDevice( // Create a new request based on options. AddRequest(DeviceRequest(requester, options, + DeviceRequest::GENERATE_STREAM, render_process_id, render_view_id, security_origin), label); @@ -238,11 +268,7 @@ void MediaStreamManager::GenerateStreamForDevice( request.requested_device_id = device_id; // Get user confirmation to use the capture device. - device_settings_->RequestCaptureDeviceUsage(*label, - render_process_id, - render_view_id, - options, - security_origin); + PostRequestToUI(*label); if (!security_origin.SchemeIs(kExtensionScheme) || (options.audio_type != content::MEDIA_TAB_AUDIO_CAPTURE && @@ -251,7 +277,7 @@ void MediaStreamManager::GenerateStreamForDevice( options.video_type != content::MEDIA_NO_SERVICE)) { LOG(ERROR) << "Invalid request or used tab capture outside extension API."; BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&MediaStreamManager::CancelGenerateStream, + base::Bind(&MediaStreamManager::CancelRequest, base::Unretained(this), *label)); return; } @@ -287,13 +313,17 @@ void MediaStreamManager::GenerateStreamForDevice( base::Unretained(this), *label, devices)); } -void MediaStreamManager::CancelGenerateStream(const std::string& label) { +void MediaStreamManager::CancelRequest(const std::string& label) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { - // The request isn't complete. + // The request isn't complete, notify the UI immediately. + ui_controller_->CancelUIRequest(label); + if (!RequestDone(it->second)) { + // TODO(xians): update the |state| to STATE_DONE to trigger a state + // changed notification to UI before deleting the request? DeviceRequest& request = it->second; for (int i = content::MEDIA_NO_SERVICE + 1; i < content::NUM_MEDIA_TYPES; ++i) { @@ -310,11 +340,12 @@ void MediaStreamManager::CancelGenerateStream(const std::string& label) { } } } + + // Delete the request. requests_.erase(it); } else { StopGeneratedStream(label); } - device_settings_->RemovePendingCaptureRequest(label); } } @@ -342,8 +373,14 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { it->second.setState(static_cast<MediaStreamType>(i), content::MEDIA_REQUEST_STATE_CLOSING); } - NotifyObserverDevicesClosed(&(it->second)); + NotifyDevicesClosed(it->second); } + + // If request isn't complete, notify the UI on the cancellation. And it + // is also safe to call CancelUIRequest if the request has been done. + ui_controller_->CancelUIRequest(label); + + // Delete the request now. requests_.erase(it); } } @@ -374,10 +411,10 @@ void MediaStreamManager::EnumerateDevices( } DeviceRequest new_request(requester, options, + DeviceRequest::ENUMERATE_DEVICES, render_process_id, render_view_id, security_origin); - new_request.type = DeviceRequest::ENUMERATE_DEVICES; if (cache->valid) { // Cached device list of this type exists. Just send it out. @@ -432,15 +469,35 @@ void MediaStreamManager::OpenDevice( } DeviceRequest new_request(requester, options, + DeviceRequest::OPEN_DEVICE, render_process_id, render_view_id, security_origin); - new_request.type = DeviceRequest::OPEN_DEVICE; new_request.requested_device_id = device_id; StartEnumeration(&new_request, label); } +void MediaStreamManager::NotifyUIDevicesOpened( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ui_controller_->NotifyUIIndicatorDevicesOpened(render_process_id, + render_view_id, + devices); +} + +void MediaStreamManager::NotifyUIDevicesClosed( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ui_controller_->NotifyUIIndicatorDevicesClosed(render_process_id, + render_view_id, + devices); +} + void MediaStreamManager::SendCachedDeviceList( EnumerationCache* cache, const std::string& label) { @@ -514,6 +571,18 @@ void MediaStreamManager::AddRequest( (*label) = request_label; } +void MediaStreamManager::PostRequestToUI(const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DeviceRequest& request = requests_[label]; + // Get user confirmation to use capture devices. + ui_controller_->MakeUIRequest(label, + request.render_process_id, + request.render_view_id, + request.options, + request.security_origin); +} + void MediaStreamManager::EnsureDeviceManagersStarted() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (device_thread_.get()) @@ -607,7 +676,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, } request->requester->StreamGenerated(label, audio_devices, video_devices); - NotifyObserverDevicesOpened(request); + NotifyDevicesOpened(*request); break; } default: @@ -682,7 +751,8 @@ void MediaStreamManager::DevicesEnumerated( } break; default: - device_settings_->AvailableDevices(*it, stream_type, devices); + ui_controller_->AddAvailableDevicesToRequest(*it, stream_type, + devices); break; } } @@ -731,7 +801,9 @@ void MediaStreamManager::Error(MediaStreamType stream_type, if (devices.size() <= 1) { // 1. Device not opened and no other devices for this request -> // signal stream error and remove the request. - it->second.requester->StreamGenerationFailed(it->first); + if (it->second.requester) + it->second.requester->StreamGenerationFailed(it->first); + requests_.erase(it); } else { // 2. Not opened but other devices exists for this request -> remove @@ -747,6 +819,7 @@ void MediaStreamManager::Error(MediaStreamType stream_type, void MediaStreamManager::DevicesAccepted(const std::string& label, const StreamDeviceInfoArray& devices) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!devices.empty()); DeviceRequests::iterator request_it = requests_.find(label); if (request_it == requests_.end()) { return; @@ -754,9 +827,20 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, DeviceRequest& request = request_it->second; - if (devices.empty()) { - // No available devices or user didn't accept device usage. - request.requester->StreamGenerationFailed(request_it->first); + if (request.type == DeviceRequest::DEVICE_ACCESS) { + if (!request.callback.is_null()) { + // Map the devices to MediaStreamDevices. + content::MediaStreamDevices selected_devices; + for (StreamDeviceInfoArray::const_iterator it = devices.begin(); + it != devices.end(); ++it) { + selected_devices.push_back(content::MediaStreamDevice( + it->stream_type, it->device_id, it->name)); + } + + request.callback.Run(label, selected_devices); + } + + // Delete the request since it is done. requests_.erase(request_it); return; } @@ -802,18 +886,24 @@ void MediaStreamManager::SettingsError(const std::string& label) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Erase this request and report an error. DeviceRequests::iterator it = requests_.find(label); - if (it != requests_.end()) { - DCHECK_EQ(it->second.type, DeviceRequest::GENERATE_STREAM); - it->second.requester->StreamGenerationFailed(label); - requests_.erase(it); + if (it == requests_.end()) return; - } + + // Notify the users about the request result. + DeviceRequest& request = it->second; + if (request.requester) + it->second.requester->StreamGenerationFailed(label); + + if (request.type == DeviceRequest::DEVICE_ACCESS) + request.callback.Run(label, content::MediaStreamDevices()); + + requests_.erase(it); } void MediaStreamManager::UseFakeDevice() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); video_capture_manager()->UseFakeDevice(); - device_settings_->UseFakeUI(); + ui_controller_->UseFakeUI(); } void MediaStreamManager::WillDestroyCurrentMessageLoop() { @@ -830,40 +920,37 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() { audio_input_device_manager_ = NULL; video_capture_manager_ = NULL; io_loop_ = NULL; - device_settings_.reset(); + ui_controller_.reset(); } -void MediaStreamManager::NotifyObserverDevicesOpened(DeviceRequest* request) { - content::MediaObserver* media_observer = - content::GetContentClient()->browser()->GetMediaObserver(); - if (media_observer == NULL) - return; +void MediaStreamManager::NotifyDevicesOpened(const DeviceRequest& request) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); content::MediaStreamDevices opened_devices; DevicesFromRequest(request, &opened_devices); - DCHECK(!opened_devices.empty()); - media_observer->OnCaptureDevicesOpened(request->render_process_id, - request->render_view_id, - opened_devices); + if (opened_devices.empty()) + return; + + NotifyUIDevicesOpened(request.render_process_id, + request.render_view_id, + opened_devices); } -void MediaStreamManager::NotifyObserverDevicesClosed(DeviceRequest* request) { - content::MediaObserver* media_observer = - content::GetContentClient()->browser()->GetMediaObserver(); - if (media_observer == NULL) - return; +void MediaStreamManager::NotifyDevicesClosed(const DeviceRequest& request) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); content::MediaStreamDevices closed_devices; DevicesFromRequest(request, &closed_devices); if (closed_devices.empty()) return; - media_observer->OnCaptureDevicesClosed(request->render_process_id, - request->render_view_id, - closed_devices); + + NotifyUIDevicesClosed(request.render_process_id, + request.render_view_id, + closed_devices); } void MediaStreamManager::DevicesFromRequest( - DeviceRequest* request, content::MediaStreamDevices* devices) { - for (StreamDeviceInfoArray::const_iterator it = request->devices.begin(); - it != request->devices.end(); ++it) { + const DeviceRequest& request, content::MediaStreamDevices* devices) { + for (StreamDeviceInfoArray::const_iterator it = request.devices.begin(); + it != request.devices.end(); ++it) { devices->push_back(content::MediaStreamDevice( it->stream_type, it->device_id, it->name)); } diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h index 5faa780..7db8a37 100644 --- a/content/browser/renderer_host/media/media_stream_manager.h +++ b/content/browser/renderer_host/media/media_stream_manager.h @@ -6,18 +6,19 @@ // supported now). Call flow: // 1. GenerateStream is called when a render process wants to use a capture // device. -// 2. MediaStreamManager will ask MediaStreamDeviceSettings for permission to +// 2. MediaStreamManager will ask MediaStreamUIController for permission to // use devices and for which device to use. // 3. MediaStreamManager will request the corresponding media device manager(s) // to enumerate available devices. The result will be given to -// MediaStreamDeviceSettings. -// 4. MediaStreamDeviceSettings will, by using user settings, pick devices which -// devices to use and let MediaStreamManager know the result. +// MediaStreamUIController. +// 4. MediaStreamUIController will, by posting the request to UI, let the +// users to select which devices to use and send callback to +// MediaStreamManager with the result. // 5. MediaStreamManager will call the proper media device manager to open the // device and let the MediaStreamRequester know it has been done. // When enumeration and open are done in separate operations, -// MediaStreamDeviceSettings is not involved as in steps. +// MediaStreamUIController is not involved as in steps. #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_MANAGER_H_ #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_MANAGER_H_ @@ -45,6 +46,10 @@ class ScopedCOMInitializer; } #endif +namespace content { +class MediaStreamUIController; +} + namespace media { class AudioManager; } @@ -95,6 +100,20 @@ class CONTENT_EXPORT MediaStreamManager // Used to access AudioInputDeviceManager. AudioInputDeviceManager* audio_input_device_manager(); + // Creates a new media access request which is identified by a unique |label| + // that's returned to the caller. This will trigger the infobar and ask users + // for access to the device. |render_process_id| and |render_view_id| refer + // to the view where the infobar will appear to the user. |callback| is + // used to send the selected device to the clients. An empty list of device + // will be returned if the users deny the access. + void MakeMediaAccessRequest( + int render_process_id, + int render_view_id, + const StreamOptions& components, + const GURL& security_origin, + const MediaRequestResponseCallback& callback, + std::string* label); + // GenerateStream opens new media devices according to |components|. It // creates a new request which is identified by a unique |label| that's // returned to the caller. |render_process_id| and |render_view_id| refer to @@ -111,8 +130,7 @@ class CONTENT_EXPORT MediaStreamManager const std::string& device_id, const GURL& security_origin, std::string* label); - // Cancel generate stream. - void CancelGenerateStream(const std::string& label); + void CancelRequest(const std::string& label); // Closes generated stream. void StopGeneratedStream(const std::string& label); @@ -143,6 +161,18 @@ class CONTENT_EXPORT MediaStreamManager const GURL& security_origin, std::string* label); + // Signals the UI that the devices are opened. + // Users are responsible for calling NotifyUIDevicesClosed when the devices + // are not used anymore, otherwise UI will leak. + void NotifyUIDevicesOpened(int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices); + + // Signals the UI that the devices are being closed. + void NotifyUIDevicesClosed(int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices); + // Implements MediaStreamProviderListener. virtual void Opened(MediaStreamType stream_type, int capture_session_id) OVERRIDE; @@ -191,9 +221,9 @@ class CONTENT_EXPORT MediaStreamManager // Helpers for signaling the media observer that new capture devices are // opened/closed. - void NotifyObserverDevicesOpened(DeviceRequest* request); - void NotifyObserverDevicesClosed(DeviceRequest* request); - void DevicesFromRequest(DeviceRequest* request, + void NotifyDevicesOpened(const DeviceRequest& request); + void NotifyDevicesClosed(const DeviceRequest& request); + void DevicesFromRequest(const DeviceRequest& request, content::MediaStreamDevices* devices); // Helpers. @@ -205,6 +235,7 @@ class CONTENT_EXPORT MediaStreamManager bool HasEnumerationRequest(MediaStreamType type); bool HasEnumerationRequest(); void ClearEnumerationCache(EnumerationCache* cache); + void PostRequestToUI(const std::string& label); // Helper to create the device managers, if needed. Auto-starts the device // thread and registers this as a listener with the device managers. @@ -224,7 +255,7 @@ class CONTENT_EXPORT MediaStreamManager // Device thread shared by VideoCaptureManager and AudioInputDeviceManager. scoped_ptr<DeviceThread> device_thread_; - scoped_ptr<MediaStreamDeviceSettings> device_settings_; + scoped_ptr<content::MediaStreamUIController> ui_controller_; media::AudioManager* const audio_manager_; // not owned scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_; diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc new file mode 100644 index 0000000..9ad4c87 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/bind.h" +#include "base/message_loop.h" +#include "content/browser/browser_thread_impl.h" +#include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/common/media/media_stream_options.h" +#include "media/audio/audio_manager_base.h" +#if defined(OS_ANDROID) +#include "media/audio/android/audio_manager_android.h" +#elif defined(OS_LINUX) || defined(OS_OPENBSD) +#include "media/audio/linux/audio_manager_linux.h" +#elif defined(OS_MACOSX) +#include "media/audio/mac/audio_manager_mac.h" +#elif defined(OS_WIN) +#include "media/audio/win/audio_manager_win.h" +#endif +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; +using content::BrowserThreadImpl; +using testing::_; + +namespace content { + +#if defined(OS_LINUX) || defined(OS_OPENBSD) +typedef media::AudioManagerLinux AudioManagerPlatform; +#elif defined(OS_MACOSX) +typedef media::AudioManagerMac AudioManagerPlatform; +#elif defined(OS_WIN) +typedef media::AudioManagerWin AudioManagerPlatform; +#elif defined(OS_ANDROID) +typedef media::AudioManagerAndroid AudioManagerPlatform; +#endif + + +// This class mocks the audio manager and overrides the +// GetAudioInputDeviceNames() method to ensure that we can run our tests on +// the buildbots. media::AudioManagerBase +class MockAudioManager : public AudioManagerPlatform { + public: + MockAudioManager() { + Init(); + } + + virtual ~MockAudioManager() {} + + virtual void GetAudioInputDeviceNames( + media::AudioDeviceNames* device_names) OVERRIDE { + if (HasAudioInputDevices()) { + AudioManagerBase::GetAudioInputDeviceNames(device_names); + } else { + device_names->push_back(media::AudioDeviceName("fake_device_name", + "fake_device_id")); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(MockAudioManager); +}; + +class MediaStreamManagerTest : public ::testing::Test { + public: + MediaStreamManagerTest() {} + + MOCK_METHOD1(Response, void(const std::string&)); + void ResponseCallback(const std::string& label, + const MediaStreamDevices& devices) { + Response(label); + message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); + } + + void WaitForResult() { message_loop_->Run(); } + + protected: + virtual void SetUp() { + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI, + message_loop_.get())); + io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO, + message_loop_.get())); + + // Create our own MediaStreamManager. + audio_manager_.reset(new MockAudioManager()); + media_stream_manager_.reset( + new media_stream::MediaStreamManager(audio_manager_.get())); + + // Use fake devices in order to run on the bots. + media_stream_manager_->UseFakeDevice(); + } + + virtual void TearDown() { + message_loop_->RunAllPending(); + + // Delete the IO message loop to clean up the MediaStreamManager. + message_loop_.reset(); + } + + void MakeMediaAccessRequest(std::string* label) { + const int render_process_id = 1; + const int render_view_id = 1; + media_stream::StreamOptions components(true, true); + const GURL security_origin; + media_stream::MediaRequestResponseCallback callback = + base::Bind(&MediaStreamManagerTest::ResponseCallback, + base::Unretained(this)); + media_stream_manager_->MakeMediaAccessRequest(render_process_id, + render_view_id, + components, + security_origin, + callback, + label); + } + + scoped_ptr<MessageLoop> message_loop_; + scoped_ptr<BrowserThreadImpl> ui_thread_; + scoped_ptr<BrowserThreadImpl> io_thread_; + scoped_ptr<media::AudioManager> audio_manager_; + scoped_ptr<media_stream::MediaStreamManager> media_stream_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(MediaStreamManagerTest); +}; + +TEST_F(MediaStreamManagerTest, MakeMediaAccessRequest) { + std::string label; + MakeMediaAccessRequest(&label); + + // Expecting the callback will be triggered and quit the test. + EXPECT_CALL(*this, Response(label)); + WaitForResult(); +} + +TEST_F(MediaStreamManagerTest, MakeAndCancelMediaAccessRequest) { + std::string label; + MakeMediaAccessRequest(&label); + // No callback is expected. + media_stream_manager_->CancelRequest(label); +} + +TEST_F(MediaStreamManagerTest, MakeMultipleRequests) { + // First request. + std::string label1; + MakeMediaAccessRequest(&label1); + + // Second request. + std::string label2; + int render_process_id = 2; + int render_view_id = 2; + media_stream::StreamOptions components(true, true); + GURL security_origin; + media_stream::MediaRequestResponseCallback callback = + base::Bind(&MediaStreamManagerTest::ResponseCallback, + base::Unretained(this)); + media_stream_manager_->MakeMediaAccessRequest(render_process_id, + render_view_id, + components, + security_origin, + callback, + &label2); + + // Expecting the callbackS from requests will be triggered and quit the test. + // Note, the callbacks might come in a different order depending on the + // value of labels. + EXPECT_CALL(*this, Response(_)).Times(2); + WaitForResult(); +} + +TEST_F(MediaStreamManagerTest, MakeAndCancelMultipleRequests) { + std::string label1; + MakeMediaAccessRequest(&label1); + std::string label2; + MakeMediaAccessRequest(&label2); + media_stream_manager_->CancelRequest(label1); + + // Expecting the callback from the second request will be triggered and + // quit the test. + EXPECT_CALL(*this, Response(label2)); + WaitForResult(); +} + +} // namespace content diff --git a/content/browser/renderer_host/media/media_stream_ui_controller.cc b/content/browser/renderer_host/media/media_stream_ui_controller.cc new file mode 100644 index 0000000..4c99f11 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_ui_controller.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/media/media_stream_ui_controller.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "content/browser/renderer_host/media/media_stream_settings_requester.h" +#include "content/browser/renderer_host/render_view_host_delegate.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/common/media/media_stream_options.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/media_observer.h" +#include "content/public/common/media_stream_request.h" +#include "googleurl/src/gurl.h" + +using content::BrowserThread; +using content::MediaStreamDevice; +using content::MediaStreamRequest; + +namespace { + +// Helper class to handle the callbacks to a MediaStreamUIController instance. +// This class will make sure that the call to PostResponse is executed on the IO +// thread (and that the instance of MediaStreamUIController still exists). +// This allows us to pass a simple base::Callback object to any class that needs +// to post a response to the MediaStreamUIController object. This logic cannot +// be implemented inside MediaStreamUIController::PostResponse since that +// would imply that the WeakPtr<MediaStreamUIController> pointer has been +// dereferenced already (which would cause an error in the ThreadChecker before +// we even get there). +class ResponseCallbackHelper + : public base::RefCountedThreadSafe<ResponseCallbackHelper> { + public: + explicit ResponseCallbackHelper( + base::WeakPtr<content::MediaStreamUIController> controller) + : controller_(controller) { + } + + void PostResponse(const std::string& label, + const content::MediaStreamDevices& devices) { + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&content::MediaStreamUIController::PostResponse, + controller_, label, devices)); + return; + } else if (controller_) { + controller_->PostResponse(label, devices); + } + } + + private: + friend class base::RefCountedThreadSafe<ResponseCallbackHelper>; + ~ResponseCallbackHelper() {} + + base::WeakPtr<content::MediaStreamUIController> controller_; + + DISALLOW_COPY_AND_ASSIGN(ResponseCallbackHelper); +}; + +} // namespace + +namespace content { + +// UI request contains all data needed to keep track of requests between the +// different calls. +class MediaStreamRequestForUI : public content::MediaStreamRequest { + public: + MediaStreamRequestForUI( + int render_pid, + int render_vid, + const GURL& origin, + const media_stream::StreamOptions& options) + : MediaStreamRequest(render_pid, render_vid, origin), + wait_for_audio(IsAudioMediaType(options.audio_type)), + wait_for_video(IsVideoMediaType(options.video_type)), + posted_task(false) { + DCHECK(wait_for_audio || wait_for_video); + } + + ~MediaStreamRequestForUI() {} + + bool IsRequestReadyForView() const { + if (wait_for_audio || wait_for_video) + return false; + + // We have got all the requested devices, it is ready if it has not + // been posted for UI yet. + return !posted_task; + } + + // Flags to indicate if we should wait for enumerated device lists. + bool wait_for_audio; + bool wait_for_video; + + // Whether or not a task was posted to make the call to + // RequestMediaAccessPermission, to make sure that we never post twice to it. + bool posted_task; +}; + +namespace { + +// Sends the request to the appropriate WebContents. +void ProceedMediaAccessPermission(const MediaStreamRequestForUI& request, + const MediaResponseCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Send the permission request to the web contents. + content::RenderViewHostImpl* host = + content::RenderViewHostImpl::FromID(request.render_process_id, + request.render_view_id); + + // Tab may have gone away. + if (!host || !host->GetDelegate()) { + callback.Run(content::MediaStreamDevices()); + return; + } + + host->GetDelegate()->RequestMediaAccessPermission(&request, callback); +} + +} // namespace + +MediaStreamUIController::MediaStreamUIController( + media_stream::SettingsRequester* requester) + : requester_(requester), + use_fake_ui_(false), + weak_ptr_factory_(this) { + DCHECK(requester_); +} + +MediaStreamUIController::~MediaStreamUIController() { + DCHECK(requests_.empty()); +} + +void MediaStreamUIController::MakeUIRequest( + const std::string& label, int render_process_id, int render_view_id, + const media_stream::StreamOptions& request_options, + const GURL& security_origin) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Create a new request. + if (!requests_.insert( + std::make_pair(label, new MediaStreamRequestForUI( + render_process_id, render_view_id, security_origin, + request_options))).second) { + NOTREACHED(); + } +} + +void MediaStreamUIController::CancelUIRequest(const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + UIRequests::iterator request_iter = requests_.find(label); + if (request_iter != requests_.end()) { + // Proceed the next pending request for the same page. + scoped_ptr<MediaStreamRequestForUI> request(request_iter->second); + int render_view_id = request->render_view_id; + int render_process_id = request->render_process_id; + bool was_posted = request->posted_task; + + // TODO(xians): Post a cancel request on UI thread to dismiss the infobar + // if request has been sent to the UI. + // Remove the request from the queue. + requests_.erase(request_iter); + + // Simply return if the canceled request has not been brought to UI. + if (!was_posted) + return; + + // Process the next pending request to replace the old infobar on the same + // page. + ProcessNextRequestForView(render_process_id, render_view_id); + } +} + +void MediaStreamUIController::AddAvailableDevicesToRequest( + const std::string& label, + MediaStreamDeviceType stream_type, + const media_stream::StreamDeviceInfoArray& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + UIRequests::const_iterator request_iter = requests_.find(label); + if (request_iter == requests_.end()) { + NOTREACHED(); + return; + } + + // Add the available devices to the request. + MediaStreamRequestForUI* request = request_iter->second; + + // Create the simplified list of devices. + content::MediaStreamDevices& requested_devices = + request->devices[stream_type]; + DCHECK(requested_devices.empty()); + for (media_stream::StreamDeviceInfoArray::const_iterator it = devices.begin(); + it != devices.end(); + ++it) { + requested_devices.push_back(MediaStreamDevice(stream_type, + it->device_id, + it->name)); + } + + if (IsAudioMediaType(stream_type)) { + DCHECK(request->wait_for_audio); + request->wait_for_audio = false; + } else if (IsVideoMediaType(stream_type)) { + DCHECK(request->wait_for_video); + request->wait_for_video = false; + } else { + NOTREACHED(); + } + + if (request->IsRequestReadyForView()) { + if (use_fake_ui_) { + PostRequestToFakeUI(label); + return; + } + + // The UI can handle only one request at the time, do not post the + // request to the view if the UI is handling any other request. + if (IsUIBusy(request->render_process_id, request->render_view_id)) + return; + + PostRequestToUI(label); + } +} + +void MediaStreamUIController::PostResponse( + const std::string& label, + const content::MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + UIRequests::iterator request_iter = requests_.find(label); + // Return if the request has been removed. + if (request_iter == requests_.end()) + return; + + DCHECK(requester_); + scoped_ptr<MediaStreamRequestForUI> request(request_iter->second); + requests_.erase(request_iter); + + // Look for queued requests for the same view. If there is a pending request, + // post it for user approval. + ProcessNextRequestForView(request->render_process_id, + request->render_view_id); + + if (devices.size() > 0) { + // Build a list of "full" device objects for the accepted devices. + media_stream::StreamDeviceInfoArray device_list; + // TODO(xians): figure out if it is all right to hard code in_use to false, + // though DevicesAccepted seems to do so. + for (MediaStreamDevices::const_iterator dev = devices.begin(); + dev != devices.end(); ++dev) { + device_list.push_back(media_stream::StreamDeviceInfo( + dev->type, dev->name, dev->device_id, false)); + } + + requester_->DevicesAccepted(label, device_list); + } else { + requester_->SettingsError(label); + } +} + +void MediaStreamUIController::NotifyUIIndicatorDevicesOpened( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!devices.empty()); + content::MediaObserver* media_observer = + content::GetContentClient()->browser()->GetMediaObserver(); + if (media_observer == NULL) + return; + + media_observer->OnCaptureDevicesOpened(render_process_id, + render_view_id, + devices); +} + +void MediaStreamUIController::NotifyUIIndicatorDevicesClosed( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!devices.empty()); + content::MediaObserver* media_observer = + content::GetContentClient()->browser()->GetMediaObserver(); + if (media_observer == NULL) + return; + + media_observer->OnCaptureDevicesClosed(render_process_id, + render_view_id, + devices); +} + +void MediaStreamUIController::UseFakeUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + use_fake_ui_ = true; +} + +bool MediaStreamUIController::IsUIBusy(int render_process_id, + int render_view_id) { + for (UIRequests::iterator it = requests_.begin(); + it != requests_.end(); ++it) { + if (it->second->render_process_id == render_process_id && + it->second->render_view_id == render_view_id && + it->second->posted_task) { + return true; + } + } + return false; +} + +void MediaStreamUIController::ProcessNextRequestForView( + int render_process_id, int render_view_id) { + std::string next_request_label; + for (UIRequests::iterator it = requests_.begin(); it != requests_.end(); + ++it) { + if (it->second->render_process_id == render_process_id && + it->second->render_view_id == render_view_id) { + // This request belongs to the given render view. + if (it->second->IsRequestReadyForView()) { + next_request_label = it->first; + break; + } + } + } + + if (next_request_label.empty()) + return; + + if (use_fake_ui_) + PostRequestToFakeUI(next_request_label); + else + PostRequestToUI(next_request_label); +} + +void MediaStreamUIController::PostRequestToUI(const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + UIRequests::iterator request_iter = requests_.find(label); + if (request_iter == requests_.end()) { + NOTREACHED(); + return; + } + MediaStreamRequestForUI* request = request_iter->second; + DCHECK(request != NULL); + + request->posted_task = true; + + scoped_refptr<ResponseCallbackHelper> helper = + new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); + content::MediaResponseCallback callback = + base::Bind(&ResponseCallbackHelper::PostResponse, + helper.get(), label); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&ProceedMediaAccessPermission, *request, callback)); +} + +void MediaStreamUIController::PostRequestToFakeUI(const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + UIRequests::iterator request_iter = requests_.find(label); + DCHECK(request_iter != requests_.end()); + MediaStreamRequestForUI* request = request_iter->second; + // Used to fake UI, which is needed for server based testing. + // Use first device for each media type. + MediaStreamDevices devices_to_use; + for (MediaStreamDeviceMap::const_iterator it = request->devices.begin(); + it != request->devices.end(); ++it) { + // Use the first capture device in the list for the fake UI. + if (!it->second.empty()) { + devices_to_use.push_back(it->second.front()); + } + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&MediaStreamUIController::PostResponse, + weak_ptr_factory_.GetWeakPtr(), label, devices_to_use)); +} + +} // namespace media_stream diff --git a/content/browser/renderer_host/media/media_stream_ui_controller.h b/content/browser/renderer_host/media/media_stream_ui_controller.h new file mode 100644 index 0000000..a7d68dc --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_ui_controller.h @@ -0,0 +1,123 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// MediaStreamUIController is used to decide which of the available capture +// device to use as well as getting user permission to use the capture device. +// There will be one instance of MediaStreamDeviceSettings handling all +// requests. + +// Expected call flow: +// 1. MakeUIRequest() is called to create a new request to the UI for capture +// device access. +// 2. AddAvailableDevicesToRequest() is called with a list of currently +// available devices. +// 3. Pick device and get user confirmation. +// 4. Confirm by calling SettingsRequester::DevicesAccepted(). +// Repeat step 1 - 4 for new device requests. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_CONTROLLER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_CONTROLLER_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/renderer_host/media/media_stream_provider.h" +#include "content/public/browser/web_contents_delegate.h" + + +namespace media_stream { +class SettingsRequester; +} + +namespace content { + +class MediaStreamRequestForUI; + +// MediaStreamUIController is responsible for getting user permission to use +// a media capture device as well as selecting what device to use. +class CONTENT_EXPORT MediaStreamUIController { + public: + explicit MediaStreamUIController(media_stream::SettingsRequester* requester); + virtual ~MediaStreamUIController(); + + // Called when a new request for the capture device access is made. + // Users are responsbile for cancel the pending request if they don't wait + // for the result from the UI. + void MakeUIRequest(const std::string& label, + int render_process_id, + int render_view_id, + const media_stream::StreamOptions& stream_components, + const GURL& security_origin); + + // Called to cancel a pending UI request of capture device access when the + // user has no action for the media stream InfoBar. + void CancelUIRequest(const std::string& label); + + // Called to pass in an array of available devices for a request represented + // by |label|. There could be multiple calls for a request. + // TODO(xians): use the monitor to get a up-to-date device list and remove + // this API. + void AddAvailableDevicesToRequest( + const std::string& label, + MediaStreamDeviceType stream_type, + const media_stream::StreamDeviceInfoArray& devices); + + // Called by the InfoBar when the user grants/denies access to some devices + // to the webpage. This is placed here, so the request can be cleared from the + // list of pending requests, instead of letting the InfoBar itself respond to + // the requester. An empty list of devices means that access has been denied. + // This method must be called on the IO thread. + void PostResponse(const std::string& label, + const content::MediaStreamDevices& devices); + + // Called to signal the UI indicator that the devices are opened. + void NotifyUIIndicatorDevicesOpened( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices); + + // Called to signal the UI indicator that the devices are closed. + void NotifyUIIndicatorDevicesClosed( + int render_process_id, + int render_view_id, + const content::MediaStreamDevices& devices); + + // Used for testing only. This function is called to use faked UI, which is + // needed for server based tests. The first non-opened device(s) will be + // picked. + void UseFakeUI(); + + private: + typedef std::map<std::string, MediaStreamRequestForUI*> UIRequests; + + // Returns true if the UI is already processing a request for this render + // view. + bool IsUIBusy(int render_process_id, int render_view_id); + + // Process the next pending request and bring it up to the UI on the given + // page for user approval. + void ProcessNextRequestForView(int render_process_id, int render_view_id); + + // Posts a request to be approved/denied by UI. + void PostRequestToUI(const std::string& label); + + // Posts a request to fake UI which is used for testing purpose. + void PostRequestToFakeUI(const std::string& label); + + media_stream::SettingsRequester* requester_; + UIRequests requests_; + + // See comment above for method UseFakeUI. Used for automated testing. + bool use_fake_ui_; + + base::WeakPtrFactory<MediaStreamUIController> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamUIController); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_CONTROLLER_H_ diff --git a/content/browser/renderer_host/media/media_stream_device_settings_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc index 090a489..214ef16 100644 --- a/content/browser/renderer_host/media/media_stream_device_settings_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc @@ -7,9 +7,10 @@ #include "base/bind.h" #include "base/message_loop.h" #include "content/browser/browser_thread_impl.h" -#include "content/browser/renderer_host/media/media_stream_device_settings.h" +#include "content/browser/renderer_host/media/media_stream_ui_controller.h" #include "content/browser/renderer_host/media/media_stream_settings_requester.h" #include "content/common/media/media_stream_options.h" +#include "content/public/common/media_stream_request.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,18 +18,17 @@ using content::BrowserThread; using content::BrowserThreadImpl; using testing::_; -namespace media_stream { +namespace content { -// Test class. -class MediaStreamDeviceSettingsTest +class MediaStreamDeviceUIControllerTest : public ::testing::Test, - public SettingsRequester { + public media_stream::SettingsRequester { public: - MediaStreamDeviceSettingsTest() {} + MediaStreamDeviceUIControllerTest() {} // Mock implementation of SettingsRequester; - MOCK_METHOD2(DevicesAccepted, void(const std::string&, - const StreamDeviceInfoArray&)); + MOCK_METHOD2(DevicesAccepted, void( + const std::string&, const media_stream::StreamDeviceInfoArray&)); MOCK_METHOD1(SettingsError, void(const std::string&)); protected: @@ -38,7 +38,7 @@ class MediaStreamDeviceSettingsTest message_loop_.get())); io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO, message_loop_.get())); - device_settings_.reset(new MediaStreamDeviceSettings(this)); + ui_controller_.reset(new content::MediaStreamUIController(this)); } virtual void TearDown() { @@ -48,13 +48,13 @@ class MediaStreamDeviceSettingsTest void CreateDummyRequest(const std::string& label, bool audio, bool video) { int dummy_render_process_id = 1; int dummy_render_view_id = 1; - StreamOptions components(audio, video); + media_stream::StreamOptions components(audio, video); GURL security_origin; - device_settings_->RequestCaptureDeviceUsage(label, - dummy_render_process_id, - dummy_render_view_id, - components, - security_origin); + ui_controller_->MakeUIRequest(label, + dummy_render_process_id, + dummy_render_view_id, + components, + security_origin); if (audio) CreateAudioDeviceForRequset(label); @@ -70,9 +70,10 @@ class MediaStreamDeviceSettingsTest dummy_audio_device.stream_type = content::MEDIA_DEVICE_AUDIO_CAPTURE; dummy_audio_device.session_id = 1; audio_device_array[0] = dummy_audio_device; - device_settings_->AvailableDevices(label, - dummy_audio_device.stream_type, - audio_device_array); + ui_controller_->AddAvailableDevicesToRequest( + label, + dummy_audio_device.stream_type, + audio_device_array); } void CreateVideoDeviceForRequset(const std::string& label) { @@ -83,21 +84,22 @@ class MediaStreamDeviceSettingsTest dummy_video_device.stream_type = content::MEDIA_DEVICE_VIDEO_CAPTURE; dummy_video_device.session_id = 1; video_device_array[0] = dummy_video_device; - device_settings_->AvailableDevices(label, - dummy_video_device.stream_type, - video_device_array); + ui_controller_->AddAvailableDevicesToRequest( + label, + dummy_video_device.stream_type, + video_device_array); } scoped_ptr<MessageLoop> message_loop_; scoped_ptr<BrowserThreadImpl> ui_thread_; scoped_ptr<BrowserThreadImpl> io_thread_; - scoped_ptr<MediaStreamDeviceSettings> device_settings_; + scoped_ptr<MediaStreamUIController> ui_controller_; private: - DISALLOW_COPY_AND_ASSIGN(MediaStreamDeviceSettingsTest); + DISALLOW_COPY_AND_ASSIGN(MediaStreamDeviceUIControllerTest); }; -TEST_F(MediaStreamDeviceSettingsTest, GenerateRequest) { +TEST_F(MediaStreamDeviceUIControllerTest, GenerateRequest) { const std::string label = "dummy_label"; CreateDummyRequest(label, true, false); @@ -106,16 +108,16 @@ TEST_F(MediaStreamDeviceSettingsTest, GenerateRequest) { EXPECT_CALL(*this, SettingsError(label)); } -TEST_F(MediaStreamDeviceSettingsTest, GenerateAndRemoveRequest) { +TEST_F(MediaStreamDeviceUIControllerTest, GenerateAndRemoveRequest) { const std::string label = "label"; CreateDummyRequest(label, true, false); // Remove the current request, it should not crash. - device_settings_->RemovePendingCaptureRequest(label); + ui_controller_->CancelUIRequest(label); } -TEST_F(MediaStreamDeviceSettingsTest, HandleRequestUsingFakeUI) { - device_settings_->UseFakeUI(); +TEST_F(MediaStreamDeviceUIControllerTest, HandleRequestUsingFakeUI) { + ui_controller_->UseFakeUI(); const std::string label = "label"; CreateDummyRequest(label, true, true); @@ -124,8 +126,8 @@ TEST_F(MediaStreamDeviceSettingsTest, HandleRequestUsingFakeUI) { EXPECT_CALL(*this, DevicesAccepted(label, _)); } -TEST_F(MediaStreamDeviceSettingsTest, CreateMultipleRequestsAndCancelTheFirst) { - device_settings_->UseFakeUI(); +TEST_F(MediaStreamDeviceUIControllerTest, CreateRequestsAndCancelTheFirst) { + ui_controller_->UseFakeUI(); // Create the first audio request. const std::string label_1 = "label_1"; @@ -140,15 +142,15 @@ TEST_F(MediaStreamDeviceSettingsTest, CreateMultipleRequestsAndCancelTheFirst) { CreateDummyRequest(label_3, true, true); // Remove the first request which has been brought to the UI. - device_settings_->RemovePendingCaptureRequest(label_1); + ui_controller_->CancelUIRequest(label_1); // We should get callbacks from the rest of the requests. EXPECT_CALL(*this, DevicesAccepted(label_2, _)); EXPECT_CALL(*this, DevicesAccepted(label_3, _)); } -TEST_F(MediaStreamDeviceSettingsTest, CreateMultipleRequestsAndCancelTheLast) { - device_settings_->UseFakeUI(); +TEST_F(MediaStreamDeviceUIControllerTest, CreateRequestsAndCancelTheLast) { + ui_controller_->UseFakeUI(); // Create the first audio request. const std::string label_1 = "label_1"; @@ -163,7 +165,7 @@ TEST_F(MediaStreamDeviceSettingsTest, CreateMultipleRequestsAndCancelTheLast) { CreateDummyRequest(label_3, true, true); // Remove the last request which is pending in the queue. - device_settings_->RemovePendingCaptureRequest(label_3); + ui_controller_->CancelUIRequest(label_3); // We should get callbacks from the rest of the requests. EXPECT_CALL(*this, DevicesAccepted(label_1, _)); diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc index 27fda13..16c85a8 100644 --- a/content/browser/speech/speech_recognition_manager_impl.cc +++ b/content/browser/speech/speech_recognition_manager_impl.cc @@ -81,7 +81,7 @@ class SpeechRecognitionManagerImpl::PermissionRequest void Abort() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); started_ = false; - BrowserMainLoop::GetMediaStreamManager()->CancelGenerateStream(label_); + BrowserMainLoop::GetMediaStreamManager()->CancelRequest(label_); } int Session() const { return session_id_; } diff --git a/content/common/media/media_stream_options.h b/content/common/media/media_stream_options.h index 5c7d2ac..0256dd1 100644 --- a/content/common/media/media_stream_options.h +++ b/content/common/media/media_stream_options.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/callback.h" #include "content/common/content_export.h" #include "content/public/common/media_stream_request.h" @@ -20,6 +21,12 @@ CONTENT_EXPORT extern const char kMediaStreamSourceTab[]; typedef content::MediaStreamDeviceType MediaStreamType; +// Callback to deliver the result of a media request. |label| is the string +// to identify the request, +typedef base::Callback< void(const std::string&, + const content::MediaStreamDevices&) > + MediaRequestResponseCallback; + // StreamOptions is a Chromium representation of WebKit's // WebUserMediaRequest Options. It describes the components // in a request for a new media stream. diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 735c847..640d5c3 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -603,8 +603,6 @@ 'browser/renderer_host/media/audio_renderer_host.h', 'browser/renderer_host/media/audio_sync_reader.cc', 'browser/renderer_host/media/audio_sync_reader.h', - 'browser/renderer_host/media/media_stream_device_settings.cc', - 'browser/renderer_host/media/media_stream_device_settings.h', 'browser/renderer_host/media/media_stream_dispatcher_host.cc', 'browser/renderer_host/media/media_stream_dispatcher_host.h', 'browser/renderer_host/media/media_stream_manager.cc', @@ -612,6 +610,8 @@ 'browser/renderer_host/media/media_stream_provider.h', 'browser/renderer_host/media/media_stream_requester.h', 'browser/renderer_host/media/media_stream_settings_requester.h', + 'browser/renderer_host/media/media_stream_ui_controller.cc', + 'browser/renderer_host/media/media_stream_ui_controller.h', 'browser/renderer_host/media/video_capture_controller.cc', 'browser/renderer_host/media/video_capture_controller_event_handler.cc', 'browser/renderer_host/media/video_capture_controller_event_handler.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 5a34ae6..6d1e64f 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -293,8 +293,9 @@ 'browser/renderer_host/gtk_key_bindings_handler_unittest.cc', 'browser/renderer_host/media/audio_input_device_manager_unittest.cc', 'browser/renderer_host/media/audio_renderer_host_unittest.cc', - 'browser/renderer_host/media/media_stream_device_settings_unittest.cc', 'browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc', + 'browser/renderer_host/media/media_stream_manager_unittest.cc', + 'browser/renderer_host/media/media_stream_ui_controller_unittest.cc', 'browser/renderer_host/media/video_capture_controller_unittest.cc', 'browser/renderer_host/media/video_capture_host_unittest.cc', 'browser/renderer_host/media/video_capture_manager_unittest.cc', |