Chromium Code Reviews| 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 |