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",
]
}
// 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/fuchsia/camera/fake_fuchsia_camera.h"
#include <fuchsia/sysmem/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include "base/fuchsia/default_context.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/message_loop/message_loop_current.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
constexpr uint8_t kYPlaneSalt = 1;
constexpr uint8_t kUPlaneSalt = 2;
constexpr uint8_t kVPlaneSalt = 3;
uint8_t GetTestFrameValue(gfx::Size size, int x, int y, uint8_t salt) {
return static_cast<uint8_t>(y + x * size.height() + salt);
}
void FillPlane(uint8_t* data,
gfx::Size size,
size_t x_step,
size_t y_step,
uint8_t salt) {
for (int y = 0; y < size.height(); ++y) {
for (int x = 0; x < size.width(); ++x) {
data[x * x_step + y * y_step] = GetTestFrameValue(size, x, y, salt);
}
}
}
void ValidatePlane(const uint8_t* data,
gfx::Size size,
size_t x_step,
size_t y_step,
uint8_t salt) {
for (int y = 0; y < size.height(); ++y) {
for (int x = 0; x < size.width(); ++x) {
SCOPED_TRACE(testing::Message() << "x=" << x << " y=" << y);
EXPECT_EQ(data[x * x_step + y * y_step],
GetTestFrameValue(size, x, y, salt));
}
}
}
} // namespace
// static
const gfx::Size FakeCameraStream::kMaxFrameSize = gfx::Size(100, 60);
// static
const gfx::Size FakeCameraStream::kDefaultFrameSize = gfx::Size(60, 40);
// static
void FakeCameraStream::ValidateFrameData(const uint8_t* data,
gfx::Size size,
uint8_t salt) {
const uint8_t* y_plane = data;
{
SCOPED_TRACE("Y plane");
ValidatePlane(y_plane, size, 1, size.width(), salt + kYPlaneSalt);
}
gfx::Size uv_size(size.width() / 2, size.height() / 2);
const uint8_t* u_plane = y_plane + size.width() * size.height();
{
SCOPED_TRACE("U plane");
ValidatePlane(u_plane, uv_size, 1, uv_size.width(), salt + kUPlaneSalt);
}
const uint8_t* v_plane = u_plane + uv_size.width() * uv_size.height();
{
SCOPED_TRACE("V plane");
ValidatePlane(v_plane, uv_size, 1, uv_size.width(), salt + kVPlaneSalt);
}
}
struct FakeCameraStream::Buffer {
explicit Buffer(base::WritableSharedMemoryMapping mapping)
: mapping(std::move(mapping)),
release_fence_watch_controller(FROM_HERE) {}
base::WritableSharedMemoryMapping mapping;
// Frame is used by the client when the |release_fence| is not null.
zx::eventpair release_fence;
base::MessagePumpForIO::ZxHandleWatchController
release_fence_watch_controller;
};
FakeCameraStream::FakeCameraStream() : binding_(this) {}
FakeCameraStream::~FakeCameraStream() = default;
void FakeCameraStream::Bind(
fidl::InterfaceRequest<fuchsia::camera3::Stream> request) {
binding_.Bind(std::move(request));
}
void FakeCameraStream::SetFakeResolution(gfx::Size resolution) {
resolution_ = resolution;
resolution_update_ =
fuchsia::math::Size{resolution_.width(), resolution_.height()};
SendResolution();
}
bool FakeCameraStream::WaitBuffersAllocated() {
EXPECT_FALSE(wait_buffers_allocated_run_loop_);
if (!buffers_.empty())
return true;
wait_buffers_allocated_run_loop_.emplace();
wait_buffers_allocated_run_loop_->Run();
wait_buffers_allocated_run_loop_.reset();
return !buffers_.empty();
}
bool FakeCameraStream::WaitFreeBuffer() {
EXPECT_FALSE(wait_free_buffer_run_loop_);
if (num_used_buffers_ < buffers_.size())
return true;
wait_free_buffer_run_loop_.emplace();
wait_free_buffer_run_loop_->Run();
wait_free_buffer_run_loop_.reset();
return num_used_buffers_ < buffers_.size();
}
void FakeCameraStream::ProduceFrame(base::TimeTicks timestamp, uint8_t salt) {
ASSERT_LT(num_used_buffers_, buffers_.size());
ASSERT_FALSE(next_frame_);
size_t index = buffers_.size();
for (size_t i = 0; i < buffers_.size(); ++i) {
if (!buffers_[i]->release_fence) {
index = i;
break;
}
}
EXPECT_LT(index, buffers_.size());
auto* buffer = buffers_[index].get();
gfx::Size coded_size((resolution_.width() + 1) & ~1,
(resolution_.height() + 1) & ~1);
// Fill Y plane.
uint8_t* y_plane = reinterpret_cast<uint8_t*>(buffer->mapping.memory());
size_t stride = kMaxFrameSize.width();
FillPlane(y_plane, coded_size, /*x_step=*/1, /*y_step=*/stride,
salt + kYPlaneSalt);
// Fill UV plane.
gfx::Size uv_size(coded_size.width() / 2, coded_size.height() / 2);
uint8_t* uv_plane = y_plane + kMaxFrameSize.width() * kMaxFrameSize.height();
FillPlane(uv_plane, uv_size, /*x_step=*/2, /*y_step=*/stride,
salt + kUPlaneSalt);
FillPlane(uv_plane + 1, uv_size, /*x_step=*/2, /*y_step=*/stride,
salt + kVPlaneSalt);
// Create FrameInfo.
fuchsia::camera3::FrameInfo frame;
frame.frame_counter = frame_counter_++;
frame.buffer_index = 0;
frame.timestamp = timestamp.ToZxTime();
EXPECT_EQ(
zx::eventpair::create(0u, &frame.release_fence, &buffer->release_fence),
ZX_OK);
// Watch release fence to get notified when the frame is released.
base::MessageLoopCurrentForIO::Get()->WatchZxHandle(
buffer->release_fence.get(), /*persistent=*/false,
ZX_EVENTPAIR_PEER_CLOSED, &buffer->release_fence_watch_controller, this);
num_used_buffers_++;
next_frame_ = std::move(frame);
SendNextFrame();
}
void FakeCameraStream::WatchResolution(WatchResolutionCallback callback) {
EXPECT_FALSE(watch_resolution_callback_);
watch_resolution_callback_ = std::move(callback);
SendResolution();
}
void FakeCameraStream::SetBufferCollection(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
token_handle) {
EXPECT_TRUE(token_handle);
// Drop old buffers.
buffers_.clear();
if (buffer_collection_) {
buffer_collection_->Close();
buffer_collection_.Unbind();
}
// Use a SyncPtr to be able to wait for Sync() synchronously.
fuchsia::sysmem::BufferCollectionTokenSyncPtr token;
token.Bind(std::move(token_handle));
// Duplicate the token to access from the stream.
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> local_token;
zx_status_t status =
token->Duplicate(/*rights_attenuation_mask=*/0, local_token.NewRequest());
EXPECT_EQ(status, ZX_OK);
status = token->Sync();
EXPECT_EQ(status, ZX_OK);
// Return the token back to the client.
new_buffer_collection_token_ = token.Unbind();
SendBufferCollection();
// Initialize the new collection using |local_token|.
auto allocator = base::fuchsia::ComponentContextForCurrentProcess()
->svc()
->Connect<fuchsia::sysmem::Allocator>();
allocator->BindSharedCollection(std::move(local_token),
buffer_collection_.NewRequest());
EXPECT_EQ(status, ZX_OK);
buffer_collection_.set_error_handler(
fit::bind_member(this, &FakeCameraStream::OnBufferCollectionError));
fuchsia::sysmem::BufferCollectionConstraints constraints;
constraints.usage.cpu =
fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
// The client is expected to request buffers it may need. We don't need to
// reserve any for the server side.
constraints.min_buffer_count_for_camping = 0;
// Initialize image format.
constraints.image_format_constraints_count = 1;
constraints.image_format_constraints[0].pixel_format.type =
fuchsia::sysmem::PixelFormatType::NV12;
constraints.image_format_constraints[0].color_spaces_count = 1;
constraints.image_format_constraints[0].color_space[0].type =
fuchsia::sysmem::ColorSpaceType::REC601_NTSC;
constraints.image_format_constraints[0].required_max_coded_width =
kMaxFrameSize.width();
constraints.image_format_constraints[0].required_max_coded_height =
kMaxFrameSize.height();
buffer_collection_->SetConstraints(/*has_constraints=*/true,
std::move(constraints));
buffer_collection_->WaitForBuffersAllocated(
fit::bind_member(this, &FakeCameraStream::OnBufferCollectionAllocated));
}
void FakeCameraStream::WatchBufferCollection(
WatchBufferCollectionCallback callback) {
EXPECT_FALSE(watch_buffer_collection_callback_);
watch_buffer_collection_callback_ = std::move(callback);
SendBufferCollection();
}
void FakeCameraStream::GetNextFrame(GetNextFrameCallback callback) {
EXPECT_FALSE(get_next_frame_callback_);
get_next_frame_callback_ = std::move(callback);
SendNextFrame();
}
void FakeCameraStream::NotImplemented_(const std::string& name) {
ADD_FAILURE() << "NotImplemented_: " << name;
}
void FakeCameraStream::OnBufferCollectionError(zx_status_t status) {
ADD_FAILURE() << "BufferCollection failed.";
if (wait_buffers_allocated_run_loop_)
wait_buffers_allocated_run_loop_->Quit();
if (wait_free_buffer_run_loop_)
wait_free_buffer_run_loop_->Quit();
}
void FakeCameraStream::OnBufferCollectionAllocated(
zx_status_t status,
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
if (status != ZX_OK) {
OnBufferCollectionError(status);
return;
}
EXPECT_TRUE(buffers_.empty());
EXPECT_TRUE(buffer_collection_info.settings.has_image_format_constraints);
EXPECT_EQ(buffer_collection_info.settings.image_format_constraints
.pixel_format.type,
fuchsia::sysmem::PixelFormatType::NV12);
size_t buffer_size =
buffer_collection_info.settings.buffer_settings.size_bytes;
for (size_t i = 0; i < buffer_collection_info.buffer_count; ++i) {
auto& buffer = buffer_collection_info.buffers[i];
EXPECT_EQ(buffer.vmo_usable_start, 0U);
auto region = base::WritableSharedMemoryRegion::Deserialize(
base::subtle::PlatformSharedMemoryRegion::Take(
std::move(buffer.vmo),
base::subtle::PlatformSharedMemoryRegion::Mode::kWritable,
buffer_size, base::UnguessableToken::Create()));
auto mapping = region.Map();
EXPECT_TRUE(mapping.IsValid());
buffers_.push_back(std::make_unique<Buffer>(std::move(mapping)));
}
if (wait_buffers_allocated_run_loop_)
wait_buffers_allocated_run_loop_->Quit();
}
void FakeCameraStream::SendResolution() {
if (!watch_resolution_callback_ || !resolution_update_)
return;
watch_resolution_callback_(resolution_update_.value());
watch_resolution_callback_ = {};
resolution_update_.reset();
}
void FakeCameraStream::SendBufferCollection() {
if (!watch_buffer_collection_callback_ || !new_buffer_collection_token_)
return;
watch_buffer_collection_callback_(
std::move(new_buffer_collection_token_.value()));
watch_buffer_collection_callback_ = {};
new_buffer_collection_token_.reset();
}
void FakeCameraStream::SendNextFrame() {
if (!get_next_frame_callback_ || !next_frame_)
return;
get_next_frame_callback_(std::move(next_frame_.value()));
get_next_frame_callback_ = {};
next_frame_.reset();
}
void FakeCameraStream::OnZxHandleSignalled(zx_handle_t handle,
zx_signals_t signals) {
EXPECT_EQ(signals, ZX_EVENTPAIR_PEER_CLOSED);
// Find the buffer that corresponds to the |handle|.
size_t index = buffers_.size();
for (size_t i = 0; i < buffers_.size(); ++i) {
if (buffers_[i]->release_fence.get() == handle) {
index = i;
break;
}
}
ASSERT_LT(index, buffers_.size());
buffers_[index]->release_fence = {};
buffers_[index]->release_fence_watch_controller.StopWatchingZxHandle();
num_used_buffers_--;
if (wait_free_buffer_run_loop_)
wait_free_buffer_run_loop_->Quit();
}
FakeCameraDevice::FakeCameraDevice(FakeCameraStream* stream)
: binding_(this), stream_(stream) {}
FakeCameraDevice::~FakeCameraDevice() = default;
void FakeCameraDevice::Bind(
fidl::InterfaceRequest<fuchsia::camera3::Device> request) {
binding_.Bind(std::move(request));
}
void FakeCameraDevice::GetIdentifier(GetIdentifierCallback callback) {
callback("Fake Camera");
}
void FakeCameraDevice::GetConfigurations(GetConfigurationsCallback callback) {
std::vector<fuchsia::camera3::Configuration> configurations(1);
configurations[0].streams.resize(1);
configurations[0].streams[0].frame_rate.numerator = 30;
configurations[0].streams[0].frame_rate.denominator = 1;
configurations[0].streams[0].image_format.pixel_format.type =
fuchsia::sysmem::PixelFormatType::NV12;
configurations[0].streams[0].image_format.coded_width = 640;
configurations[0].streams[0].image_format.coded_height = 480;
configurations[0].streams[0].image_format.bytes_per_row = 640;
callback(std::move(configurations));
}
void FakeCameraDevice::ConnectToStream(
uint32_t index,
fidl::InterfaceRequest<fuchsia::camera3::Stream> request) {
EXPECT_EQ(index, 0U);
stream_->Bind(std::move(request));
}
void FakeCameraDevice::NotImplemented_(const std::string& name) {
ADD_FAILURE() << "NotImplemented_: " << name;
}
} // namespace media
\ No newline at end of file
// 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