diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-07 13:12:53 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-07 13:12:53 +0000 |
commit | 2b94cfe5dd1f0797adc9ceb1f972a7121057901c (patch) | |
tree | 81adb9be920dbae538c9b8c6ed9366a166d12aac /content/browser | |
parent | 0f531d70380d25e306f99674cb8ba8aa8dbbebd2 (diff) | |
download | chromium_src-2b94cfe5dd1f0797adc9ceb1f972a7121057901c.zip chromium_src-2b94cfe5dd1f0797adc9ceb1f972a7121057901c.tar.gz chromium_src-2b94cfe5dd1f0797adc9ceb1f972a7121057901c.tar.bz2 |
Replace MediaStreamUIController with MediaStreamUIProxy.
Previously a single object MediaStreamUIController was used to control UI for
all streams. Replaced it with a per-stream MediaStreamUIProxy that simplifies
code in many places.
Also moved media request queueing logic from content layer to chrome. Now
different types of requests may be queued differently (e.g. there is no
reason to block screen capture requests on webcam infobar).
This change was previously landed in 197222 and reverted in 197242
TBR=tommi@chromium.org
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=204044
Review URL: https://chromiumcodereview.appspot.com/16342002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204808 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser')
12 files changed, 787 insertions, 816 deletions
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 4724338..fe2c26f3 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 @@ -9,6 +9,7 @@ #include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/media/media_stream_dispatcher_host.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/media/media_stream_ui_proxy.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/common/media/media_stream_messages.h" #include "content/common/media/media_stream_options.h" @@ -125,7 +126,7 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost, MediaStreamManager* manager_; }; -class MockMediaStreamUI : public MediaStreamUI { +class MockMediaStreamUIProxy : public FakeMediaStreamUIProxy { public: MOCK_METHOD1(OnStarted, void(const base::Closure& stop)); }; @@ -164,11 +165,12 @@ class MediaStreamDispatcherHostTest : public testing::Test { } virtual void SetupFakeUI(bool expect_started) { - scoped_ptr<MockMediaStreamUI> stream_ui(new MockMediaStreamUI()); + scoped_ptr<MockMediaStreamUIProxy> stream_ui(new MockMediaStreamUIProxy()); if (expect_started) { EXPECT_CALL(*stream_ui, OnStarted(_)); } - media_stream_manager_->UseFakeUI(stream_ui.PassAs<MediaStreamUI>()); + media_stream_manager_->UseFakeUI( + stream_ui.PassAs<FakeMediaStreamUIProxy>()); } virtual void TearDown() OVERRIDE { @@ -336,10 +338,10 @@ TEST_F(MediaStreamDispatcherHostTest, CloseFromUI) { StreamOptions options(MEDIA_NO_SERVICE, MEDIA_DEVICE_VIDEO_CAPTURE); base::Closure close_callback; - scoped_ptr<MockMediaStreamUI> stream_ui(new MockMediaStreamUI()); + scoped_ptr<MockMediaStreamUIProxy> stream_ui(new MockMediaStreamUIProxy()); EXPECT_CALL(*stream_ui, OnStarted(_)) .WillOnce(SaveArg<0>(&close_callback)); - media_stream_manager_->UseFakeUI(stream_ui.PassAs<MediaStreamUI>()); + media_stream_manager_->UseFakeUI(stream_ui.PassAs<FakeMediaStreamUIProxy>()); EXPECT_CALL(*host_.get(), OnStreamGenerated(kRenderId, kPageRequestId, 0, 1)); EXPECT_CALL(*host_.get(), diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 84c2dc1..2a52423 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -14,7 +14,7 @@ #include "base/threading/thread.h" #include "content/browser/renderer_host/media/audio_input_device_manager.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/media_stream_ui_proxy.h" #include "content/browser/renderer_host/media/video_capture_manager.h" #include "content/browser/renderer_host/media/web_contents_capture_util.h" #include "content/public/browser/browser_thread.h" @@ -54,37 +54,19 @@ static std::string RandomLabel() { } // Helper to verify if a media stream type is part of options or not. -static bool Requested(const StreamOptions& options, +static bool Requested(const MediaStreamRequest& request, MediaStreamType stream_type) { - return (options.audio_type == stream_type || - options.video_type == stream_type); + return (request.audio_type == stream_type || + request.video_type == stream_type); } // TODO(xians): Merge DeviceRequest with MediaStreamRequest. class MediaStreamManager::DeviceRequest { public: - DeviceRequest() - : requester(NULL), - type(MEDIA_GENERATE_STREAM), - render_process_id(-1), - render_view_id(-1), - state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) { - } - DeviceRequest(MediaStreamRequester* requester, - const StreamOptions& request_options, - MediaStreamRequestType request_type, - int render_process_id, - int render_view_id, - const GURL& request_security_origin, - const std::string& requested_device_id) + const MediaStreamRequest& request) : requester(requester), - options(request_options), - type(request_type), - render_process_id(render_process_id), - render_view_id(render_view_id), - security_origin(request_security_origin), - requested_device_id(requested_device_id), + request(request), state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) { } @@ -94,8 +76,8 @@ class MediaStreamManager::DeviceRequest { void SetState(MediaStreamType stream_type, MediaRequestState new_state) { state_[stream_type] = new_state; - if (options.video_type != MEDIA_TAB_VIDEO_CAPTURE && - options.audio_type != MEDIA_TAB_AUDIO_CAPTURE) { + if (request.video_type != MEDIA_TAB_VIDEO_CAPTURE && + request.audio_type != MEDIA_TAB_AUDIO_CAPTURE) { return; } @@ -109,12 +91,11 @@ class MediaStreamManager::DeviceRequest { // used internally within the content module. std::string device_id = WebContentsCaptureUtil::StripWebContentsDeviceScheme( - requested_device_id); + request.requested_device_id); media_observer->OnMediaRequestStateChanged( - render_process_id, render_view_id, - MediaStreamDevice( - stream_type, device_id, device_id), new_state); + request.render_process_id, request.render_view_id, + MediaStreamDevice(stream_type, device_id, device_id), new_state); } MediaRequestState state(MediaStreamType stream_type) const { @@ -122,18 +103,16 @@ class MediaStreamManager::DeviceRequest { } MediaStreamRequester* const requester; // Can be NULL. - const StreamOptions options; - const MediaStreamRequestType type; - const int render_process_id; - const int render_view_id; - const GURL security_origin; - const std::string requested_device_id; + MediaStreamRequest request; + 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. - MediaRequestResponseCallback callback; + MediaStreamManager::MediaRequestResponseCallback callback; + + scoped_ptr<MediaStreamUIProxy> ui_proxy; private: std::vector<MediaRequestState> state_; @@ -147,11 +126,11 @@ MediaStreamManager::EnumerationCache::~EnumerationCache() { } MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager) - : ui_controller_(new MediaStreamUIController(this)), - audio_manager_(audio_manager), + : audio_manager_(audio_manager), monitoring_started_(false), io_loop_(NULL), - screen_capture_active_(false) { + screen_capture_active_(false), + use_fake_ui_(false) { DCHECK(audio_manager_); memset(active_enumeration_ref_count_, 0, sizeof(active_enumeration_ref_count_)); @@ -194,13 +173,11 @@ std::string MediaStreamManager::MakeMediaAccessRequest( const MediaRequestResponseCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Create a new request based on options. - DeviceRequest* request = new DeviceRequest(NULL, - options, - MEDIA_DEVICE_ACCESS, - render_process_id, - render_view_id, - security_origin, - std::string()); + MediaStreamRequest stream_request( + render_process_id, render_view_id, security_origin, + MEDIA_DEVICE_ACCESS, std::string(), + options.audio_type, options.video_type); + DeviceRequest* request = new DeviceRequest(NULL, stream_request); const std::string& label = AddRequest(request); request->callback = callback; @@ -267,13 +244,11 @@ std::string MediaStreamManager::GenerateStream( } // Create a new request based on options. - DeviceRequest* request = new DeviceRequest(requester, - options, - MEDIA_GENERATE_STREAM, - target_render_process_id, - target_render_view_id, - security_origin, - requested_device_id); + MediaStreamRequest stream_request( + target_render_process_id, target_render_view_id, security_origin, + MEDIA_GENERATE_STREAM, requested_device_id, + options.audio_type, options.video_type); + DeviceRequest* request = new DeviceRequest(requester, stream_request); const std::string& label = AddRequest(request); HandleRequest(label); return label; @@ -284,9 +259,6 @@ void MediaStreamManager::CancelRequest(const std::string& label) { DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { - // 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? @@ -320,7 +292,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { // Find the request and close all open devices for the request. DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { - if (it->second->type == MEDIA_ENUMERATE_DEVICES) { + if (it->second->request.request_type == MEDIA_ENUMERATE_DEVICES) { StopEnumerateDevices(label); return; } @@ -332,7 +304,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { device_it != request->devices.end(); ++device_it) { GetDeviceManager(device_it->device.type)->Close(device_it->session_id); } - if (request->type == MEDIA_GENERATE_STREAM && + if (request->request.request_type == MEDIA_GENERATE_STREAM && RequestDone(*request)) { // Notify observers that this device is being closed. for (int i = MEDIA_NO_SERVICE + 1; i != NUM_MEDIA_TYPES; ++i) { @@ -342,12 +314,7 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { MEDIA_REQUEST_STATE_CLOSING); } } - NotifyUIDevicesClosed(label); } - - // 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); } } @@ -384,13 +351,11 @@ std::string MediaStreamManager::EnumerateDevices( return std::string(); } - DeviceRequest* request = new DeviceRequest(requester, - options, - MEDIA_ENUMERATE_DEVICES, - render_process_id, - render_view_id, - security_origin, - std::string()); + MediaStreamRequest stream_request( + render_process_id, render_view_id, security_origin, + MEDIA_ENUMERATE_DEVICES, std::string(), + options.audio_type, options.video_type); + DeviceRequest* request = new DeviceRequest(requester, stream_request); const std::string& label = AddRequest(request); if (cache->valid) { @@ -415,7 +380,7 @@ void MediaStreamManager::StopEnumerateDevices(const std::string& label) { DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { - DCHECK_EQ(it->second->type, MEDIA_ENUMERATE_DEVICES); + DCHECK_EQ(it->second->request.request_type, MEDIA_ENUMERATE_DEVICES); // Delete the DeviceRequest. scoped_ptr<DeviceRequest> request(it->second); RemoveRequest(it); @@ -444,29 +409,17 @@ std::string MediaStreamManager::OpenDevice( return std::string(); } - DeviceRequest* request = new DeviceRequest(requester, - options, - MEDIA_OPEN_DEVICE, - render_process_id, - render_view_id, - security_origin, - device_id); + MediaStreamRequest stream_request( + render_process_id, render_view_id, security_origin, + MEDIA_OPEN_DEVICE, device_id, + options.audio_type, options.video_type); + DeviceRequest* request = new DeviceRequest(requester, stream_request); const std::string& label = AddRequest(request); StartEnumeration(request); return label; } -void MediaStreamManager::NotifyUIDevicesOpened(const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ui_controller_->NotifyUIIndicatorDevicesOpened(label); -} - -void MediaStreamManager::NotifyUIDevicesClosed(const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ui_controller_->NotifyUIIndicatorDevicesClosed(label); -} - void MediaStreamManager::SendCachedDeviceList( EnumerationCache* cache, const std::string& label) { @@ -523,7 +476,7 @@ void MediaStreamManager::StartEnumeration(DeviceRequest* request) { // Start enumeration for devices of all requested device types. for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) { const MediaStreamType stream_type = static_cast<MediaStreamType>(i); - if (Requested(request->options, stream_type)) { + if (Requested(request->request, stream_type)) { request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED); DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); if (active_enumeration_ref_count_[stream_type] == 0) { @@ -549,35 +502,57 @@ std::string MediaStreamManager::AddRequest(DeviceRequest* request) { } void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it) { - if (it->second->options.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) { + if (it->second->request.video_type == MEDIA_SCREEN_VIDEO_CAPTURE) { DCHECK(screen_capture_active_); screen_capture_active_ = false; } - NotifyUIDevicesClosed(it->first); - requests_.erase(it); } void MediaStreamManager::PostRequestToUI(const std::string& label) { 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, - request->type, - request->requested_device_id); + + if (use_fake_ui_) { + if (!fake_ui_) + fake_ui_.reset(new FakeMediaStreamUIProxy()); + + MediaStreamDevices devices; + if (audio_enumeration_cache_.valid) { + for (StreamDeviceInfoArray::const_iterator it = + audio_enumeration_cache_.devices.begin(); + it != audio_enumeration_cache_.devices.end(); ++it) { + devices.push_back(it->device); + } + } + if (video_enumeration_cache_.valid) { + for (StreamDeviceInfoArray::const_iterator it = + video_enumeration_cache_.devices.begin(); + it != video_enumeration_cache_.devices.end(); ++it) { + devices.push_back(it->device); + } + } + + fake_ui_->SetAvailableDevices(devices); + + request->ui_proxy = fake_ui_.Pass(); + } else { + request->ui_proxy = MediaStreamUIProxy::Create(); + } + + request->ui_proxy->RequestAccess( + request->request, + base::Bind(&MediaStreamManager::HandleAccessRequestResponse, + base::Unretained(this), label)); } void MediaStreamManager::HandleRequest(const std::string& label) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DeviceRequest* request = requests_[label]; - const MediaStreamType audio_type = request->options.audio_type; - const MediaStreamType video_type = request->options.video_type; + const MediaStreamType audio_type = request->request.audio_type; + const MediaStreamType video_type = request->request.video_type; bool is_web_contents_capture = audio_type == MEDIA_TAB_AUDIO_CAPTURE || @@ -679,7 +654,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, return; } - switch (request->type) { + switch (request->request.request_type) { case MEDIA_OPEN_DEVICE: request->requester->DeviceOpened(label, devices->front()); break; @@ -709,7 +684,9 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, } request->requester->StreamGenerated(label, audio_devices, video_devices); - NotifyUIDevicesOpened(label); + request->ui_proxy->OnStarted( + base::Bind(&MediaStreamManager::StopStreamFromUI, + base::Unretained(this), label)); break; } default: @@ -751,10 +728,9 @@ void MediaStreamManager::DevicesEnumerated( std::list<std::string> label_list; for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); ++it) { - if (it->second->state(stream_type) == - MEDIA_REQUEST_STATE_REQUESTED && - Requested(it->second->options, stream_type)) { - if (it->second->type != MEDIA_ENUMERATE_DEVICES) + if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED && + Requested(it->second->request, stream_type)) { + if (it->second->request.request_type != MEDIA_ENUMERATE_DEVICES) it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL); label_list.push_back(it->first); } @@ -762,15 +738,15 @@ void MediaStreamManager::DevicesEnumerated( for (std::list<std::string>::iterator it = label_list.begin(); it != label_list.end(); ++it) { DeviceRequest* request = requests_[*it]; - switch (request->type) { + switch (request->request.request_type) { case MEDIA_ENUMERATE_DEVICES: if (need_update_clients && request->requester) request->requester->DevicesEnumerated(*it, devices); break; default: - if (request->state(request->options.audio_type) == + if (request->state(request->request.audio_type) == MEDIA_REQUEST_STATE_REQUESTED || - request->state(request->options.video_type) == + request->state(request->request.video_type) == MEDIA_REQUEST_STATE_REQUESTED) { // We are doing enumeration for other type of media, wait until it is // all done before posting the request to UI because UI needs @@ -842,28 +818,37 @@ void MediaStreamManager::Error(MediaStreamType stream_type, } } -void MediaStreamManager::DevicesAccepted(const std::string& label, - const StreamDeviceInfoArray& devices) { +void MediaStreamManager::HandleAccessRequestResponse( + const std::string& label, + const MediaStreamDevices& devices) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!devices.empty()); + DeviceRequests::iterator request_it = requests_.find(label); if (request_it == requests_.end()) { return; } - if (request_it->second->type == MEDIA_DEVICE_ACCESS) { + // Handle the case when the request was denied. + if (devices.empty()) { + // Notify the users about the request result. scoped_ptr<DeviceRequest> request(request_it->second); - if (!request->callback.is_null()) { - // Map the devices to MediaStreamDevices. - MediaStreamDevices selected_devices; - for (StreamDeviceInfoArray::const_iterator it = devices.begin(); - it != devices.end(); ++it) { - selected_devices.push_back(it->device); - } + if (request->requester) + request->requester->StreamGenerationFailed(label); - request->callback.Run(label, selected_devices); + if (request->request.request_type == MEDIA_DEVICE_ACCESS && + !request->callback.is_null()) { + request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass()); } + RemoveRequest(request_it); + return; + } + + if (request_it->second->request.request_type == MEDIA_DEVICE_ACCESS) { + scoped_ptr<DeviceRequest> request(request_it->second); + if (!request->callback.is_null()) + request->callback.Run(devices, request->ui_proxy.Pass()); + // Delete the request since it is done. RemoveRequest(request_it); return; @@ -871,16 +856,18 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, // Process all newly-accepted devices for this request. DeviceRequest* request = request_it->second; - bool found_audio = false, found_video = false; - for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); + bool found_audio = false; + bool found_video = false; + for (MediaStreamDevices::const_iterator device_it = devices.begin(); device_it != devices.end(); ++device_it) { - StreamDeviceInfo device_info = *device_it; // Make a copy. + StreamDeviceInfo device_info; + device_info.device = *device_it; // TODO(justinlin): Nicer way to do this? // Re-append the device's id since we lost it when posting request to UI. if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE || device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) { - device_info.device.id = request->requested_device_id; + device_info.device.id = request->request.requested_device_id; // Initialize the sample_rate and channel_layout here since for audio // mirroring, we don't go through EnumerateDevices where these are usually @@ -902,48 +889,26 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, // Set in_use to false to be able to track if this device has been // opened. in_use might be true if the device type can be used in more // than one session. - DCHECK_EQ(request->state(device_it->device.type), - MEDIA_REQUEST_STATE_PENDING_APPROVAL); device_info.in_use = false; device_info.session_id = GetDeviceManager(device_info.device.type)->Open(device_info); - request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_OPENING); + request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING); request->devices.push_back(device_info); - if (device_info.device.type == request->options.audio_type) { + if (device_info.device.type == request->request.audio_type) { found_audio = true; - } else if (device_info.device.type == request->options.video_type) { + } else if (device_info.device.type == request->request.video_type) { found_video = true; } } // Check whether we've received all stream types requested. - if (!found_audio && IsAudioMediaType(request->options.audio_type)) - request->SetState(request->options.audio_type, MEDIA_REQUEST_STATE_ERROR); - - if (!found_video && IsVideoMediaType(request->options.video_type)) - request->SetState(request->options.video_type, MEDIA_REQUEST_STATE_ERROR); -} - -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()) - return; - - // Notify the users about the request result. - scoped_ptr<DeviceRequest> request(it->second); - if (request->requester) - request->requester->StreamGenerationFailed(label); - - if (request->type == MEDIA_DEVICE_ACCESS && - !request->callback.is_null()) { - request->callback.Run(label, MediaStreamDevices()); - } + if (!found_audio && IsAudioMediaType(request->request.audio_type)) + request->SetState(request->request.audio_type, MEDIA_REQUEST_STATE_ERROR); - RemoveRequest(it); + if (!found_video && IsVideoMediaType(request->request.video_type)) + request->SetState(request->request.video_type, MEDIA_REQUEST_STATE_ERROR); } void MediaStreamManager::StopStreamFromUI(const std::string& label) { @@ -960,38 +925,17 @@ void MediaStreamManager::StopStreamFromUI(const std::string& label) { StopGeneratedStream(label); } -void MediaStreamManager::GetAvailableDevices(MediaStreamDevices* devices) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(audio_enumeration_cache_.valid || video_enumeration_cache_.valid); - DCHECK(devices->empty()); - if (audio_enumeration_cache_.valid) { - for (StreamDeviceInfoArray::const_iterator it = - audio_enumeration_cache_.devices.begin(); - it != audio_enumeration_cache_.devices.end(); - ++it) { - devices->push_back(it->device); - } - } - - if (video_enumeration_cache_.valid) { - for (StreamDeviceInfoArray::const_iterator it = - video_enumeration_cache_.devices.begin(); - it != video_enumeration_cache_.devices.end(); - ++it) { - devices->push_back(it->device); - } - } -} - void MediaStreamManager::UseFakeDevice() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); video_capture_manager()->UseFakeDevice(); audio_input_device_manager()->UseFakeDevice(); - UseFakeUI(scoped_ptr<MediaStreamUI>()); + UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>()); } -void MediaStreamManager::UseFakeUI(scoped_ptr<MediaStreamUI> fake_ui) { - ui_controller_->UseFakeUI(fake_ui.Pass()); +void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + use_fake_ui_ = true; + fake_ui_ = fake_ui.Pass(); } void MediaStreamManager::WillDestroyCurrentMessageLoop() { @@ -1008,7 +952,6 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() { audio_input_device_manager_ = NULL; video_capture_manager_ = NULL; io_loop_ = NULL; - ui_controller_.reset(); } void MediaStreamManager::NotifyDevicesChanged( @@ -1039,23 +982,23 @@ void MediaStreamManager::NotifyDevicesChanged( bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - const bool requested_audio = IsAudioMediaType(request.options.audio_type); - const bool requested_video = IsVideoMediaType(request.options.video_type); + const bool requested_audio = IsAudioMediaType(request.request.audio_type); + const bool requested_video = IsVideoMediaType(request.request.video_type); const bool audio_done = !requested_audio || - request.state(request.options.audio_type) == + request.state(request.request.audio_type) == MEDIA_REQUEST_STATE_DONE || - request.state(request.options.audio_type) == + request.state(request.request.audio_type) == MEDIA_REQUEST_STATE_ERROR; if (!audio_done) return false; const bool video_done = !requested_video || - request.state(request.options.video_type) == + request.state(request.request.video_type) == MEDIA_REQUEST_STATE_DONE || - request.state(request.options.video_type) == + request.state(request.request.video_type) == MEDIA_REQUEST_STATE_ERROR; if (!video_done) return false; diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h index acde644d..ae9fd37 100644 --- a/content/browser/renderer_host/media/media_stream_manager.h +++ b/content/browser/renderer_host/media/media_stream_manager.h @@ -32,7 +32,6 @@ #include "base/message_loop.h" #include "base/system_monitor/system_monitor.h" #include "content/browser/renderer_host/media/media_stream_provider.h" -#include "content/browser/renderer_host/media/media_stream_settings_requester.h" #include "content/common/media/media_stream_options.h" #include "content/common/content_export.h" @@ -47,9 +46,10 @@ class AudioManager; namespace content { class AudioInputDeviceManager; +class FakeMediaStreamUIProxy; class MediaStreamDeviceSettings; class MediaStreamRequester; -class MediaStreamUIController; +class MediaStreamUIProxy; class VideoCaptureManager; // MediaStreamManager is used to generate and close new media devices, not to @@ -59,9 +59,14 @@ class VideoCaptureManager; class CONTENT_EXPORT MediaStreamManager : public MediaStreamProviderListener, public base::MessageLoop::DestructionObserver, - public SettingsRequester, public base::SystemMonitor::DevicesChangedObserver { public: + // Callback to deliver the result of a media request. |label| is the string + // to identify the request, + typedef base::Callback<void(const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUIProxy> ui)> + MediaRequestResponseCallback; + explicit MediaStreamManager(media::AudioManager* audio_manager); virtual ~MediaStreamManager(); @@ -122,14 +127,6 @@ class CONTENT_EXPORT MediaStreamManager MediaStreamType type, const GURL& security_origin); - // 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(const std::string& label); - - // Signals the UI that the devices are being closed. - void NotifyUIDevicesClosed(const std::string& label); - // Implements MediaStreamProviderListener. virtual void Opened(MediaStreamType stream_type, int capture_session_id) OVERRIDE; @@ -141,13 +138,6 @@ class CONTENT_EXPORT MediaStreamManager int capture_session_id, MediaStreamProviderError error) OVERRIDE; - // Implements SettingsRequester. - virtual void DevicesAccepted(const std::string& label, - const StreamDeviceInfoArray& devices) OVERRIDE; - virtual void SettingsError(const std::string& label) OVERRIDE; - virtual void StopStreamFromUI(const std::string& label) OVERRIDE; - virtual void GetAvailableDevices(MediaStreamDevices* devices) OVERRIDE; - // Implements base::SystemMonitor::DevicesChangedObserver. virtual void OnDevicesChanged( base::SystemMonitor::DeviceType device_type) OVERRIDE; @@ -158,7 +148,7 @@ class CONTENT_EXPORT MediaStreamManager // Called by the unittests to specify fake UI that should be used for next // generated stream. - void UseFakeUI(scoped_ptr<MediaStreamUI> fake_ui); + void UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui); // This object gets deleted on the UI thread after the IO thread has been // destroyed. So we need to know when IO thread is being destroyed so that @@ -189,6 +179,11 @@ class CONTENT_EXPORT MediaStreamManager void NotifyDevicesChanged(MediaStreamType stream_type, const StreamDeviceInfoArray& devices); + + void HandleAccessRequestResponse(const std::string& label, + const MediaStreamDevices& devices); + void StopStreamFromUI(const std::string& label); + // Helpers. bool RequestDone(const DeviceRequest& request) const; MediaStreamProvider* GetDeviceManager(MediaStreamType stream_type); @@ -210,15 +205,9 @@ class CONTENT_EXPORT MediaStreamManager void StartMonitoring(); void StopMonitoring(); - // Callback for UI called when the user requests stream with the specified - // |label| to be stopped. - void OnStopStreamRequested(const std::string& label); - // Device thread shared by VideoCaptureManager and AudioInputDeviceManager. scoped_ptr<base::Thread> device_thread_; - scoped_ptr<MediaStreamUIController> ui_controller_; - media::AudioManager* const audio_manager_; // not owned scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_; scoped_refptr<VideoCaptureManager> video_capture_manager_; @@ -244,6 +233,9 @@ class CONTENT_EXPORT MediaStreamManager bool screen_capture_active_; + bool use_fake_ui_; + scoped_ptr<FakeMediaStreamUIProxy> fake_ui_; + DISALLOW_COPY_AND_ASSIGN(MediaStreamManager); }; diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc index a515a36..b800d75 100644 --- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc @@ -8,6 +8,7 @@ #include "base/message_loop.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/media/media_stream_ui_proxy.h" #include "content/common/media/media_stream_options.h" #include "media/audio/audio_manager_base.h" #if defined(OS_ANDROID) @@ -63,10 +64,11 @@ 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); + MOCK_METHOD1(Response, void(int index)); + void ResponseCallback(int index, + const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUIProxy> ui_proxy) { + Response(index); message_loop_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); } @@ -95,15 +97,15 @@ class MediaStreamManagerTest : public ::testing::Test { message_loop_.reset(); } - std::string MakeMediaAccessRequest() { + std::string MakeMediaAccessRequest(int index) { const int render_process_id = 1; const int render_view_id = 1; StreamOptions components(MEDIA_DEVICE_AUDIO_CAPTURE, MEDIA_DEVICE_VIDEO_CAPTURE); const GURL security_origin; - MediaRequestResponseCallback callback = + MediaStreamManager::MediaRequestResponseCallback callback = base::Bind(&MediaStreamManagerTest::ResponseCallback, - base::Unretained(this)); + base::Unretained(this), index); return media_stream_manager_->MakeMediaAccessRequest(render_process_id, render_view_id, components, @@ -122,22 +124,22 @@ class MediaStreamManagerTest : public ::testing::Test { }; TEST_F(MediaStreamManagerTest, MakeMediaAccessRequest) { - std::string label = MakeMediaAccessRequest(); + MakeMediaAccessRequest(0); // Expecting the callback will be triggered and quit the test. - EXPECT_CALL(*this, Response(label)); + EXPECT_CALL(*this, Response(0)); WaitForResult(); } TEST_F(MediaStreamManagerTest, MakeAndCancelMediaAccessRequest) { - std::string label = MakeMediaAccessRequest(); + std::string label = MakeMediaAccessRequest(0); // No callback is expected. media_stream_manager_->CancelRequest(label); } TEST_F(MediaStreamManagerTest, MakeMultipleRequests) { // First request. - std::string label1 = MakeMediaAccessRequest(); + std::string label1 = MakeMediaAccessRequest(0); // Second request. int render_process_id = 2; @@ -145,9 +147,9 @@ TEST_F(MediaStreamManagerTest, MakeMultipleRequests) { StreamOptions components(MEDIA_DEVICE_AUDIO_CAPTURE, MEDIA_DEVICE_VIDEO_CAPTURE); GURL security_origin; - MediaRequestResponseCallback callback = + MediaStreamManager::MediaRequestResponseCallback callback = base::Bind(&MediaStreamManagerTest::ResponseCallback, - base::Unretained(this)); + base::Unretained(this), 1); std::string label2 = media_stream_manager_->MakeMediaAccessRequest( render_process_id, render_view_id, @@ -158,18 +160,19 @@ TEST_F(MediaStreamManagerTest, MakeMultipleRequests) { // 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); + EXPECT_CALL(*this, Response(0)); + EXPECT_CALL(*this, Response(1)); WaitForResult(); } TEST_F(MediaStreamManagerTest, MakeAndCancelMultipleRequests) { - std::string label1 = MakeMediaAccessRequest(); - std::string label2 = MakeMediaAccessRequest(); + std::string label1 = MakeMediaAccessRequest(0); + std::string label2 = MakeMediaAccessRequest(1); media_stream_manager_->CancelRequest(label1); // Expecting the callback from the second request will be triggered and // quit the test. - EXPECT_CALL(*this, Response(label2)); + EXPECT_CALL(*this, Response(1)); WaitForResult(); } diff --git a/content/browser/renderer_host/media/media_stream_settings_requester.h b/content/browser/renderer_host/media/media_stream_settings_requester.h deleted file mode 100644 index 9aa4f31..0000000 --- a/content/browser/renderer_host/media/media_stream_settings_requester.h +++ /dev/null @@ -1,39 +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. - -#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_SETTINGS_REQUESTER_H_ -#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_SETTINGS_REQUESTER_H_ - -#include <string> - -#include "content/common/content_export.h" -#include "content/common/media/media_stream_options.h" - -namespace content { - -// Implemented by the class requesting media capture device usage. -class CONTENT_EXPORT SettingsRequester { - public: - // If no error occurred, this call will deliver the result and the request - // is considered answered. - virtual void DevicesAccepted(const std::string& label, - const StreamDeviceInfoArray& devices) = 0; - - // An error for specified |request_id| has occurred. - virtual void SettingsError(const std::string& label) = 0; - - // Called when user requested the stream with the specified |label| to be - // stopped. - virtual void StopStreamFromUI(const std::string& label) = 0; - - // Gets a list of available devices stored in the requester. - virtual void GetAvailableDevices(MediaStreamDevices* devices) = 0; - - protected: - virtual ~SettingsRequester() {} -}; - -} // namespace content - -#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_SETTINGS_REQUESTER_H_ diff --git a/content/browser/renderer_host/media/media_stream_ui_controller.cc b/content/browser/renderer_host/media/media_stream_ui_controller.cc deleted file mode 100644 index 115bfd6..0000000 --- a/content/browser/renderer_host/media/media_stream_ui_controller.cc +++ /dev/null @@ -1,336 +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_ui_controller.h" - -#include <algorithm> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/message_loop_proxy.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" -#include "media/base/bind_to_loop.h" - -namespace content { - -// UI request contains all data needed to keep track of requests between the -// different calls. -class MediaStreamRequestForUI : public MediaStreamRequest { - public: - MediaStreamRequestForUI(int render_pid, - int render_vid, - const GURL& origin, - const StreamOptions& options, - MediaStreamRequestType request_type, - const std::string& requested_device_id) - : MediaStreamRequest(render_pid, render_vid, origin, - request_type, requested_device_id, - options.audio_type, options.video_type), - posted_task(false) { - DCHECK(IsAudioMediaType(options.audio_type) || - IsVideoMediaType(options.video_type)); - } - - ~MediaStreamRequestForUI() {} - - // 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. - RenderViewHostImpl* host = RenderViewHostImpl::FromID( - request.render_process_id, request.render_view_id); - - // Tab may have gone away. - if (!host || !host->GetDelegate()) { - callback.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>()); - return; - } - - host->GetDelegate()->RequestMediaAccessPermission(request, callback); -} - -} // namespace - -MediaStreamUIController::MediaStreamUIController(SettingsRequester* requester) - : requester_(requester), - use_fake_ui_(false) { - DCHECK(requester_); -} - -MediaStreamUIController::~MediaStreamUIController() { - DCHECK(requests_.empty()); - DCHECK(stream_indicators_.empty()); -} - -void MediaStreamUIController::MakeUIRequest( - const std::string& label, - int render_process_id, - int render_view_id, - const StreamOptions& request_options, - const GURL& security_origin, MediaStreamRequestType request_type, - const std::string& requested_device_id) { - 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, request_type, requested_device_id))).second) { - NOTREACHED(); - } - - 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(render_process_id, render_view_id)) - return; - - PostRequestToUI(label); -} - -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); - } - - NotifyUIIndicatorDevicesClosed(label); -} - -void MediaStreamUIController::ProcessAccessRequestResponse( - const std::string& label, - const MediaStreamDevices& devices, - scoped_ptr<MediaStreamUI> stream_ui) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - UIRequests::iterator request_iter = requests_.find(label); - // Return if the request has been removed. - if (request_iter == requests_.end()) { - if (stream_ui) { - BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, - stream_ui.release()); - } - 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.empty()) { - if (stream_ui) { - DCHECK(stream_indicators_.find(label) == stream_indicators_.end()); - stream_indicators_[label] = stream_ui.release(); - } - - // Build a list of "full" device objects for the accepted devices. - 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(StreamDeviceInfo( - dev->type, dev->name, dev->id, - dev->sample_rate, dev->channel_layout, false)); - } - - requester_->DevicesAccepted(label, device_list); - } else { - DCHECK(!stream_ui); - requester_->SettingsError(label); - } -} - -void MediaStreamUIController::NotifyUIIndicatorDevicesOpened( - const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - IndicatorsMap::iterator it = stream_indicators_.find(label); - if (it != stream_indicators_.end()) { - base::Closure stop_callback = media::BindToLoop( - base::MessageLoopProxy::current(), - base::Bind(&MediaStreamUIController::OnStopStreamFromUI, - base::Unretained(this), label)); - - // base::Unretained is safe here because the target can be deleted only on - // UI thread when posted from IO thread (see - // NotifyUIIndicatorDevicesClosed()). - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&MediaStreamUI::OnStarted, - base::Unretained(it->second), stop_callback)); - } -} - -void MediaStreamUIController::NotifyUIIndicatorDevicesClosed( - const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - - IndicatorsMap::iterator indicator = stream_indicators_.find(label); - if (indicator != stream_indicators_.end()) { - BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, indicator->second); - stream_indicators_.erase(indicator); - } -} - -void MediaStreamUIController::UseFakeUI(scoped_ptr<MediaStreamUI> fake_ui) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - use_fake_ui_ = true; - fake_ui_ = fake_ui.Pass(); -} - -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->posted_task) { - next_request_label = it->first; - break; - } - } - } - - if (next_request_label.empty()) - return; - - if (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; - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, base::Bind( - &ProceedMediaAccessPermission, *request, media::BindToLoop( - base::MessageLoopProxy::current(), base::Bind( - &MediaStreamUIController::ProcessAccessRequestResponse, - base::Unretained(this), label)))); -} - -void MediaStreamUIController::PostRequestToFakeUI(const std::string& label) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(requester_); - UIRequests::iterator request_iter = requests_.find(label); - DCHECK(request_iter != requests_.end()); - MediaStreamRequestForUI* request = request_iter->second; - - MediaStreamDevices devices; - requester_->GetAvailableDevices(&devices); - MediaStreamDevices devices_to_use; - bool accepted_audio = false; - bool accepted_video = false; - // Use the first capture device of the same media type in the list for the - // fake UI. - for (MediaStreamDevices::const_iterator it = devices.begin(); - it != devices.end(); ++it) { - if (!accepted_audio && - IsAudioMediaType(request->audio_type) && - IsAudioMediaType(it->type)) { - devices_to_use.push_back(*it); - accepted_audio = true; - } else if (!accepted_video && - IsVideoMediaType(request->video_type) && - IsVideoMediaType(it->type)) { - devices_to_use.push_back(*it); - accepted_video = true; - } - } - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&MediaStreamUIController::ProcessAccessRequestResponse, - base::Unretained(this), label, devices_to_use, - base::Passed(&fake_ui_))); -} - -void MediaStreamUIController::OnStopStreamFromUI(const std::string& label) { - // It's safe to base::Unretained() here because |requester_| references - // MediaStreamManager which always outlives IO thread. - // - // TODO(sergeyu): Refactor this code to not rely on what |requester_| is. - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SettingsRequester::StopStreamFromUI, - base::Unretained(requester_), label)); -} - -} // namespace content diff --git a/content/browser/renderer_host/media/media_stream_ui_controller.h b/content/browser/renderer_host/media/media_stream_ui_controller.h deleted file mode 100644 index 6d14e83..0000000 --- a/content/browser/renderer_host/media/media_stream_ui_controller.h +++ /dev/null @@ -1,106 +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. - -// 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. Pick device and get user confirmation. -// 3. Confirm by calling SettingsRequester::DevicesAccepted(). -// Repeat step 1 - 3 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 "content/browser/renderer_host/media/media_stream_provider.h" -#include "content/public/browser/web_contents_delegate.h" - -namespace content { - -class MediaStreamRequestForUI; -class SettingsRequester; - -// 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(SettingsRequester* requester); - virtual ~MediaStreamUIController(); - - // Called when a new request for the capture device access is made. - // Users are responsible for canceling 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 StreamOptions& stream_components, - const GURL& security_origin, - MediaStreamRequestType request_type, - const std::string& requested_device_id); - - // 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 signal the UI indicator that the devices are opened. - void NotifyUIIndicatorDevicesOpened(const std::string& label); - - // Called to signal the UI indicator that the devices are closed. - void NotifyUIIndicatorDevicesClosed(const std::string& label); - - // 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(scoped_ptr<MediaStreamUI> fake_ui); - - private: - typedef std::map<std::string, MediaStreamRequestForUI*> UIRequests; - typedef std::map<std::string, MediaStreamUI*> IndicatorsMap; - - // 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); - - // Callback handler for WebContents::RequestMediaAccessPermission(). - void ProcessAccessRequestResponse(const std::string& label, - const MediaStreamDevices& devices, - scoped_ptr<MediaStreamUI> stream_ui); - - // Callback for UI called when user requests a stream to be stopped. - void OnStopStreamFromUI(const std::string& label); - - SettingsRequester* requester_; - UIRequests requests_; - - // See comment above for method UseFakeUI. Used for automated testing. - bool use_fake_ui_; - scoped_ptr<MediaStreamUI> fake_ui_; - - // Container MediaStreamUI objects for currently active streams. - IndicatorsMap stream_indicators_; - - 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_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc new file mode 100644 index 0000000..3e4edbc --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc @@ -0,0 +1,216 @@ +// Copyright 2013 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_proxy.h" + +#include "content/browser/renderer_host/render_view_host_delegate.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/public/browser/browser_thread.h" +#include "media/video/capture/fake_video_capture_device.h" + +namespace content { + +class MediaStreamUIProxy::Core { + public: + explicit Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, + RenderViewHostDelegate* test_render_delegate); + ~Core(); + + void RequestAccess(const MediaStreamRequest& request); + void OnStarted(); + + private: + void ProcessAccessRequestResponse(const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUI> stream_ui); + void ProcessStopRequestFromUI(); + + base::WeakPtr<MediaStreamUIProxy> proxy_; + scoped_ptr<MediaStreamUI> ui_; + + RenderViewHostDelegate* const test_render_delegate_; + + // WeakPtr<> is used to RequestMediaAccessPermission() because there is no way + // cancel media requests. + base::WeakPtrFactory<Core> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Core); +}; + +MediaStreamUIProxy::Core::Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, + RenderViewHostDelegate* test_render_delegate) + : proxy_(proxy), + test_render_delegate_(test_render_delegate), + weak_factory_(this) { +} + +MediaStreamUIProxy::Core::~Core() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +void MediaStreamUIProxy::Core::RequestAccess( + const MediaStreamRequest& request) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + RenderViewHostDelegate* render_delegate; + + if (test_render_delegate_) { + render_delegate = test_render_delegate_; + } else { + RenderViewHostImpl* host = RenderViewHostImpl::FromID( + request.render_process_id, request.render_view_id); + + // Tab may have gone away. + if (!host || !host->GetDelegate()) { + ProcessAccessRequestResponse( + MediaStreamDevices(), scoped_ptr<MediaStreamUI>()); + return; + } + + render_delegate = host->GetDelegate(); + } + + render_delegate->RequestMediaAccessPermission( + request, base::Bind(&Core::ProcessAccessRequestResponse, + weak_factory_.GetWeakPtr())); +} + +void MediaStreamUIProxy::Core::OnStarted() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (ui_) { + ui_->OnStarted(base::Bind(&Core::ProcessStopRequestFromUI, + base::Unretained(this))); + } +} + +void MediaStreamUIProxy::Core::ProcessAccessRequestResponse( + const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUI> stream_ui) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + ui_ = stream_ui.Pass(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, + proxy_, devices)); +} + +void MediaStreamUIProxy::Core::ProcessStopRequestFromUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&MediaStreamUIProxy::ProcessStopRequestFromUI, proxy_)); +} + +// static +scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::Create() { + return scoped_ptr<MediaStreamUIProxy>(new MediaStreamUIProxy(NULL)); +} + +// static +scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests( + RenderViewHostDelegate* render_delegate) { + return scoped_ptr<MediaStreamUIProxy>( + new MediaStreamUIProxy(render_delegate)); +} + +MediaStreamUIProxy::MediaStreamUIProxy( + RenderViewHostDelegate* test_render_delegate) + : weak_factory_(this) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + core_.reset(new Core(weak_factory_.GetWeakPtr(), test_render_delegate)); +} + +MediaStreamUIProxy::~MediaStreamUIProxy() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, core_.release()); +} + +void MediaStreamUIProxy::RequestAccess( + const MediaStreamRequest& request, + const ResponseCallback& response_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + response_callback_ = response_callback; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&Core::RequestAccess, base::Unretained(core_.get()), request)); +} + +void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + stop_callback_ = stop_callback; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&Core::OnStarted, base::Unretained(core_.get()))); +} + +void MediaStreamUIProxy::ProcessAccessRequestResponse( + const MediaStreamDevices& devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!response_callback_.is_null()); + + ResponseCallback cb = response_callback_; + response_callback_.Reset(); + cb.Run(devices); +} + +void MediaStreamUIProxy::ProcessStopRequestFromUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!stop_callback_.is_null()); + + base::Closure cb = stop_callback_; + stop_callback_.Reset(); + cb.Run(); +} + +FakeMediaStreamUIProxy::FakeMediaStreamUIProxy() + : MediaStreamUIProxy(NULL) { +} + +FakeMediaStreamUIProxy::~FakeMediaStreamUIProxy() {} + +void FakeMediaStreamUIProxy::SetAvailableDevices( + const MediaStreamDevices& devices) { + devices_ = devices; +} + +void FakeMediaStreamUIProxy::RequestAccess( + const MediaStreamRequest& request, + const ResponseCallback& response_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + response_callback_ = response_callback; + + MediaStreamDevices devices_to_use; + bool accepted_audio = false; + bool accepted_video = false; + // Use the first capture device of the same media type in the list for the + // fake UI. + for (MediaStreamDevices::const_iterator it = devices_.begin(); + it != devices_.end(); ++it) { + if (!accepted_audio && + IsAudioMediaType(request.audio_type) && + IsAudioMediaType(it->type)) { + devices_to_use.push_back(*it); + accepted_audio = true; + } else if (!accepted_video && + IsVideoMediaType(request.video_type) && + IsVideoMediaType(it->type)) { + devices_to_use.push_back(*it); + accepted_video = true; + } + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, + weak_factory_.GetWeakPtr(), devices_to_use)); +} + +void FakeMediaStreamUIProxy::OnStarted(const base::Closure& stop_callback) { +} + +} // namespace content diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.h b/content/browser/renderer_host/media/media_stream_ui_proxy.h new file mode 100644 index 0000000..62bbeb8 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_ui_proxy.h @@ -0,0 +1,88 @@ +// Copyright 2013 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. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_PROXY_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_PROXY_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/public/common/media_stream_request.h" + +namespace content { + +class RenderViewHostDelegate; + +// MediaStreamUIProxy proxies calls to media stream UI between IO thread and UI +// thread. One instance of this class is create per MediaStream object. It must +// be create, used and destroyed on IO thread. +class CONTENT_EXPORT MediaStreamUIProxy { + public: + typedef base::Callback< + void (const MediaStreamDevices& devices)> ResponseCallback; + + static scoped_ptr<MediaStreamUIProxy> Create(); + static scoped_ptr<MediaStreamUIProxy> CreateForTests( + RenderViewHostDelegate* render_delegate); + + virtual ~MediaStreamUIProxy(); + + // Requests access for the MediaStream by calling + // WebContentsDelegate::RequestMediaAccessPermission(). The specified + // |response_callback| is called when the WebContentsDelegate approves or + // denies request. + virtual void RequestAccess(const MediaStreamRequest& request, + const ResponseCallback& response_callback); + + // Notifies the UI that the MediaStream has been started. Must be called after + // access has been approved using RequestAccess(). |stop_callback| is be + // called on the IO thread after the user has requests the stream to be + // stopped. + virtual void OnStarted(const base::Closure& stop_callback); + + void SetRenderViewHostDelegateForTests(RenderViewHostDelegate* delegate); + + protected: + MediaStreamUIProxy(RenderViewHostDelegate* test_render_delegate); + + private: + class Core; + friend class Core; + friend class FakeMediaStreamUIProxy; + + void ProcessAccessRequestResponse(const MediaStreamDevices& devices); + void ProcessStopRequestFromUI(); + + scoped_ptr<Core> core_; + ResponseCallback response_callback_; + base::Closure stop_callback_; + + base::WeakPtrFactory<MediaStreamUIProxy> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamUIProxy); +}; + +class CONTENT_EXPORT FakeMediaStreamUIProxy : public MediaStreamUIProxy { + public: + explicit FakeMediaStreamUIProxy(); + virtual ~FakeMediaStreamUIProxy(); + + void SetAvailableDevices(const MediaStreamDevices& devices); + + // MediaStreamUIProxy overrides. + virtual void RequestAccess( + const MediaStreamRequest& request, + const ResponseCallback& response_callback) OVERRIDE; + virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE; + + private: + MediaStreamDevices devices_; + + DISALLOW_COPY_AND_ASSIGN(FakeMediaStreamUIProxy); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_UI_PROXY_H_ diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc new file mode 100644 index 0000000..1e9b815 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_ui_proxy_unittest.cc @@ -0,0 +1,213 @@ +// Copyright 2013 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_proxy.h" + +#include "base/message_loop.h" +#include "content/browser/renderer_host/render_view_host_delegate.h" +#include "content/public/common/renderer_preferences.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/rect.h" + +using testing::_; +using testing::SaveArg; + +namespace content { +namespace { + +class MockRenderViewHostDelegate : public RenderViewHostDelegate { + public: + MOCK_METHOD2(RequestMediaAccessPermission, + void(const MediaStreamRequest& request, + const MediaResponseCallback& callback)); + + // Stubs for pure virtual methods we don't care about. + virtual gfx::Rect GetRootWindowResizerRect() const OVERRIDE { + NOTREACHED(); + return gfx::Rect(); + } + virtual RendererPreferences GetRendererPrefs( + BrowserContext* browser_context) const OVERRIDE { + NOTREACHED(); + return RendererPreferences(); + } +}; + +class MockResponseCallback { + public: + MOCK_METHOD1(OnAccessRequestResponse, + void(const MediaStreamDevices& devices)); +}; + +class MockMediaStreamUI : public MediaStreamUI { + public: + MOCK_METHOD1(OnStarted, void(const base::Closure& stop)); +}; + +class MockStopStreamHandler { + public: + MOCK_METHOD0(OnStop, void()); +}; + + +} // namespace + +class MediaStreamUIProxyTest : public testing::Test { + public: + MediaStreamUIProxyTest() + : ui_thread_(BrowserThread::UI, &message_loop_), + io_thread_(BrowserThread::IO, &message_loop_) { + proxy_ = MediaStreamUIProxy::CreateForTests(&delegate_); + } + + virtual ~MediaStreamUIProxyTest() { + proxy_.reset(); + message_loop_.RunUntilIdle(); + } + + protected: + base::MessageLoop message_loop_; + TestBrowserThread ui_thread_; + TestBrowserThread io_thread_; + + MockRenderViewHostDelegate delegate_; + MockResponseCallback response_callback_; + scoped_ptr<MediaStreamUIProxy> proxy_; +}; + +MATCHER_P(SameRequest, expected, "") { + return + expected.render_process_id == arg.render_process_id && + expected.render_view_id == arg.render_view_id && + expected.security_origin == arg.security_origin && + expected.request_type == arg.request_type && + expected.requested_device_id == arg.requested_device_id && + expected.audio_type == arg.audio_type && + expected.video_type == arg.video_type; +} + +TEST_F(MediaStreamUIProxyTest, Deny) { + MediaStreamRequest request(0, 0, GURL("http://origin/"), + MEDIA_GENERATE_STREAM, std::string(), + MEDIA_DEVICE_AUDIO_CAPTURE, + MEDIA_DEVICE_VIDEO_CAPTURE); + proxy_->RequestAccess( + request, base::Bind(&MockResponseCallback::OnAccessRequestResponse, + base::Unretained(&response_callback_))); + MediaResponseCallback callback; + EXPECT_CALL(delegate_, RequestMediaAccessPermission(SameRequest(request), _)) + .WillOnce(SaveArg<1>(&callback)); + message_loop_.RunUntilIdle(); + ASSERT_FALSE(callback.is_null()); + + MediaStreamDevices devices; + callback.Run(devices, scoped_ptr<MediaStreamUI>()); + + MediaStreamDevices response; + EXPECT_CALL(response_callback_, OnAccessRequestResponse(_)) + .WillOnce(SaveArg<0>(&response)); + message_loop_.RunUntilIdle(); + + EXPECT_TRUE(response.empty()); +} + +TEST_F(MediaStreamUIProxyTest, AcceptAndStart) { + MediaStreamRequest request(0, 0, GURL("http://origin/"), + MEDIA_GENERATE_STREAM, std::string(), + MEDIA_DEVICE_AUDIO_CAPTURE, + MEDIA_DEVICE_VIDEO_CAPTURE); + proxy_->RequestAccess( + request, base::Bind(&MockResponseCallback::OnAccessRequestResponse, + base::Unretained(&response_callback_))); + MediaResponseCallback callback; + EXPECT_CALL(delegate_, RequestMediaAccessPermission(SameRequest(request), _)) + .WillOnce(SaveArg<1>(&callback)); + message_loop_.RunUntilIdle(); + ASSERT_FALSE(callback.is_null()); + + MediaStreamDevices devices; + devices.push_back( + MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic")); + scoped_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI()); + EXPECT_CALL(*ui, OnStarted(_)); + callback.Run(devices, ui.PassAs<MediaStreamUI>()); + + MediaStreamDevices response; + EXPECT_CALL(response_callback_, OnAccessRequestResponse(_)) + .WillOnce(SaveArg<0>(&response)); + message_loop_.RunUntilIdle(); + + EXPECT_FALSE(response.empty()); + + proxy_->OnStarted(base::Closure()); + message_loop_.RunUntilIdle(); +} + +// Verify that the proxy can be deleted before the request is processed. +TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) { + MediaStreamRequest request(0, 0, GURL("http://origin/"), + MEDIA_GENERATE_STREAM, std::string(), + MEDIA_DEVICE_AUDIO_CAPTURE, + MEDIA_DEVICE_VIDEO_CAPTURE); + proxy_->RequestAccess( + request, base::Bind(&MockResponseCallback::OnAccessRequestResponse, + base::Unretained(&response_callback_))); + MediaResponseCallback callback; + EXPECT_CALL(delegate_, RequestMediaAccessPermission(SameRequest(request), _)) + .WillOnce(SaveArg<1>(&callback)); + message_loop_.RunUntilIdle(); + ASSERT_FALSE(callback.is_null()); + + proxy_.reset(); + + MediaStreamDevices devices; + scoped_ptr<MediaStreamUI> ui; + callback.Run(devices, ui.Pass()); +} + +TEST_F(MediaStreamUIProxyTest, StopFromUI) { + MediaStreamRequest request(0, 0, GURL("http://origin/"), + MEDIA_GENERATE_STREAM, std::string(), + MEDIA_DEVICE_AUDIO_CAPTURE, + MEDIA_DEVICE_VIDEO_CAPTURE); + proxy_->RequestAccess( + request, base::Bind(&MockResponseCallback::OnAccessRequestResponse, + base::Unretained(&response_callback_))); + MediaResponseCallback callback; + EXPECT_CALL(delegate_, RequestMediaAccessPermission(SameRequest(request), _)) + .WillOnce(SaveArg<1>(&callback)); + message_loop_.RunUntilIdle(); + ASSERT_FALSE(callback.is_null()); + + base::Closure stop_callback; + + MediaStreamDevices devices; + devices.push_back( + MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE, "Mic", "Mic")); + scoped_ptr<MockMediaStreamUI> ui(new MockMediaStreamUI()); + EXPECT_CALL(*ui, OnStarted(_)) + .WillOnce(SaveArg<0>(&stop_callback)); + callback.Run(devices, ui.PassAs<MediaStreamUI>()); + + MediaStreamDevices response; + EXPECT_CALL(response_callback_, OnAccessRequestResponse(_)) + .WillOnce(SaveArg<0>(&response)); + message_loop_.RunUntilIdle(); + + EXPECT_FALSE(response.empty()); + + MockStopStreamHandler stop_handler; + proxy_->OnStarted(base::Bind(&MockStopStreamHandler::OnStop, + base::Unretained(&stop_handler))); + message_loop_.RunUntilIdle(); + + ASSERT_FALSE(stop_callback.is_null()); + EXPECT_CALL(stop_handler, OnStop()); + stop_callback.Run(); + message_loop_.RunUntilIdle(); +} + +} // content diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc index c921ed2..33cf503 100644 --- a/content/browser/speech/speech_recognition_manager_impl.cc +++ b/content/browser/speech/speech_recognition_manager_impl.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "content/browser/browser_main_loop.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/media/media_stream_ui_proxy.h" #include "content/browser/speech/google_one_shot_remote_engine.h" #include "content/browser/speech/google_streaming_remote_engine.h" #include "content/browser/speech/speech_recognition_engine.h" @@ -68,7 +69,14 @@ SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl() SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() { DCHECK(g_speech_recognition_manager_impl); g_speech_recognition_manager_impl = NULL; - // Recognition sessions will be aborted by the corresponding destructors. + + for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); + ++it) { + // MediaStreamUIProxy must be deleted on the IO thread. + BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, + it->second->ui.release()); + delete it->second; + } sessions_.clear(); } @@ -79,10 +87,11 @@ int SpeechRecognitionManagerImpl::CreateSession( const int session_id = GetNextSessionID(); DCHECK(!SessionExists(session_id)); // Set-up the new session. - Session& session = sessions_[session_id]; - session.id = session_id; - session.config = config; - session.context = config.initial_context; + Session* session = new Session(); + sessions_[session_id] = session; + session->id = session_id; + session->config = config; + session->context = config.initial_context; std::string hardware_info; bool can_report_metrics = false; @@ -126,7 +135,7 @@ int SpeechRecognitionManagerImpl::CreateSession( google_remote_engine->SetConfig(remote_engine_config); - session.recognizer = new SpeechRecognizerImpl( + session->recognizer = new SpeechRecognizerImpl( this, session_id, !config.continuous, @@ -135,7 +144,7 @@ int SpeechRecognitionManagerImpl::CreateSession( // TODO(janx): Implement a SpeechRecognizerImplAndroid with a JNI interface // forwarding calls to Android's platform speech recognition service (see // crbug.com/222352). - session.recognizer = NULL; + session->recognizer = NULL; #endif return session_id; } @@ -177,7 +186,7 @@ void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, if (ask_user) { SessionsTable::iterator iter = sessions_.find(session_id); DCHECK(iter != sessions_.end()); - SpeechRecognitionSessionContext& context = iter->second.context; + SpeechRecognitionSessionContext& context = iter->second->context; context.label = BrowserMainLoop::GetMediaStreamManager()->MakeMediaAccessRequest( context.render_process_id, @@ -186,8 +195,7 @@ void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, GURL(context.context_name), base::Bind( &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback, - weak_factory_.GetWeakPtr())); - + weak_factory_.GetWeakPtr(), session_id)); return; } #endif // defined(OS_IOS) @@ -212,26 +220,29 @@ void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, } void SpeechRecognitionManagerImpl::MediaRequestPermissionCallback( - const std::string& label, const MediaStreamDevices& devices) { + int session_id, + const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUIProxy> stream_ui) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - for (SessionsTable::iterator iter = sessions_.begin(); - iter != sessions_.end(); ++iter) { - if (iter->second.context.label == label) { - bool is_allowed = false; - if (!devices.empty()) { - // Copy the approved devices array to the context for UI indication. - iter->second.context.devices = devices; - is_allowed = true; - } - // Clear the label to indicate the request has been done. - iter->second.context.label.clear(); + SessionsTable::iterator iter = sessions_.find(session_id); + if (iter == sessions_.end()) + return; - // Notify the recognition about the request result. - RecognitionAllowedCallback(iter->first, false, is_allowed); - break; - } + bool is_allowed = !devices.empty(); + if (is_allowed) { + // Copy the approved devices array to the context for UI indication. + iter->second->context.devices = devices; + + // Save the UI object. + iter->second->ui = stream_ui.Pass(); } + + // Clear the label to indicate the request has been done. + iter->second->context.label.clear(); + + // Notify the recognition about the request result. + RecognitionAllowedCallback(iter->first, false, is_allowed); } void SpeechRecognitionManagerImpl::AbortSession(int session_id) { @@ -239,12 +250,8 @@ void SpeechRecognitionManagerImpl::AbortSession(int session_id) { if (!SessionExists(session_id)) return; -#if !defined(OS_IOS) - const SpeechRecognitionSessionContext& context = - GetSessionContext(session_id); - if (!context.label.empty()) - BrowserMainLoop::GetMediaStreamManager()->CancelRequest(context.label); -#endif // !defined(OS_IOS) + SessionsTable::iterator iter = sessions_.find(session_id); + iter->second->ui.reset(); base::MessageLoop::current()->PostTask( FROM_HERE, @@ -259,12 +266,8 @@ void SpeechRecognitionManagerImpl::StopAudioCaptureForSession(int session_id) { if (!SessionExists(session_id)) return; -#if !defined(OS_IOS) - const SpeechRecognitionSessionContext& context = - GetSessionContext(session_id); - if (!context.label.empty()) - BrowserMainLoop::GetMediaStreamManager()->CancelRequest(context.label); -#endif // !defined(OS_IOS) + SessionsTable::iterator iter = sessions_.find(session_id); + iter->second->ui.reset(); base::MessageLoop::current()->PostTask( FROM_HERE, @@ -284,15 +287,11 @@ void SpeechRecognitionManagerImpl::OnRecognitionStart(int session_id) { if (!SessionExists(session_id)) return; -#if !defined(OS_IOS) - const SpeechRecognitionSessionContext& context = - GetSessionContext(session_id); - if (!context.devices.empty()) { - // Notify the UI the devices are being used. - BrowserMainLoop::GetMediaStreamManager()->NotifyUIDevicesOpened( - context.label); + SessionsTable::iterator iter = sessions_.find(session_id); + if (iter->second->ui) { + // Notify the UI that the devices are being used. + iter->second->ui->OnStarted(base::Closure()); } -#endif // !defined(OS_IOS) DCHECK_EQ(primary_session_id_, session_id); if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) @@ -406,15 +405,6 @@ void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!SessionExists(session_id)) return; -#if !defined(OS_IOS) - const SpeechRecognitionSessionContext& context = - GetSessionContext(session_id); - if (!context.devices.empty()) { - // Notify the UI the devices has been closed. - BrowserMainLoop::GetMediaStreamManager()->NotifyUIDevicesClosed( - context.label); - } -#endif // !defined(OS_IOS) if (SpeechRecognitionEventListener* delegate_listener = GetDelegateListener()) delegate_listener->OnRecognitionEnd(session_id); @@ -434,7 +424,7 @@ int SpeechRecognitionManagerImpl::GetSession( SessionsTable::const_iterator iter; for(iter = sessions_.begin(); iter != sessions_.end(); ++iter) { const int session_id = iter->first; - const SpeechRecognitionSessionContext& context = iter->second.context; + const SpeechRecognitionSessionContext& context = iter->second->context; if (context.render_process_id == render_process_id && context.render_view_id == render_view_id && context.request_id == request_id) { @@ -446,7 +436,7 @@ int SpeechRecognitionManagerImpl::GetSession( SpeechRecognitionSessionContext SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const { - return GetSession(session_id).context; + return GetSession(session_id)->context; } void SpeechRecognitionManagerImpl::AbortAllSessionsForListener( @@ -457,10 +447,10 @@ void SpeechRecognitionManagerImpl::AbortAllSessionsForListener( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); ++it) { - Session& session = it->second; - if (session.config.event_listener == listener) { - AbortSession(session.id); - session.listener_is_active = false; + Session* session = it->second; + if (session->config.event_listener == listener) { + AbortSession(session->id); + session->listener_is_active = false; } } } @@ -471,10 +461,10 @@ void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderView( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); ++it) { - Session& session = it->second; - if (session.context.render_process_id == render_process_id && - session.context.render_view_id == render_view_id) { - AbortSession(session.id); + Session* session = it->second; + if (session->context.render_process_id == render_process_id && + session->context.render_view_id == render_view_id) { + AbortSession(session->id); } } } @@ -489,7 +479,7 @@ void SpeechRecognitionManagerImpl::DispatchEvent(int session_id, if (!SessionExists(session_id)) return; - const Session& session = GetSession(session_id); + Session* session = GetSession(session_id); FSMState session_state = GetSessionState(session_id); DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE); DCHECK_LE(event, EVENT_MAX_VALUE); @@ -509,7 +499,7 @@ void SpeechRecognitionManagerImpl::DispatchEvent(int session_id, // session) are always routed to the SpeechRecognitionEventListener(s) // regardless the choices taken in this FSM. void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState( - const Session& session, FSMState session_state, FSMEvent event) { + Session* session, FSMState session_state, FSMEvent event) { // Note: since we're not tracking the state of the recognizer object, rather // we're directly retrieving it (through GetSessionState), we see its events // (that are AUDIO_ENDED and RECOGNITION_ENDED) after its state evolution @@ -522,13 +512,13 @@ void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState( case SESSION_STATE_IDLE: switch (event) { case EVENT_START: - return SessionStart(session); + return SessionStart(*session); case EVENT_ABORT: - return SessionAbort(session); + return SessionAbort(*session); case EVENT_RECOGNITION_ENDED: return SessionDelete(session); case EVENT_STOP_CAPTURE: - return SessionStopAudioCapture(session); + return SessionStopAudioCapture(*session); case EVENT_AUDIO_ENDED: return; } @@ -536,39 +526,39 @@ void SpeechRecognitionManagerImpl::ExecuteTransitionAndGetNextState( case SESSION_STATE_CAPTURING_AUDIO: switch (event) { case EVENT_STOP_CAPTURE: - return SessionStopAudioCapture(session); + return SessionStopAudioCapture(*session); case EVENT_ABORT: - return SessionAbort(session); + return SessionAbort(*session); case EVENT_START: return; case EVENT_AUDIO_ENDED: case EVENT_RECOGNITION_ENDED: - return NotFeasible(session, event); + return NotFeasible(*session, event); } break; case SESSION_STATE_WAITING_FOR_RESULT: switch (event) { case EVENT_ABORT: - return SessionAbort(session); + return SessionAbort(*session); case EVENT_AUDIO_ENDED: - return ResetCapturingSessionId(session); + return ResetCapturingSessionId(*session); case EVENT_START: case EVENT_STOP_CAPTURE: return; case EVENT_RECOGNITION_ENDED: - return NotFeasible(session, event); + return NotFeasible(*session, event); } break; } - return NotFeasible(session, event); + return NotFeasible(*session, event); } SpeechRecognitionManagerImpl::FSMState SpeechRecognitionManagerImpl::GetSessionState(int session_id) const { - const Session& session = GetSession(session_id); - if (!session.recognizer.get() || !session.recognizer->IsActive()) + Session* session = GetSession(session_id); + if (!session->recognizer.get() || !session->recognizer->IsActive()) return SESSION_STATE_IDLE; - if (session.recognizer->IsCapturingAudio()) + if (session->recognizer->IsCapturingAudio()) return SESSION_STATE_CAPTURING_AUDIO; return SESSION_STATE_WAITING_FOR_RESULT; } @@ -601,11 +591,12 @@ void SpeechRecognitionManagerImpl::ResetCapturingSessionId( primary_session_id_ = kSessionIDInvalid; } -void SpeechRecognitionManagerImpl::SessionDelete(const Session& session) { - DCHECK(session.recognizer.get() == NULL || !session.recognizer->IsActive()); - if (primary_session_id_ == session.id) +void SpeechRecognitionManagerImpl::SessionDelete(Session* session) { + DCHECK(session->recognizer == NULL || !session->recognizer->IsActive()); + if (primary_session_id_ == session->id) primary_session_id_ = kSessionIDInvalid; - sessions_.erase(session.id); + sessions_.erase(session->id); + delete session; } void SpeechRecognitionManagerImpl::NotFeasible(const Session& session, @@ -627,7 +618,7 @@ bool SpeechRecognitionManagerImpl::SessionExists(int session_id) const { return sessions_.find(session_id) != sessions_.end(); } -const SpeechRecognitionManagerImpl::Session& +SpeechRecognitionManagerImpl::Session* SpeechRecognitionManagerImpl::GetSession(int session_id) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); SessionsTable::const_iterator iter = sessions_.find(session_id); @@ -637,8 +628,8 @@ SpeechRecognitionManagerImpl::GetSession(int session_id) const { SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener( int session_id) const { - const Session& session = GetSession(session_id); - return session.listener_is_active ? session.config.event_listener : NULL; + Session* session = GetSession(session_id); + return session->listener_is_active ? session->config.event_listener : NULL; } SpeechRecognitionEventListener* @@ -648,7 +639,7 @@ SpeechRecognitionManagerImpl::GetDelegateListener() const { const SpeechRecognitionSessionConfig& SpeechRecognitionManagerImpl::GetSessionConfig(int session_id) const { - return GetSession(session_id).config; + return GetSession(session_id)->config; } bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { diff --git a/content/browser/speech/speech_recognition_manager_impl.h b/content/browser/speech/speech_recognition_manager_impl.h index e9ea5a0..d7cc3c1 100644 --- a/content/browser/speech/speech_recognition_manager_impl.h +++ b/content/browser/speech/speech_recognition_manager_impl.h @@ -21,6 +21,7 @@ namespace content { class BrowserMainLoop; +class MediaStreamUIProxy; class SpeechRecognitionManagerDelegate; class SpeechRecognizer; @@ -122,6 +123,7 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : SpeechRecognitionSessionConfig config; SpeechRecognitionSessionContext context; scoped_refptr<SpeechRecognizer> recognizer; + scoped_ptr<MediaStreamUIProxy> ui; }; // Callback issued by the SpeechRecognitionManagerDelegate for reporting @@ -130,19 +132,21 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : bool ask_user, bool is_allowed); - // Callback to get back the result of a media request. |label| is the string - // to identify the request; |devices| is an array of devices approved to be - // used for the request, |devices| is empty if the users deny the request. - void MediaRequestPermissionCallback(const std::string& label, - const MediaStreamDevices& devices); + // Callback to get back the result of a media request. |devices| is an array + // of devices approved to be used for the request, |devices| is empty if the + // users deny the request. + void MediaRequestPermissionCallback(int session_id, + const MediaStreamDevices& devices, + scoped_ptr<MediaStreamUIProxy> stream_ui); // Entry point for pushing any external event into the session handling FSM. void DispatchEvent(int session_id, FSMEvent event); // Defines the behavior of the session handling FSM, selecting the appropriate // transition according to the session, its current state and the event. - void ExecuteTransitionAndGetNextState( - const Session& session, FSMState session_state, FSMEvent event); + void ExecuteTransitionAndGetNextState(Session* session, + FSMState session_state, + FSMEvent event); // Retrieves the state of the session, enquiring directly the recognizer. FSMState GetSessionState(int session_id) const; @@ -152,16 +156,16 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : void SessionAbort(const Session& session); void SessionStopAudioCapture(const Session& session); void ResetCapturingSessionId(const Session& session); - void SessionDelete(const Session& session); + void SessionDelete(Session* session); void NotFeasible(const Session& session, FSMEvent event); bool SessionExists(int session_id) const; - const Session& GetSession(int session_id) const; + Session* GetSession(int session_id) const; SpeechRecognitionEventListener* GetListener(int session_id) const; SpeechRecognitionEventListener* GetDelegateListener() const; int GetNextSessionID(); - typedef std::map<int, Session> SessionsTable; + typedef std::map<int, Session*> SessionsTable; SessionsTable sessions_; int primary_session_id_; int last_session_id_; |