Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Unified Diff: media/capture/video/chromeos/camera_device_delegate_unittest.cc

Issue 2837273004: media: add video capture device for ARC++ camera HAL v3 (Closed)
Patch Set: address chfremer's comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/capture/video/chromeos/camera_device_delegate_unittest.cc
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..13aaee08d5092ecf66457863e08a54cd22146989
--- /dev/null
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -0,0 +1,558 @@
+// 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 "media/capture/video/chromeos/camera_device_delegate.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/capture/video/chromeos/camera_device_context.h"
+#include "media/capture/video/chromeos/camera_hal_delegate.h"
+#include "media/capture/video/chromeos/mock_camera_module.h"
+#include "media/capture/video/chromeos/mojo/arc_camera3.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::A;
+using testing::AtLeast;
+using testing::Invoke;
+
+namespace media {
+
+namespace {
+
+void DumpError(const tracked_objects::Location& location,
+ const std::string& message) {
+ DPLOG(ERROR) << location.ToString() << " " << message;
+}
+
+class MockVideoCaptureClient : public VideoCaptureDevice::Client {
+ public:
+ MOCK_METHOD0(DoReserveOutputBuffer, void(void));
+ MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
+ MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
+ MOCK_METHOD0(DoResurrectLastOutputBuffer, void(void));
+ MOCK_METHOD2(OnError,
+ void(const tracked_objects::Location& from_here,
+ const std::string& reason));
+ MOCK_CONST_METHOD0(GetBufferPoolUtilization, double(void));
+ MOCK_METHOD0(OnStarted, void(void));
+
+ explicit MockVideoCaptureClient(base::Callback<void()> quit_cb)
+ : quit_cb_(quit_cb) {
+ ON_CALL(*this, OnError(_, _)).WillByDefault(Invoke(DumpError));
+ }
+
+ ~MockVideoCaptureClient() { quit_cb_.Run(); }
+
+ void OnIncomingCapturedData(const uint8_t* data,
+ int length,
+ const VideoCaptureFormat& format,
+ int rotation,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp,
+ int frame_feedback_id) override {
+ ASSERT_GT(length, 0);
+ ASSERT_TRUE(data);
+ quit_cb_.Run();
+ }
+
+ // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
+ Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
+ media::VideoPixelStorage storage,
+ int frame_feedback_id) override {
+ DoReserveOutputBuffer();
+ NOTREACHED() << "This should never be called";
+ return Buffer();
+ }
+ void OnIncomingCapturedBuffer(Buffer buffer,
+ const VideoCaptureFormat& format,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp) override {
+ DoOnIncomingCapturedBuffer();
+ }
+ void OnIncomingCapturedBufferExt(
+ Buffer buffer,
+ const VideoCaptureFormat& format,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp,
+ gfx::Rect visible_rect,
+ const VideoFrameMetadata& additional_metadata) override {
+ DoOnIncomingCapturedVideoFrame();
+ }
+ Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions,
+ media::VideoPixelFormat format,
+ media::VideoPixelStorage storage,
+ int frame_feedback_id) {
+ DoResurrectLastOutputBuffer();
+ NOTREACHED() << "This should never be called";
+ return Buffer();
+ }
+
+ private:
+ base::Callback<void()> quit_cb_;
+};
+
+class MockCameraDevice : public arc::mojom::Camera3DeviceOps {
+ public:
+ MockCameraDevice() {}
+
+ ~MockCameraDevice() {}
+
+ void Initialize(arc::mojom::Camera3CallbackOpsPtr callback_ops,
+ InitializeCallback callback) override {
+ DoInitialize(callback_ops, callback);
+ }
+ MOCK_METHOD2(DoInitialize,
+ void(arc::mojom::Camera3CallbackOpsPtr& callback_ops,
+ InitializeCallback& callback));
+
+ void ConfigureStreams(arc::mojom::Camera3StreamConfigurationPtr config,
+ ConfigureStreamsCallback callback) override {
+ DoConfigureStreams(config, callback);
+ }
+ MOCK_METHOD2(DoConfigureStreams,
+ void(arc::mojom::Camera3StreamConfigurationPtr& config,
+ ConfigureStreamsCallback& callback));
+
+ void ConstructDefaultRequestSettings(
+ arc::mojom::Camera3RequestTemplate type,
+ ConstructDefaultRequestSettingsCallback callback) override {
+ DoConstructDefaultRequestSettings(type, callback);
+ }
+ MOCK_METHOD2(DoConstructDefaultRequestSettings,
+ void(arc::mojom::Camera3RequestTemplate type,
+ ConstructDefaultRequestSettingsCallback& callback));
+
+ void ProcessCaptureRequest(arc::mojom::Camera3CaptureRequestPtr request,
+ ProcessCaptureRequestCallback callback) override {
+ DoProcessCaptureRequest(request, callback);
+ }
+ MOCK_METHOD2(DoProcessCaptureRequest,
+ void(arc::mojom::Camera3CaptureRequestPtr& request,
+ ProcessCaptureRequestCallback& callback));
+
+ void Dump(mojo::ScopedHandle fd) override { DoDump(fd); }
+ MOCK_METHOD1(DoDump, void(mojo::ScopedHandle& fd));
+
+ void Flush(FlushCallback callback) override { DoFlush(callback); }
+ MOCK_METHOD1(DoFlush, void(FlushCallback& callback));
+
+ void RegisterBuffer(uint64_t buffer_id,
+ arc::mojom::Camera3DeviceOps::BufferType type,
+ std::vector<mojo::ScopedHandle> fds,
+ uint32_t drm_format,
+ arc::mojom::HalPixelFormat hal_pixel_format,
+ uint32_t width,
+ uint32_t height,
+ const std::vector<uint32_t>& strides,
+ const std::vector<uint32_t>& offsets,
+ RegisterBufferCallback callback) override {
+ DoRegisterBuffer(buffer_id, type, fds, drm_format, hal_pixel_format, width,
+ height, strides, offsets, callback);
+ }
+ MOCK_METHOD10(DoRegisterBuffer,
+ void(uint64_t buffer_id,
+ arc::mojom::Camera3DeviceOps::BufferType type,
+ std::vector<mojo::ScopedHandle>& fds,
+ uint32_t drm_format,
+ arc::mojom::HalPixelFormat hal_pixel_format,
+ uint32_t width,
+ uint32_t height,
+ const std::vector<uint32_t>& strides,
+ const std::vector<uint32_t>& offsets,
+ RegisterBufferCallback& callback));
+
+ void Close(CloseCallback callback) override { DoClose(callback); }
+ MOCK_METHOD1(DoClose, void(CloseCallback& callback));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockCameraDevice);
+};
+
+const VideoCaptureDeviceDescriptor kDefaultDescriptor("Fake device", "0");
+const VideoCaptureFormat kDefaultCaptureFormat(gfx::Size(1280, 720),
+ 30.0,
+ PIXEL_FORMAT_I420);
+
+} // namespace
+
+class CameraDeviceDelegateTest : public ::testing::Test {
+ public:
+ CameraDeviceDelegateTest()
+ : mock_camera_device_binding_(&mock_camera_device_),
+ device_delegate_thread_("DeviceDelegateThread"),
+ hal_delegate_thread_("HalDelegateThread") {}
+
+ void SetUp() override {
+ hal_delegate_thread_.Start();
+ camera_hal_delegate_ =
+ new CameraHalDelegate(hal_delegate_thread_.task_runner());
+ camera_hal_delegate_->StartForTesting(
+ mock_camera_module_.GetInterfacePtrInfo());
+
+ ResetCaptureClient();
+ }
+
+ void TearDown() override {
+ camera_hal_delegate_->Reset();
+ hal_delegate_thread_.Stop();
+ }
+
+ void AllocateDeviceWithDescriptor(VideoCaptureDeviceDescriptor descriptor) {
+ ASSERT_FALSE(device_delegate_thread_.IsRunning());
+ ASSERT_FALSE(camera_device_delegate_);
+ device_delegate_thread_.Start();
+ camera_device_delegate_ =
+ new CameraDeviceDelegate(descriptor, camera_hal_delegate_,
+ device_delegate_thread_.task_runner());
+ }
+
+ void GetFakeCameraInfo(uint32_t camera_id,
+ arc::mojom::CameraModule::GetCameraInfoCallback& cb) {
+ arc::mojom::CameraInfoPtr camera_info = arc::mojom::CameraInfo::New();
+ arc::mojom::CameraMetadataPtr static_metadata =
+ arc::mojom::CameraMetadata::New();
+ switch (camera_id) {
+ case 0:
+ camera_info->facing = arc::mojom::CameraFacing::CAMERA_FACING_FRONT;
+ camera_info->orientation = 0;
+ camera_info->static_camera_characteristics = std::move(static_metadata);
+ break;
+ default:
+ FAIL() << "Invalid camera id";
+ }
+ std::move(cb).Run(0, std::move(camera_info));
+ }
+
+ void OpenMockCameraDevice(
+ int32_t camera_id,
+ arc::mojom::Camera3DeviceOpsRequest& device_ops_request,
+ base::OnceCallback<void(int32_t)>& callback) {
+ mock_camera_device_binding_.Bind(std::move(device_ops_request));
+ std::move(callback).Run(0);
+ }
+
+ void InitializeMockCameraDevice(
+ arc::mojom::Camera3CallbackOpsPtr& callback_ops,
+ base::OnceCallback<void(int32_t)>& callback) {
+ callback_ops_ = std::move(callback_ops);
+ std::move(callback).Run(0);
+ }
+
+ void ConfigureFakeStreams(
+ arc::mojom::Camera3StreamConfigurationPtr& config,
+ base::OnceCallback<
+ void(int32_t, arc::mojom::Camera3StreamConfigurationPtr)>& callback) {
+ ASSERT_EQ(1u, config->streams.size());
+ ASSERT_EQ(static_cast<uint32_t>(kDefaultCaptureFormat.frame_size.width()),
+ config->streams[0]->width);
+ ASSERT_EQ(static_cast<uint32_t>(kDefaultCaptureFormat.frame_size.height()),
+ config->streams[0]->height);
+ ASSERT_EQ(arc::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_YCbCr_420_888,
+ config->streams[0]->format);
+ config->streams[0]->usage = 0;
+ config->streams[0]->max_buffers = 1;
+ std::move(callback).Run(0, std::move(config));
+ }
+
+ void ConstructFakeRequestSettings(
+ arc::mojom::Camera3RequestTemplate type,
+ base::OnceCallback<void(arc::mojom::CameraMetadataPtr)>& callback) {
+ ASSERT_EQ(arc::mojom::Camera3RequestTemplate::CAMERA3_TEMPLATE_PREVIEW,
+ type);
+ arc::mojom::CameraMetadataPtr fake_settings =
+ arc::mojom::CameraMetadata::New();
+ fake_settings->entry_count = 1;
+ fake_settings->entry_capacity = 1;
+ fake_settings->entries = std::vector<arc::mojom::CameraMetadataEntryPtr>();
+ std::move(callback).Run(std::move(fake_settings));
+ }
+
+ void RegisterBuffer(uint64_t buffer_id,
+ arc::mojom::Camera3DeviceOps::BufferType type,
+ std::vector<mojo::ScopedHandle>& fds,
+ uint32_t drm_format,
+ arc::mojom::HalPixelFormat hal_pixel_format,
+ uint32_t width,
+ uint32_t height,
+ const std::vector<uint32_t>& strides,
+ const std::vector<uint32_t>& offsets,
+ base::OnceCallback<void(int32_t)>& callback) {
+ std::move(callback).Run(0);
+ }
+
+ void ProcessCaptureRequest(arc::mojom::Camera3CaptureRequestPtr& request,
+ base::OnceCallback<void(int32_t)>& callback) {
+ std::move(callback).Run(0);
+
+ arc::mojom::Camera3NotifyMsgPtr msg = arc::mojom::Camera3NotifyMsg::New();
+ msg->type = arc::mojom::Camera3MsgType::CAMERA3_MSG_SHUTTER;
+ msg->message = arc::mojom::Camera3NotifyMsgMessage::New();
+ arc::mojom::Camera3ShutterMsgPtr shutter_msg =
+ arc::mojom::Camera3ShutterMsg::New();
+ shutter_msg->timestamp = base::TimeTicks::Now().ToInternalValue();
+ msg->message->set_shutter(std::move(shutter_msg));
+ callback_ops_->Notify(std::move(msg));
+
+ arc::mojom::Camera3CaptureResultPtr result =
+ arc::mojom::Camera3CaptureResult::New();
+ result->frame_number = request->frame_number;
+ result->result = arc::mojom::CameraMetadata::New();
+ result->output_buffers = std::move(request->output_buffers);
+ result->partial_result = 1;
+ callback_ops_->ProcessCaptureResult(std::move(result));
+ }
+
+ void CloseMockCameraDevice(base::OnceCallback<void(int32_t)>& callback) {
+ if (mock_camera_device_binding_.is_bound()) {
+ mock_camera_device_binding_.Close();
+ }
+ callback_ops_.reset();
+ std::move(callback).Run(0);
+ }
+
+ void SetUpExpectationUntilInitialized() {
+ EXPECT_CALL(mock_camera_module_, DoGetCameraInfo(0, _))
+ .Times(1)
+ .WillOnce(Invoke(this, &CameraDeviceDelegateTest::GetFakeCameraInfo));
+ EXPECT_CALL(mock_camera_module_, DoOpenDevice(0, _, _))
+ .Times(1)
+ .WillOnce(
+ Invoke(this, &CameraDeviceDelegateTest::OpenMockCameraDevice));
+ EXPECT_CALL(mock_camera_device_, DoInitialize(_, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ this, &CameraDeviceDelegateTest::InitializeMockCameraDevice));
+ }
+
+ void SetUpExpectationUntilStreamConfigured() {
+ SetUpExpectationUntilInitialized();
+ EXPECT_CALL(mock_camera_device_, DoConfigureStreams(_, _))
+ .Times(1)
+ .WillOnce(
+ Invoke(this, &CameraDeviceDelegateTest::ConfigureFakeStreams));
+ }
+
+ void SetUpExpectationUntilCapturing(MockVideoCaptureClient* mock_client) {
+ SetUpExpectationUntilStreamConfigured();
+ EXPECT_CALL(mock_camera_device_, DoConstructDefaultRequestSettings(_, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ this, &CameraDeviceDelegateTest::ConstructFakeRequestSettings));
+ EXPECT_CALL(*mock_client, OnStarted()).Times(1);
+ }
+
+ void SetUpExpectationForCaptureLoop() {
+ EXPECT_CALL(mock_camera_device_,
+ DoRegisterBuffer(_, _, _, _, _, _, _, _, _, _))
+ .Times(AtLeast(1))
+ .WillOnce(Invoke(this, &CameraDeviceDelegateTest::RegisterBuffer))
+ .WillRepeatedly(
+ Invoke(this, &CameraDeviceDelegateTest::RegisterBuffer));
+ EXPECT_CALL(mock_camera_device_, DoProcessCaptureRequest(_, _))
+ .Times(AtLeast(1))
+ .WillOnce(
+ Invoke(this, &CameraDeviceDelegateTest::ProcessCaptureRequest))
+ .WillRepeatedly(
+ Invoke(this, &CameraDeviceDelegateTest::ProcessCaptureRequest));
+ }
+
+ void SetUpExpectationForClose() {
+ EXPECT_CALL(mock_camera_device_, DoClose(_))
+ .Times(1)
+ .WillOnce(
+ Invoke(this, &CameraDeviceDelegateTest::CloseMockCameraDevice));
+ }
+
+ void ResetCaptureClient() {
+ mock_client_.reset(
+ new MockVideoCaptureClient(BindToCurrentLoop(base::BindRepeating(
+ &CameraDeviceDelegateTest::QuitRunLoop, base::Unretained(this)))));
+ }
+
+ void ResetDevice() {
+ ASSERT_TRUE(device_delegate_thread_.IsRunning());
+ ASSERT_TRUE(camera_device_delegate_);
+ while (!camera_device_delegate_->HasOneRef()) {
+ base::PlatformThread::YieldCurrentThread();
+ }
+ camera_device_delegate_ = nullptr;
+ device_delegate_thread_.Stop();
+ }
+
+ void DoLoop() {
+ run_loop_.reset(new base::RunLoop());
+ run_loop_->Run();
+ }
+
+ void QuitRunLoop() {
+ if (run_loop_) {
+ run_loop_->Quit();
+ }
+ }
+
+ CameraDeviceContext::State GetState() {
+ if (camera_device_delegate_->device_context_) {
+ return camera_device_delegate_->device_context_->GetState();
+ } else {
+ // No device context means the VCD is either not started yet or already
+ // stopped.
+ return CameraDeviceContext::State::kStopped;
+ }
+ }
+
+ protected:
+ scoped_refptr<CameraHalDelegate> camera_hal_delegate_;
+ scoped_refptr<CameraDeviceDelegate> camera_device_delegate_;
+
+ testing::StrictMock<unittest_internal::MockCameraModule> mock_camera_module_;
+
+ testing::StrictMock<MockCameraDevice> mock_camera_device_;
+ mojo::Binding<arc::mojom::Camera3DeviceOps> mock_camera_device_binding_;
+ arc::mojom::Camera3CallbackOpsPtr callback_ops_;
+
+ base::Thread device_delegate_thread_;
+
+ std::unique_ptr<VideoCaptureDevice::Client> mock_client_;
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ base::Thread hal_delegate_thread_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ DISALLOW_COPY_AND_ASSIGN(CameraDeviceDelegateTest);
+};
+
+// Test the complete capture flow: initialize, configure stream, capture one
+// frame, and close the device.
+TEST_F(CameraDeviceDelegateTest, AllocateCaptureAndStop) {
+ AllocateDeviceWithDescriptor(kDefaultDescriptor);
+
+ VideoCaptureParams params;
+ params.requested_format = kDefaultCaptureFormat;
+
+ SetUpExpectationUntilCapturing(
+ reinterpret_cast<MockVideoCaptureClient*>(mock_client_.get()));
+ SetUpExpectationForCaptureLoop();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CameraDeviceDelegate::AllocateAndStart,
+ camera_device_delegate_, params, base::Passed(&mock_client_)));
+
+ // Wait until a frame is received. |mock_client_| calls OnFrameCaptured() to
+ // stop the run loop.
+ DoLoop();
+ EXPECT_EQ(CameraDeviceContext::State::kCapturing, GetState());
+
+ SetUpExpectationForClose();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&CameraDeviceDelegate::StopAndDeAllocate,
+ camera_device_delegate_));
+
+ // Wait until |mock_client_| is deleted.
+ DoLoop();
+ EXPECT_EQ(CameraDeviceContext::State::kStopped, GetState());
+
+ ResetDevice();
+}
+
+// Test that the camera device delegate closes properly when StopAndDeAllocate
+// is called right after the device is initialized.
+TEST_F(CameraDeviceDelegateTest, StopAfterInitialized) {
+ AllocateDeviceWithDescriptor(kDefaultDescriptor);
+
+ VideoCaptureParams params;
+ params.requested_format = kDefaultCaptureFormat;
+
+ SetUpExpectationUntilInitialized();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CameraDeviceDelegate::AllocateAndStart,
+ camera_device_delegate_, params, base::Passed(&mock_client_)));
+
+ EXPECT_CALL(mock_camera_device_, DoConfigureStreams(_, _))
+ .Times(1)
+ .WillOnce(Invoke(
+ [this](arc::mojom::Camera3StreamConfigurationPtr& config,
+ base::OnceCallback<void(
+ int32_t, arc::mojom::Camera3StreamConfigurationPtr)>&
+ callback) {
+ EXPECT_EQ(CameraDeviceContext::State::kInitialized,
+ this->GetState());
+ this->QuitRunLoop();
+ std::move(callback).Run(
+ 0, arc::mojom::Camera3StreamConfiguration::New());
+ }));
+
+ // Wait until the QuitRunLoop call in |mock_camera_device_->ConfigureStreams|.
+ DoLoop();
+
+ SetUpExpectationForClose();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&CameraDeviceDelegate::StopAndDeAllocate,
+ camera_device_delegate_));
+
+ // Wait until |mock_client_| is deleted.
+ DoLoop();
+ EXPECT_EQ(CameraDeviceContext::State::kStopped, GetState());
+
+ ResetDevice();
+}
+
+// Test that the camera device delegate closes properly when StopAndDeAllocate
+// is called right after the stream is configured.
+TEST_F(CameraDeviceDelegateTest, StopAfterStreamConfigured) {
+ AllocateDeviceWithDescriptor(kDefaultDescriptor);
+
+ VideoCaptureParams params;
+ params.requested_format = kDefaultCaptureFormat;
+
+ SetUpExpectationUntilStreamConfigured();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&CameraDeviceDelegate::AllocateAndStart,
+ camera_device_delegate_, params, base::Passed(&mock_client_)));
+
+ EXPECT_CALL(mock_camera_device_, DoConstructDefaultRequestSettings(_, _))
+ .Times(1)
+ .WillOnce(
+ Invoke([this](arc::mojom::Camera3RequestTemplate type,
+ base::OnceCallback<void(arc::mojom::CameraMetadataPtr)>&
+ callback) {
+ EXPECT_EQ(CameraDeviceContext::State::kStreamConfigured,
+ this->GetState());
+ this->QuitRunLoop();
+ std::move(callback).Run(arc::mojom::CameraMetadataPtr());
+ }));
+
+ // Wait until the QuitRunLoop call in |mock_camera_device_->ConfigureStreams|.
+ DoLoop();
+
+ SetUpExpectationForClose();
+
+ device_delegate_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&CameraDeviceDelegate::StopAndDeAllocate,
+ camera_device_delegate_));
+
+ // Wait until |mock_client_| is deleted.
+ DoLoop();
+ EXPECT_EQ(CameraDeviceContext::State::kStopped, GetState());
+
+ ResetDevice();
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698