summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--content/browser/renderer_host/browser_render_process_host.cc4
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host.cc144
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host.h77
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc237
-rw-r--r--content/content_browser.gypi2
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',