diff options
6 files changed, 465 insertions, 0 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fe54971..e01c852 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1991,6 +1991,7 @@ '../content/browser/mach_broker_mac_unittest.cc', '../content/browser/plugin_service_unittest.cc', '../content/browser/renderer_host/media/audio_renderer_host_unittest.cc', + '../content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc', '../content/browser/renderer_host/media/video_capture_host_unittest.cc', '../content/browser/renderer_host/media/video_capture_manager_unittest.cc', '../content/browser/renderer_host/render_view_host_unittest.cc', diff --git a/content/browser/renderer_host/browser_render_process_host.cc b/content/browser/renderer_host/browser_render_process_host.cc index a50c607..734571e 100644 --- a/content/browser/renderer_host/browser_render_process_host.cc +++ b/content/browser/renderer_host/browser_render_process_host.cc @@ -47,6 +47,8 @@ #include "content/browser/renderer_host/gpu_message_filter.h" #include "content/browser/renderer_host/media/audio_input_renderer_host.h" #include "content/browser/renderer_host/media/audio_renderer_host.h" +#include "content/browser/renderer_host/media/media_stream_dispatcher_host.h" +#include "content/browser/renderer_host/media/video_capture_host.h" #include "content/browser/renderer_host/p2p/socket_dispatcher_host.h" #include "content/browser/renderer_host/pepper_file_message_filter.h" #include "content/browser/renderer_host/pepper_message_filter.h" @@ -354,6 +356,7 @@ void BrowserRenderProcessHost::CreateMessageFilters() { channel_->AddFilter(resource_message_filter); channel_->AddFilter(new AudioInputRendererHost()); channel_->AddFilter(new AudioRendererHost(&profile()->GetResourceContext())); + channel_->AddFilter(new VideoCaptureHost()); channel_->AddFilter( new AppCacheDispatcherHost(&profile()->GetResourceContext(), id())); channel_->AddFilter(new ClipboardMessageFilter()); @@ -365,6 +368,7 @@ void BrowserRenderProcessHost::CreateMessageFilters() { GeolocationDispatcherHost::New( id(), profile()->GetGeolocationPermissionContext())); channel_->AddFilter(new GpuMessageFilter(id(), widget_helper_.get())); + channel_->AddFilter(new media_stream::MediaStreamDispatcherHost(id())); channel_->AddFilter(new PepperFileMessageFilter(id(), profile())); channel_->AddFilter( new PepperMessageFilter(&profile()->GetResourceContext())); diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc new file mode 100644 index 0000000..9f7ddb7 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2011 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_dispatcher_host.h" + +#include "content/common/media/media_stream_messages.h" +#include "content/common/media/media_stream_options.h" + +namespace media_stream { + +MediaStreamDispatcherHost::MediaStreamDispatcherHost(int render_process_id) + : render_process_id_(render_process_id) { +} + +MediaStreamDispatcherHost::~MediaStreamDispatcherHost() { +} + +MediaStreamManager* MediaStreamDispatcherHost::manager() { + return MediaStreamManager::Get(); +} + +bool MediaStreamDispatcherHost::OnMessageReceived( + const IPC::Message& message, bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(MediaStreamDispatcherHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(MediaStreamHostMsg_GenerateStream, OnGenerateStream) + IPC_MESSAGE_HANDLER(MediaStreamHostMsg_StopGeneratedStream, + OnStopGeneratedStream) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + return handled; +} + +void MediaStreamDispatcherHost::OnChannelClosing() { + BrowserMessageFilter::OnChannelClosing(); + VLOG(1) << "MediaStreamDispatcherHost::OnChannelClosing"; + + // Since the IPC channel is gone, close all requested VideCaptureDevices and + // cancel pending requests. + manager()->CancelRequests(this); + for (StreamMap::iterator it = streams_.begin(); it != streams_.end(); it++) { + std::string label = it->first; + manager()->StopGeneratedStream(label); + } +} + +void MediaStreamDispatcherHost::OnGenerateStream( + int render_view_id, + int page_request_id, + const media_stream::StreamOptions& components, + const std::string& security_origin) { + VLOG(1) << "MediaStreamDispatcherHost::OnGenerateStream(" + << render_view_id << ", " + << page_request_id << ", [ " + << (components.audio ? "audio " : "") + << ((components.video_option & + StreamOptions::kFacingUser) ? + "video_facing_user " : "") + << ((components.video_option & + StreamOptions::kFacingEnvironment) ? + "video_facing_environment " : "") + << "], " + << security_origin << ")"; + + std::string label; + manager()->GenerateStream(this, render_process_id_, render_view_id, + components, security_origin, &label); + DCHECK(!label.empty()); + streams_[label] = StreamRequest(render_view_id, page_request_id); +} + +void MediaStreamDispatcherHost::OnStopGeneratedStream( + int render_view_id, const std::string& label) { + VLOG(1) << "MediaStreamDispatcherHost::OnStopGeneratedStream(" + << ", {label = " << label << "})"; + + StreamMap::iterator it = streams_.find(label); + DCHECK(it != streams_.end()); + manager()->StopGeneratedStream(label); + streams_.erase(it); +} + +void MediaStreamDispatcherHost::StreamGenerated( + const std::string& label, + const StreamDeviceInfoArray& audio_devices, + const StreamDeviceInfoArray& video_devices) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + VLOG(1) << "MediaStreamDispatcherHost::StreamGenerated(" + << ", {label = " << label << "})"; + + StreamMap::iterator it = streams_.find(label); + DCHECK(it != streams_.end()); + StreamRequest request = it->second; + + Send(new MediaStreamMsg_StreamGenerated( + request.render_view_id, request.page_request_id, label, audio_devices, + video_devices)); +} + +void MediaStreamDispatcherHost::StreamGenerationFailed( + const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + VLOG(1) << "MediaStreamDispatcherHost::StreamGenerationFailed(" + << ", {label = " << label << "})"; + + StreamMap::iterator it = streams_.find(label); + DCHECK(it != streams_.end()); + StreamRequest request = it->second; + streams_.erase(it); + + Send(new MediaStreamMsg_StreamGenerationFailed(request.render_view_id, + request.page_request_id)); +} + +void MediaStreamDispatcherHost::AudioDeviceFailed(const std::string& label, + int index) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + VLOG(1) << "MediaStreamDispatcherHost::AudioDeviceFailed(" + << ", {label = " << label << "})"; + + StreamMap::iterator it = streams_.find(label); + DCHECK(it != streams_.end()); + StreamRequest request = it->second; + Send(new MediaStreamHostMsg_AudioDeviceFailed(request.render_view_id, + label, + index)); +} + +void MediaStreamDispatcherHost::VideoDeviceFailed(const std::string& label, + int index) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + VLOG(1) << "MediaStreamDispatcherHost::VideoDeviceFailed(" + << ", {label = " << label << "})"; + + StreamMap::iterator it = streams_.find(label); + DCHECK(it != streams_.end()); + StreamRequest request = it->second; + Send(new MediaStreamHostMsg_VideoDeviceFailed(request.render_view_id, + label, + index)); +} + +} // namespace media_stream diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.h b/content/browser/renderer_host/media/media_stream_dispatcher_host.h new file mode 100644 index 0000000..3951339 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.h @@ -0,0 +1,77 @@ +// Copyright (c) 2011 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_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_DISPATCHER_HOST_H_ + +#include <map> +#include <string> +#include <utility> + +#include "content/browser/browser_message_filter.h" +#include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/media/media_stream_requester.h" +#include "content/common/media/media_stream_options.h" + +namespace media_stream { + +// MediaStreamDispatcherHost is a delegate for Media Stream API messages used by +// MediaStreamImpl. It's the complement of MediaStreamDispatcher +// (owned by RenderView). +class MediaStreamDispatcherHost + : public BrowserMessageFilter, + public MediaStreamRequester { + public: + explicit MediaStreamDispatcherHost(int render_process_id); + virtual ~MediaStreamDispatcherHost(); + + // MediaStreamRequester implementation. + virtual void StreamGenerated(const std::string& label, + const StreamDeviceInfoArray& audio_devices, + const StreamDeviceInfoArray& video_devices); + + virtual void StreamGenerationFailed(const std::string& label); + virtual void AudioDeviceFailed(const std::string& label, int index); + virtual void VideoDeviceFailed(const std::string& label, int index); + + // BrowserMessageFilter implementation. + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok); + virtual void OnChannelClosing(); + + private: + friend class MockMediaStreamDispatcherHost; + + void OnGenerateStream(int render_view_id, + int page_request_id, + const StreamOptions& components, + const std::string& security_origin); + + void OnStopGeneratedStream(int render_view_id, const std::string& label); + + // Returns the media stream manager to forward events to, + // creating one if needed. + MediaStreamManager* manager(); + + int render_process_id_; + struct StreamRequest { + StreamRequest() {} + StreamRequest(int render_view_id, int page_request_id) + : render_view_id(render_view_id), + page_request_id(page_request_id ) { + } + int render_view_id; + // Id of the request generated by MediaStreamDispatcher. + int page_request_id; + }; + typedef std::map<std::string, StreamRequest> StreamMap; + // Streams generated for this host. + StreamMap streams_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamDispatcherHost); +}; + +} // namespace media_stream + +#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_STREAM_DISPATCHER_HOST_H_ 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 new file mode 100644 index 0000000..76eef56 --- /dev/null +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -0,0 +1,237 @@ +// Copyright (c) 2011 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/message_loop.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/video_capture_manager.h" +#include "content/common/media/media_stream_messages.h" +#include "content/common/media/media_stream_options.h" +#include "ipc/ipc_message_macros.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::DeleteArg; +using ::testing::DoAll; +using ::testing::Return; + +const int kProcessId = 5; +const int kRenderId = 6; +const int kPageRequestId = 7; + +namespace media_stream { + +class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost { + public: + explicit MockMediaStreamDispatcherHost(MessageLoop* message_loop) + : MediaStreamDispatcherHost(kProcessId), + message_loop_(message_loop) {} + virtual ~MockMediaStreamDispatcherHost() {} + + // A list of mock methods. + MOCK_METHOD4(OnStreamGenerated, + void(int routing_id, int request_id, int audio_array_size, + int video_array_size)); + MOCK_METHOD2(OnStreamGenerationFailed, void(int routing_id, int request_id)); + MOCK_METHOD2(OnAudioDeviceFailed, void(int routing_id, int index)); + MOCK_METHOD2(OnVideoDeviceFailed, void(int routing_id, int index)); + + // Accessor to private functions. + void OnGenerateStream( + int page_request_id, + const StreamOptions& components) { + MediaStreamDispatcherHost::OnGenerateStream(kRenderId, + page_request_id, + components, + std::string()); + } + void OnStopGeneratedStream(const std::string& label) { + MediaStreamDispatcherHost::OnStopGeneratedStream(kRenderId, label); + } + + // Return the number of streams that have been opened or is being open. + size_t NumberOfStreams() { + return streams_.size(); + } + + std::string label_; + StreamDeviceInfoArray audio_devices_; + StreamDeviceInfoArray video_devices_; + + private: + // This method is used to dispatch IPC messages to the renderer. We intercept + // these messages here and dispatch to our mock methods to verify the + // conversation between this object and the renderer. + virtual bool Send(IPC::Message* message) { + CHECK(message); + + // In this method we dispatch the messages to the according handlers as if + // we are the renderer. + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MockMediaStreamDispatcherHost, *message) + IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated, OnStreamGenerated) + IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed, + OnStreamGenerationFailed) + IPC_MESSAGE_HANDLER(MediaStreamHostMsg_VideoDeviceFailed, + OnVideoDeviceFailed) + IPC_MESSAGE_HANDLER(MediaStreamHostMsg_AudioDeviceFailed, + OnAudioDeviceFailed) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + EXPECT_TRUE(handled); + + delete message; + return true; + } + + // These handler methods do minimal things and delegate to the mock methods. + void OnStreamGenerated( + const IPC::Message& msg, + int request_id, + std::string label, + StreamDeviceInfoArray audio_device_list, + StreamDeviceInfoArray video_device_list) { + OnStreamGenerated(msg.routing_id(), request_id, audio_device_list.size(), + video_device_list.size()); + // Notify that the event have occured. + message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + label_ = label; + audio_devices_ = audio_device_list; + video_devices_ = video_device_list; + } + + void OnStreamGenerationFailed(const IPC::Message& msg, + int request_id) { + OnStreamGenerationFailed(msg.routing_id(), request_id); + message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + label_= ""; + } + + void OnAudioDeviceFailed(const IPC::Message& msg, + std::string label, + int index) { + OnAudioDeviceFailed(msg.routing_id(), index); + audio_devices_.erase(audio_devices_.begin() + index); + message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + void OnVideoDeviceFailed(const IPC::Message& msg, + std::string label, + int index) { + OnVideoDeviceFailed(msg.routing_id(), index); + video_devices_.erase(video_devices_.begin() + index); + message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + MessageLoop* message_loop_; +}; + +class MediaStreamDispatcherHostTest : public testing::Test { + public: + void WaitForResult() { + message_loop_->Run(); + } + + protected: + virtual void SetUp() { + // Create message loop so that + // DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) passes. + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get())); + + // Make sure the MediaStreamManager exist and use + // fake audio / video devices. + MediaStreamManager* manager = MediaStreamManager::Get(); + manager->UseFakeDevice(); + + host_ = + new MockMediaStreamDispatcherHost(message_loop_.get()); + } + + scoped_refptr<MockMediaStreamDispatcherHost> host_; + scoped_ptr<MessageLoop> message_loop_; + scoped_ptr<BrowserThread> io_thread_; +}; + +TEST_F(MediaStreamDispatcherHostTest, GenerateStream) { + StreamOptions options(false, StreamOptions::kFacingUser); + + EXPECT_CALL(*host_, OnStreamGenerated(kRenderId, kPageRequestId, 0, 1)); + host_->OnGenerateStream(kPageRequestId, options); + + WaitForResult(); + + std::string label = host_->label_; + + EXPECT_EQ(host_->audio_devices_.size(), 0u); + EXPECT_EQ(host_->video_devices_.size(), 1u); + EXPECT_EQ(host_->NumberOfStreams(), 1u); + + host_->OnStopGeneratedStream(label); + EXPECT_EQ(host_->NumberOfStreams(), 0u); +} + +TEST_F(MediaStreamDispatcherHostTest, GenerateTwoStreams) { + StreamOptions options(false, StreamOptions::kFacingUser); + + // Generate first stream. + EXPECT_CALL(*host_, OnStreamGenerated(kRenderId, kPageRequestId, 0, 1)); + host_->OnGenerateStream(kPageRequestId, options); + + WaitForResult(); + + // Check the latest generated stream. + EXPECT_EQ(host_->audio_devices_.size(), 0u); + EXPECT_EQ(host_->video_devices_.size(), 1u); + // Check that we now have one opened streams. + EXPECT_EQ(host_->NumberOfStreams(), 1u); + std::string label1 = host_->label_; + + // Generate second stream. + EXPECT_CALL(*host_, OnStreamGenerated(kRenderId, kPageRequestId+1, 0, 1)); + host_->OnGenerateStream(kPageRequestId+1, options); + + WaitForResult(); + std::string label2 = host_->label_; + + // Check the latest generated stream. + EXPECT_EQ(host_->audio_devices_.size(), 0u); + EXPECT_EQ(host_->video_devices_.size(), 1u); + // Check that we now have two opened streams. + EXPECT_EQ(host_->NumberOfStreams(), 2u); + + host_->OnStopGeneratedStream(label1); + host_->OnStopGeneratedStream(label2); + EXPECT_EQ(host_->NumberOfStreams(), 0u); +} + +TEST_F(MediaStreamDispatcherHostTest, FailDevice) { + StreamOptions options(false, StreamOptions::kFacingUser); + + EXPECT_CALL(*host_, OnStreamGenerated(kRenderId, kPageRequestId, 0, 1)); + host_->OnGenerateStream(kPageRequestId, options); + WaitForResult(); + std::string label = host_->label_; + + EXPECT_EQ(host_->audio_devices_.size(), 0u); + EXPECT_EQ(host_->video_devices_.size(), 1u); + EXPECT_EQ(host_->NumberOfStreams(), 1u); + + EXPECT_CALL(*host_, OnVideoDeviceFailed(kRenderId, 0)); + int session_id = host_->video_devices_[0].session_id; + MediaStreamManager::Get()->video_capture_manager()->Error(session_id); + WaitForResult(); + EXPECT_EQ(host_->video_devices_.size(), 0u); + EXPECT_EQ(host_->NumberOfStreams(), 1u); + + // TODO(perkj): test audio device failure? + + host_->OnStopGeneratedStream(label); + EXPECT_EQ(host_->NumberOfStreams(), 0u); +} + +}; // namespace media_stream diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 44b82c3..48518ab 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -272,6 +272,8 @@ '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', 'browser/renderer_host/media/media_stream_manager.h', 'browser/renderer_host/media/media_stream_provider.h', |