Commit 4186c2fc authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

Reland "[Fuchsia] Add unittests for VideoCaptureDeviceFuchsia"

This is a reland of 02ee5e26

Original change's description:
> [Fuchsia] Add unittests for VideoCaptureDeviceFuchsia
>
> Added unittests of for VideoCaptureDeviceFuchsia with fake
> fuchsia.camera3.Device and fuchsia.camera3.Stream implementations.
>
> In the future the fakes can be used for web_engine_integration_tests.
>
> Bug: 866669
> Change-Id: I0b5b0267127f9843b12123eb9e9a61890ec95a25
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2181184
> Reviewed-by: Emircan Uysaler <emircan@chromium.org>
> Reviewed-by: Kevin Marshall <kmarshall@chromium.org>
> Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#768422}

TBR=kmarshall@chromium.org

Bug: 866669
Change-Id: I6c68aee7c50b8e3dc93744446042920c79c37db9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2200000Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#768524}
parent 629101a4
...@@ -393,6 +393,16 @@ test("capture_unittests") { ...@@ -393,6 +393,16 @@ test("capture_unittests") {
] ]
} }
if (is_fuchsia) {
deps += [
"//media/fuchsia/camera:test_support",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
"//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
]
sources += [ "video/fuchsia/video_capture_device_fuchsia_test.cc" ]
}
if (is_win) { if (is_win) {
sources += [ sources += [
"video/win/video_capture_device_factory_win_unittest.cc", "video/win/video_capture_device_factory_win_unittest.cc",
......
// Copyright 2020 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/fuchsia/video_capture_device_fuchsia.h"
#include "base/test/task_environment.h"
#include "media/fuchsia/camera/fake_fuchsia_camera.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace media {
namespace {
struct ReceivedFrame {
VideoCaptureDevice::Client::Buffer buffer;
VideoCaptureFormat format;
gfx::ColorSpace color_space;
base::TimeTicks reference_time;
base::TimeDelta timestamp;
gfx::Rect visible_rect;
};
void ValidateReceivedFrame(const ReceivedFrame& frame,
gfx::Size expected_size,
uint8_t salt) {
gfx::Size coded_size((expected_size.width() + 1) & ~1,
(expected_size.height() + 1) & ~1);
ASSERT_EQ(frame.format.frame_size, coded_size);
EXPECT_EQ(frame.format.pixel_format, PIXEL_FORMAT_I420);
EXPECT_EQ(frame.visible_rect, gfx::Rect(expected_size));
EXPECT_EQ(frame.color_space, gfx::ColorSpace());
auto handle = frame.buffer.handle_provider->GetHandleForInProcessAccess();
FakeCameraStream::ValidateFrameData(handle->data(), coded_size, salt);
}
// VideoCaptureBufferHandle implementation that references memory allocated on
// the heap.
class HeapBufferHandle : public VideoCaptureBufferHandle {
public:
HeapBufferHandle(size_t size, uint8_t* data) : size_(size), data_(data) {}
size_t mapped_size() const final { return size_; }
uint8_t* data() const final { return data_; }
const uint8_t* const_data() const final { return data_; }
private:
const size_t size_;
uint8_t* const data_;
};
// VideoCaptureDevice::Client::Buffer::HandleProvider implementation that
// allocates memory on the heap.
class HeapBufferHandleProvider
: public VideoCaptureDevice::Client::Buffer::HandleProvider {
public:
HeapBufferHandleProvider(size_t size) : data_(size) {}
~HeapBufferHandleProvider() final = default;
base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() final {
NOTREACHED();
return {};
}
mojo::ScopedSharedBufferHandle DuplicateAsMojoBuffer() final {
NOTREACHED();
return {};
}
std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
override {
return std::make_unique<HeapBufferHandle>(data_.size(), data_.data());
}
gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override {
return gfx::GpuMemoryBufferHandle();
}
private:
std::vector<uint8_t> data_;
};
class TestVideoCaptureClient : public VideoCaptureDevice::Client {
public:
~TestVideoCaptureClient() final = default;
void WaitFrame() {
EXPECT_FALSE(wait_frame_run_loop_);
wait_frame_run_loop_.emplace();
wait_frame_run_loop_->Run();
wait_frame_run_loop_.reset();
}
const std::vector<ReceivedFrame>& received_frames() {
return received_frames_;
}
private:
// VideoCaptureDevice::Client implementation.
void OnStarted() final {
EXPECT_FALSE(started_);
started_ = true;
}
ReserveResult ReserveOutputBuffer(const gfx::Size& dimensions,
VideoPixelFormat format,
int frame_feedback_id,
Buffer* buffer) final {
EXPECT_TRUE(started_);
EXPECT_EQ(format, PIXEL_FORMAT_I420);
EXPECT_EQ(dimensions.width() % 2, 0);
EXPECT_EQ(dimensions.height() % 2, 0);
size_t size = dimensions.width() * dimensions.height() * 3 / 2;
buffer->handle_provider = std::make_unique<HeapBufferHandleProvider>(size);
return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
}
void OnIncomingCapturedBufferExt(
Buffer buffer,
const VideoCaptureFormat& format,
const gfx::ColorSpace& color_space,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
gfx::Rect visible_rect,
const VideoFrameMetadata& additional_metadata) final {
EXPECT_TRUE(started_);
received_frames_.push_back(ReceivedFrame{std::move(buffer), format,
color_space, reference_time,
timestamp, visible_rect});
if (wait_frame_run_loop_)
wait_frame_run_loop_->Quit();
}
void OnIncomingCapturedData(const uint8_t* data,
int length,
const VideoCaptureFormat& frame_format,
const gfx::ColorSpace& color_space,
int clockwise_rotation,
bool flip_y,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id) final {
NOTREACHED();
}
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id) final {
NOTREACHED();
}
void OnIncomingCapturedBuffer(Buffer buffer,
const VideoCaptureFormat& format,
base::TimeTicks reference_time,
base::TimeDelta timestamp) final {
NOTREACHED();
}
void OnError(VideoCaptureError error,
const base::Location& from_here,
const std::string& reason) final {
NOTREACHED();
}
void OnFrameDropped(VideoCaptureFrameDropReason reason) final {
NOTREACHED();
}
void OnLog(const std::string& message) final { NOTREACHED(); }
double GetBufferPoolUtilization() const final {
NOTREACHED();
return 0;
}
bool started_ = false;
std::vector<ReceivedFrame> received_frames_;
base::Optional<base::RunLoop> wait_frame_run_loop_;
};
} // namespace
class VideoCaptureDeviceFuchsiaTest : public testing::Test {
public:
VideoCaptureDeviceFuchsiaTest() {
fidl::InterfaceHandle<fuchsia::camera3::Device> device_handle;
fake_device_.Bind(device_handle.NewRequest());
device_ =
std::make_unique<VideoCaptureDeviceFuchsia>(std::move(device_handle));
}
~VideoCaptureDeviceFuchsiaTest() override { device_->StopAndDeAllocate(); }
void StartCapturer() {
VideoCaptureParams params;
params.requested_format.frame_size = FakeCameraStream::kDefaultFrameSize;
params.requested_format.frame_rate = 30.0;
params.requested_format.pixel_format = PIXEL_FORMAT_I420;
auto client = std::make_unique<TestVideoCaptureClient>();
client_ = client.get();
device_->AllocateAndStart(params, std::move(client));
EXPECT_TRUE(fake_stream_.WaitBuffersAllocated());
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
FakeCameraStream fake_stream_;
FakeCameraDevice fake_device_{&fake_stream_};
std::unique_ptr<VideoCaptureDeviceFuchsia> device_;
TestVideoCaptureClient* client_ = nullptr;
};
TEST_F(VideoCaptureDeviceFuchsiaTest, Initialize) {
StartCapturer();
}
TEST_F(VideoCaptureDeviceFuchsiaTest, SendFrame) {
StartCapturer();
auto frame_timestamp = base::TimeTicks::Now();
fake_stream_.ProduceFrame(frame_timestamp, 1);
client_->WaitFrame();
ASSERT_EQ(client_->received_frames().size(), 1U);
EXPECT_EQ(client_->received_frames()[0].reference_time, frame_timestamp);
ValidateReceivedFrame(client_->received_frames()[0],
FakeCameraStream::kDefaultFrameSize, 1);
}
TEST_F(VideoCaptureDeviceFuchsiaTest, MultipleFrames) {
StartCapturer();
EXPECT_TRUE(fake_stream_.WaitBuffersAllocated());
for (size_t i = 0; i < 10; ++i) {
ASSERT_TRUE(fake_stream_.WaitFreeBuffer());
auto frame_timestamp =
base::TimeTicks() + base::TimeDelta::FromMilliseconds(i * 16);
fake_stream_.ProduceFrame(frame_timestamp, i);
client_->WaitFrame();
ASSERT_EQ(client_->received_frames().size(), i + 1);
EXPECT_EQ(client_->received_frames()[i].reference_time, frame_timestamp);
ValidateReceivedFrame(client_->received_frames()[i],
FakeCameraStream::kDefaultFrameSize, i);
}
}
TEST_F(VideoCaptureDeviceFuchsiaTest, FrameDimensionsNotDivisibleBy2) {
const gfx::Size kOddResolution(21, 7);
fake_stream_.SetFakeResolution(kOddResolution);
StartCapturer();
fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
client_->WaitFrame();
ASSERT_EQ(client_->received_frames().size(), 1U);
ValidateReceivedFrame(client_->received_frames()[0], kOddResolution, 1);
}
TEST_F(VideoCaptureDeviceFuchsiaTest, MidStreamResolutionChange) {
StartCapturer();
// Capture the first frame at the default resolution.
fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
client_->WaitFrame();
ASSERT_TRUE(fake_stream_.WaitFreeBuffer());
// Update resolution and produce another frames.
const gfx::Size kUpdatedResolution(3, 14);
fake_stream_.SetFakeResolution(kUpdatedResolution);
fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
client_->WaitFrame();
// Verify that we get captured frames with correct resolution.
ASSERT_EQ(client_->received_frames().size(), 2U);
ValidateReceivedFrame(client_->received_frames()[0],
FakeCameraStream::kDefaultFrameSize, 1);
ValidateReceivedFrame(client_->received_frames()[1], kUpdatedResolution, 1);
}
} // namespace media
# Copyright 2020 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.
source_set("test_support") {
testonly = true
public_deps = [
"//base",
"//testing/gtest",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
"//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
"//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp",
"//ui/gfx/geometry",
]
sources = [
"fake_fuchsia_camera.cc",
"fake_fuchsia_camera.h",
]
}
This diff is collapsed.
// Copyright 2020 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 MEDIA_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
#define MEDIA_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
#include <fuchsia/camera3/cpp/fidl.h>
#include <fuchsia/camera3/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h>
#include <vector>
#include "base/message_loop/message_pump_for_io.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class FakeCameraStream : public fuchsia::camera3::testing::Stream_TestBase,
public base::MessagePumpForIO::ZxHandleWatcher {
public:
static const gfx::Size kMaxFrameSize;
static const gfx::Size kDefaultFrameSize;
// Verifies that the I420 image stored at |data| matches the frame produced
// by ProduceFrame().
static void ValidateFrameData(const uint8_t* data,
gfx::Size size,
uint8_t salt);
FakeCameraStream();
~FakeCameraStream() final;
FakeCameraStream(const FakeCameraStream&) = delete;
FakeCameraStream& operator=(const FakeCameraStream&) = delete;
void Bind(fidl::InterfaceRequest<fuchsia::camera3::Stream> request);
void SetFakeResolution(gfx::Size resolution);
// Waits for the buffer collection to be allocated. Returns true if the buffer
// collection was allocated successfully.
bool WaitBuffersAllocated();
// Waits until there is at least one free buffer that can be used for the next
// frame.
bool WaitFreeBuffer();
void ProduceFrame(base::TimeTicks timestamp, uint8_t salt);
private:
struct Buffer;
// fuchsia::camera3::Stream implementation.
void WatchResolution(WatchResolutionCallback callback) final;
void SetBufferCollection(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
token_handle) final;
void WatchBufferCollection(WatchBufferCollectionCallback callback) final;
void GetNextFrame(GetNextFrameCallback callback) final;
// fuchsia::camera3::testing::Stream_TestBase override.
void NotImplemented_(const std::string& name) override;
void OnBufferCollectionError(zx_status_t status);
void OnBufferCollectionAllocated(
zx_status_t status,
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
// Calls callback for the pending WatchResolution() if the call is pending and
// resolution has been updated.
void SendResolution();
// Calls callback for the pending WatchBufferCollection() if we have a new
// token and the call is pending.
void SendBufferCollection();
// Calls callback for the pending GetNextFrame() if we have a new frame and
// the call is pending.
void SendNextFrame();
// ZxHandleWatcher interface. Used to wait for frame release_fences to get
// notified when the client releases a buffer.
void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) final;
fidl::Binding<fuchsia::camera3::Stream> binding_;
gfx::Size resolution_ = kDefaultFrameSize;
base::Optional<fuchsia::math::Size> resolution_update_ = fuchsia::math::Size{
kDefaultFrameSize.width(), kDefaultFrameSize.height()};
WatchResolutionCallback watch_resolution_callback_;
base::Optional<fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>>
new_buffer_collection_token_;
WatchBufferCollectionCallback watch_buffer_collection_callback_;
base::Optional<fuchsia::camera3::FrameInfo> next_frame_;
GetNextFrameCallback get_next_frame_callback_;
fuchsia::sysmem::BufferCollectionPtr buffer_collection_;
base::Optional<base::RunLoop> wait_buffers_allocated_run_loop_;
base::Optional<base::RunLoop> wait_free_buffer_run_loop_;
std::vector<std::unique_ptr<Buffer>> buffers_;
size_t num_used_buffers_ = 0;
size_t frame_counter_ = 0;
};
class FakeCameraDevice : public fuchsia::camera3::testing::Device_TestBase {
public:
explicit FakeCameraDevice(FakeCameraStream* stream);
~FakeCameraDevice() final;
FakeCameraDevice(const FakeCameraDevice&) = delete;
FakeCameraDevice& operator=(const FakeCameraDevice&) = delete;
void Bind(fidl::InterfaceRequest<fuchsia::camera3::Device> request);
private:
// fuchsia::camera3::Device implementation.
void GetIdentifier(GetIdentifierCallback callback) final;
void GetConfigurations(GetConfigurationsCallback callback) final;
void ConnectToStream(
uint32_t index,
fidl::InterfaceRequest<fuchsia::camera3::Stream> request) final;
// fuchsia::camera3::testing::Device_TestBase override.
void NotImplemented_(const std::string& name) override;
fidl::Binding<fuchsia::camera3::Device> binding_;
FakeCameraStream* const stream_;
};
} // namespace media
#endif // MEDIA_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment