Index: content/browser/media/capture/video_capture_browsertest.cc |
diff --git a/content/browser/media/capture/video_capture_browsertest.cc b/content/browser/media/capture/video_capture_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d894c37dc19b8c779a94dc3b32d2dcba8af1e93d |
--- /dev/null |
+++ b/content/browser/media/capture/video_capture_browsertest.cc |
@@ -0,0 +1,263 @@ |
+// Copyright 2017 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 "base/command_line.h" |
+#include "base/run_loop.h" |
+#include "build/build_config.h" |
+#include "content/browser/browser_main_loop.h" |
+#include "content/browser/renderer_host/media/media_stream_manager.h" |
+#include "content/browser/renderer_host/media/video_capture_manager.h" |
+#include "content/public/common/content_switches.h" |
+#include "content/public/test/content_browser_test.h" |
+#include "media/base/bind_to_current_loop.h" |
+#include "media/base/media_switches.h" |
+#include "media/capture/video_capture_types.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+ |
+using testing::_; |
+using testing::AtLeast; |
+using testing::Invoke; |
+using testing::InvokeWithoutArgs; |
+using testing::Values; |
+ |
+namespace content { |
+ |
+class MockVideoCaptureControllerEventHandler |
+ : public VideoCaptureControllerEventHandler { |
+ public: |
+ MOCK_METHOD4(DoOnBufferCreated, |
+ void(VideoCaptureControllerID id, |
+ mojo::ScopedSharedBufferHandle* handle, |
+ int length, |
+ int buffer_id)); |
+ MOCK_METHOD2(OnBufferDestroyed, |
+ void(VideoCaptureControllerID, int buffer_id)); |
+ MOCK_METHOD3(OnBufferReady, |
+ void(VideoCaptureControllerID id, |
+ int buffer_id, |
+ const media::mojom::VideoFrameInfoPtr& frame_info)); |
+ MOCK_METHOD1(OnStarted, void(VideoCaptureControllerID)); |
+ MOCK_METHOD1(OnEnded, void(VideoCaptureControllerID)); |
+ MOCK_METHOD1(OnError, void(VideoCaptureControllerID)); |
+ MOCK_METHOD1(OnStartedUsingGpuDecode, void(VideoCaptureControllerID)); |
+ MOCK_METHOD1(OnStoppedUsingGpuDecode, void(VideoCaptureControllerID)); |
+ |
+ void OnBufferCreated(VideoCaptureControllerID id, |
+ mojo::ScopedSharedBufferHandle handle, |
+ int length, |
+ int buffer_id) override { |
+ DoOnBufferCreated(id, &handle, length, buffer_id); |
+ } |
+}; |
+ |
+class MockMediaStreamProviderListener : public MediaStreamProviderListener { |
+ public: |
+ MOCK_METHOD2(Opened, void(MediaStreamType, int)); |
+ MOCK_METHOD2(Closed, void(MediaStreamType, int)); |
+ MOCK_METHOD2(Aborted, void(MediaStreamType, int)); |
+}; |
+ |
+struct TestParams { |
+ std::string fake_device_factory_config_string; |
+ size_t device_index_to_use; |
+ media::VideoPixelFormat pixel_format_to_use; |
+ gfx::Size resolution_to_use; |
+ float frame_rate_to_use; |
+}; |
+ |
+struct FrameInfo { |
+ gfx::Size size; |
+ media::VideoPixelFormat pixel_format; |
+ media::VideoPixelStorage storage_type; |
+ base::TimeDelta timestamp; |
+}; |
+ |
+class VideoCaptureBrowserTest |
+ : public ContentBrowserTest, |
+ public ::testing::WithParamInterface<TestParams> { |
+ protected: |
+ void SetUpCommandLine(base::CommandLine* command_line) override { |
+ command_line->AppendSwitchASCII( |
+ switches::kUseFakeDeviceForMediaStream, |
+ GetParam().fake_device_factory_config_string); |
+ command_line->AppendSwitch(switches::kUseFakeUIForMediaStream); |
+ } |
+ |
+ void SetUpRequiringBrowserMainLoopOnMainThread() { |
+ BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance(); |
+ ASSERT_TRUE(browser_main_loop); |
+ media_stream_manager_ = browser_main_loop->media_stream_manager(); |
+ ASSERT_TRUE(media_stream_manager_); |
+ } |
+ |
+ void SetUpAndStartCaptureDeviceOnIOThread(base::Closure continuation) { |
+ video_capture_manager_ = media_stream_manager_->video_capture_manager(); |
+ ASSERT_TRUE(video_capture_manager_); |
+ video_capture_manager_->RegisterListener(&mock_stream_provider_listener_); |
+ video_capture_manager_->EnumerateDevices( |
+ base::Bind(&VideoCaptureBrowserTest::OnDeviceDescriptorsReceived, |
+ base::Unretained(this), std::move(continuation))); |
+ } |
+ |
+ void OnDeviceDescriptorsReceived( |
+ base::Closure continuation, |
+ const media::VideoCaptureDeviceDescriptors& descriptors) { |
+ ASSERT_TRUE(GetParam().device_index_to_use < descriptors.size()); |
+ const auto& descriptor = descriptors[GetParam().device_index_to_use]; |
+ MediaStreamDevice media_stream_device( |
+ MEDIA_DEVICE_VIDEO_CAPTURE, descriptor.device_id, |
+ descriptor.display_name, descriptor.facing); |
+ session_id_ = video_capture_manager_->Open(media_stream_device); |
+ media::VideoCaptureParams capture_params; |
+ capture_params.requested_format = media::VideoCaptureFormat( |
+ GetParam().resolution_to_use, GetParam().frame_rate_to_use, |
+ GetParam().pixel_format_to_use); |
+ video_capture_manager_->StartCaptureForClient( |
+ session_id_, capture_params, stub_client_id_, |
+ &mock_controller_event_handler_, |
+ base::Bind(&VideoCaptureBrowserTest::OnConnectClientToControllerAnswer, |
+ base::Unretained(this), std::move(continuation))); |
+ } |
+ |
+ void OnConnectClientToControllerAnswer( |
+ base::Closure continuation, |
+ const base::WeakPtr<VideoCaptureController>& controller) { |
+ ASSERT_TRUE(controller.get()); |
+ controller_ = controller; |
+ if (!continuation) |
+ return; |
+ continuation.Run(); |
+ } |
+ |
+ void TearDownCaptureDeviceOnIOThread(base::Closure continuation, |
+ bool post_to_end_of_message_queue) { |
+ // StopCaptureForClient must not be called synchronously from either the |
+ // |done_cb| passed to StartCaptureForClient() nor any callback made to a |
+ // VideoCaptureControllerEventHandler. To satisfy this, we have to post our |
+ // invocation to the end of the IO message queue. |
+ if (post_to_end_of_message_queue) { |
+ base::ThreadTaskRunnerHandle::Get()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&VideoCaptureBrowserTest::TearDownCaptureDeviceOnIOThread, |
+ base::Unretained(this), continuation, false)); |
+ return; |
+ } |
+ |
+ video_capture_manager_->StopCaptureForClient( |
+ controller_.get(), stub_client_id_, &mock_controller_event_handler_, |
+ false); |
+ |
+ EXPECT_CALL(mock_stream_provider_listener_, Closed(_, _)) |
+ .WillOnce(InvokeWithoutArgs([continuation]() { continuation.Run(); })); |
+ |
+ video_capture_manager_->Close(session_id_); |
+ } |
+ |
+ void RunStartAndImmediatelyStopTest() { |
+ SetUpRequiringBrowserMainLoopOnMainThread(); |
+ base::RunLoop run_loop; |
+ auto quit_run_loop_on_current_thread_cb = |
+ media::BindToCurrentLoop(run_loop.QuitClosure()); |
+ auto after_start_continuation = |
+ base::Bind(&VideoCaptureBrowserTest::TearDownCaptureDeviceOnIOThread, |
+ base::Unretained(this), |
+ std::move(quit_run_loop_on_current_thread_cb), true); |
+ BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind( |
+ &VideoCaptureBrowserTest::SetUpAndStartCaptureDeviceOnIOThread, |
+ base::Unretained(this), std::move(after_start_continuation))); |
+ run_loop.Run(); |
+ } |
+ |
+ void RunReceiveFramesFromFakeCaptureDeviceTest() { |
+ SetUpRequiringBrowserMainLoopOnMainThread(); |
+ |
+ std::vector<FrameInfo> received_frame_infos; |
+ static const size_t num_frames_to_receive = 3; |
mcasas
2017/03/03 00:21:12
nit: s/num_frames_to_receive/kNumFramesToReceive/
chfremer
2017/03/03 18:21:13
Done.
|
+ base::RunLoop run_loop; |
+ |
+ auto quit_run_loop_on_current_thread_cb = |
+ media::BindToCurrentLoop(run_loop.QuitClosure()); |
+ auto finish_test_cb = |
+ base::Bind(&VideoCaptureBrowserTest::TearDownCaptureDeviceOnIOThread, |
+ base::Unretained(this), |
+ std::move(quit_run_loop_on_current_thread_cb), true); |
+ |
+ EXPECT_CALL(mock_controller_event_handler_, DoOnBufferCreated(_, _, _, _)) |
+ .Times(AtLeast(1)); |
+ EXPECT_CALL(mock_controller_event_handler_, OnBufferReady(_, _, _)) |
+ .WillRepeatedly( |
+ Invoke([&received_frame_infos, &finish_test_cb]( |
+ VideoCaptureControllerID id, int buffer_id, |
+ const media::mojom::VideoFrameInfoPtr& frame_info) { |
+ FrameInfo received_frame_info; |
+ received_frame_info.pixel_format = frame_info->pixel_format; |
+ received_frame_info.storage_type = frame_info->storage_type; |
+ received_frame_info.size = frame_info->coded_size; |
+ received_frame_info.timestamp = frame_info->timestamp; |
+ received_frame_infos.emplace_back(received_frame_info); |
+ if (received_frame_infos.size() >= num_frames_to_receive) { |
+ finish_test_cb.Run(); |
+ return; |
mcasas
2017/03/03 00:21:13
No need to return here since the callback does
not
chfremer
2017/03/03 18:21:13
Done.
|
+ } |
+ })); |
+ |
+ base::Closure do_nothing; |
+ BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind( |
+ &VideoCaptureBrowserTest::SetUpAndStartCaptureDeviceOnIOThread, |
+ base::Unretained(this), std::move(do_nothing))); |
+ run_loop.Run(); |
+ |
+ EXPECT_GE(received_frame_infos.size(), num_frames_to_receive); |
+ base::TimeDelta previous_timestamp; |
+ bool first_frame = true; |
+ for (const auto& frame_info : received_frame_infos) { |
+ EXPECT_EQ(GetParam().pixel_format_to_use, frame_info.pixel_format); |
+ EXPECT_EQ(media::PIXEL_STORAGE_CPU, frame_info.storage_type); |
+ EXPECT_EQ(GetParam().resolution_to_use, frame_info.size); |
+ // Timestamps are expected to increase |
+ if (!first_frame) |
+ EXPECT_GT(frame_info.timestamp, previous_timestamp); |
+ first_frame = false; |
+ previous_timestamp = frame_info.timestamp; |
+ } |
+ } |
+ |
+ private: |
+ MediaStreamManager* media_stream_manager_ = nullptr; |
+ VideoCaptureManager* video_capture_manager_ = nullptr; |
+ int session_id_ = 0; |
+ const VideoCaptureControllerID stub_client_id_ = 123; |
+ MockMediaStreamProviderListener mock_stream_provider_listener_; |
+ MockVideoCaptureControllerEventHandler mock_controller_event_handler_; |
+ base::WeakPtr<VideoCaptureController> controller_; |
+}; |
+ |
+IN_PROC_BROWSER_TEST_P(VideoCaptureBrowserTest, StartAndImmediatelyStop) { |
+ RunStartAndImmediatelyStopTest(); |
mcasas
2017/03/03 00:21:12
Instead of adding this function to VideoCaptureBro
chfremer
2017/03/03 18:21:13
Done.
|
+} |
+ |
+IN_PROC_BROWSER_TEST_P(VideoCaptureBrowserTest, |
+ ReceiveFramesFromFakeCaptureDevice) { |
+ RunReceiveFramesFromFakeCaptureDeviceTest(); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ , |
+ VideoCaptureBrowserTest, |
+ Values(TestParams{"fps=25,device-count=2", 0, media::PIXEL_FORMAT_I420, |
+ gfx::Size(1280, 720), 25.0f}, |
+ // The 2nd device outputs Y16 |
+ TestParams{"fps=25,device-count=2", 1, media::PIXEL_FORMAT_Y16, |
+ gfx::Size(1280, 720), 25.0f}, |
+ TestParams{"fps=15,device-count=2", 1, media::PIXEL_FORMAT_Y16, |
+ gfx::Size(640, 480), 15.0f}, |
+ // The 3rd device outputs MJPEG, which is converted to I420. |
+ TestParams{"fps=15,device-count=3", 2, media::PIXEL_FORMAT_I420, |
+ gfx::Size(640, 480), 25.0f})); |
+ |
+} // namespace content |