Commit 290f3130 authored by Ricky Liang's avatar Ricky Liang Committed by Commit Bot

media: pass aligned captured buffer correctly on CrOS VCD

The Chrome OS HAL VideoCaptureDevice receives the captured data through
DMAbuf.  On certain platforms the DMAbuf are allocated under some
alignment constraints, e.g. on Intel platform the buffer plane width is
aligned to 64.  Hence when mapped to CPU the memory planes are not
contiguous so directly calling libyuv::ConverToI420() with the buffer
address and size does not work.

BUG=b:78259838
TEST=Make sure camera preview with resolution 2592x1944 displays
     correctly on DUT.

Change-Id: I85cf4fadf24a9374ee837e820dbd89881d14f475
Reviewed-on: https://chromium-review.googlesource.com/1029935
Commit-Queue: Ricky Liang <jcliang@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Reviewed-by: default avatarChristian Fremerey <chfremer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555386}
parent c3ccd16b
......@@ -51,6 +51,13 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client {
base::TimeTicks reference_time,
base::TimeDelta tiemstamp,
int frame_feedback_id));
MOCK_METHOD6(OnIncomingCapturedGfxBuffer,
void(gfx::GpuMemoryBuffer* buffer,
const media::VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id));
MOCK_METHOD0(DoReserveOutputBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
......
......@@ -73,6 +73,13 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client {
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id));
MOCK_METHOD6(OnIncomingCapturedGfxBuffer,
void(gfx::GpuMemoryBuffer* buffer,
const media::VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id));
MOCK_METHOD0(DoReserveOutputBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
......
......@@ -25,6 +25,13 @@ class MockDeviceClient : public media::VideoCaptureDevice::Client {
base::TimeTicks reference_time,
base::TimeDelta tiemstamp,
int frame_feedback_id));
MOCK_METHOD6(OnIncomingCapturedGfxBuffer,
void(gfx::GpuMemoryBuffer* buffer,
const media::VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id));
MOCK_METHOD0(DoReserveOutputBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
......
......@@ -224,16 +224,27 @@ component("capture_lib") {
if (is_chromeos) {
sources += [
"video/chromeos/camera_buffer_factory.cc",
"video/chromeos/camera_buffer_factory.h",
"video/chromeos/camera_device_context.cc",
"video/chromeos/camera_device_context.h",
"video/chromeos/camera_device_delegate.cc",
"video/chromeos/camera_device_delegate.h",
"video/chromeos/camera_hal_delegate.cc",
"video/chromeos/camera_hal_delegate.h",
"video/chromeos/camera_hal_dispatcher_impl.cc",
"video/chromeos/camera_hal_dispatcher_impl.h",
"video/chromeos/camera_metadata_utils.cc",
"video/chromeos/camera_metadata_utils.h",
"video/chromeos/display_rotation_observer.cc",
"video/chromeos/display_rotation_observer.h",
"video/chromeos/pixel_format_utils.cc",
"video/chromeos/pixel_format_utils.h",
"video/chromeos/stream_buffer_manager.cc",
"video/chromeos/stream_buffer_manager.h",
"video/chromeos/video_capture_device_chromeos_halv3.cc",
"video/chromeos/video_capture_device_chromeos_halv3.h",
"video/chromeos/video_capture_device_factory_chromeos.cc",
"video/chromeos/video_capture_device_factory_chromeos.h",
]
deps += [
"//chromeos:chromeos",
......@@ -275,6 +286,8 @@ test("capture_unittests") {
"video/linux/camera_config_chromeos_unittest.cc",
"video/linux/v4l2_capture_delegate_unittest.cc",
"video/mac/video_capture_device_factory_mac_unittest.mm",
"video/mock_gpu_memory_buffer_manager.cc",
"video/mock_gpu_memory_buffer_manager.h",
"video/shared_memory_handle_provider_unittest.cc",
"video/video_capture_device_client_unittest.cc",
"video/video_capture_device_unittest.cc",
......@@ -289,6 +302,7 @@ test("capture_unittests") {
":capture",
":test_support",
"//base/test:test_support",
"//gpu/command_buffer/client",
"//media:test_support",
"//media/capture/mojom:image_capture",
"//media/capture/mojom:image_capture_types",
......@@ -333,8 +347,9 @@ test("capture_unittests") {
"video/chromeos/camera_hal_dispatcher_impl_unittest.cc",
"video/chromeos/local_gpu_memory_buffer_manager.cc",
"video/chromeos/mock_camera_module.cc",
"video/chromeos/mock_gpu_memory_buffer_manager.cc",
"video/chromeos/mock_camera_module.h",
"video/chromeos/mock_video_capture_client.cc",
"video/chromeos/mock_video_capture_client.h",
"video/chromeos/stream_buffer_manager_unittest.cc",
]
deps += [
......
......@@ -43,14 +43,13 @@ void CameraDeviceContext::LogToClient(std::string message) {
}
void CameraDeviceContext::SubmitCapturedData(
const uint8_t* data,
int length,
gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
base::TimeTicks reference_time,
base::TimeDelta timestamp) {
int total_rotation = (sensor_orientation_ + screen_rotation_) % 360;
client_->OnIncomingCapturedData(data, length, frame_format, total_rotation,
reference_time, timestamp);
client_->OnIncomingCapturedGfxBuffer(buffer, frame_format, total_rotation,
reference_time, timestamp);
}
void CameraDeviceContext::SetSensorOrientation(int sensor_orientation) {
......
......@@ -14,6 +14,9 @@
namespace media {
// A class storing the context of a running CameraDeviceDelegate.
//
// The class is also used to forward/translate events and method calls to a
// given VideoCaptureDevice::Client.
class CAPTURE_EXPORT CameraDeviceContext {
public:
// The internal state of the running CameraDeviceDelegate. The state
......@@ -106,8 +109,7 @@ class CAPTURE_EXPORT CameraDeviceContext {
void LogToClient(std::string message);
// Submits the capture data to |client_->OnIncomingCapturedData|.
void SubmitCapturedData(const uint8_t* data,
int length,
void SubmitCapturedData(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
base::TimeTicks reference_time,
base::TimeDelta timestamp);
......
......@@ -17,9 +17,9 @@
#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/mock_gpu_memory_buffer_manager.h"
#include "media/capture/video/chromeos/mock_video_capture_client.h"
#include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
#include "media/capture/video/mock_gpu_memory_buffer_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -289,18 +289,16 @@ class CameraDeviceDelegateTest : public ::testing::Test {
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gpu::kNullSurfaceHandle))
.Times(1)
.WillOnce(Invoke(
&mock_gpu_memory_buffer_manager_,
&unittest_internal::MockGpuMemoryBufferManager::ReturnValidBuffer));
.WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
CreateFakeGpuMemoryBuffer));
EXPECT_CALL(mock_gpu_memory_buffer_manager_,
CreateGpuMemoryBuffer(
gfx::Size(1280, 720), gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gpu::kNullSurfaceHandle))
.Times(1)
.WillOnce(Invoke(
&mock_gpu_memory_buffer_manager_,
&unittest_internal::MockGpuMemoryBufferManager::ReturnValidBuffer));
.WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
CreateFakeGpuMemoryBuffer));
}
void SetUpExpectationUntilCapturing(
......
......@@ -13,8 +13,8 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "media/capture/video/chromeos/mock_camera_module.h"
#include "media/capture/video/chromeos/mock_gpu_memory_buffer_manager.h"
#include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
#include "media/capture/video/mock_gpu_memory_buffer_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -153,9 +153,8 @@ TEST_F(CameraHalDelegateTest, GetBuiltinCameraInfo) {
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gpu::kNullSurfaceHandle))
.Times(1)
.WillOnce(Invoke(
&mock_gpu_memory_buffer_manager_,
&unittest_internal::MockGpuMemoryBufferManager::ReturnValidBuffer));
.WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
CreateFakeGpuMemoryBuffer));
VideoCaptureFormats supported_formats;
camera_hal_delegate_->GetSupportedFormats(descriptors[0], &supported_formats);
......
// 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/mock_gpu_memory_buffer_manager.h"
#include <memory>
using ::testing::Return;
namespace media {
namespace unittest_internal {
MockGpuMemoryBuffer::MockGpuMemoryBuffer() = default;
MockGpuMemoryBuffer::~MockGpuMemoryBuffer() = default;
MockGpuMemoryBufferManager::MockGpuMemoryBufferManager() = default;
MockGpuMemoryBufferManager::~MockGpuMemoryBufferManager() = default;
std::unique_ptr<gfx::GpuMemoryBuffer>
MockGpuMemoryBufferManager::ReturnValidBuffer(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gpu::SurfaceHandle surface_handle) {
// We use only NV12 in unit tests.
EXPECT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, format);
gfx::GpuMemoryBufferHandle handle;
handle.type = gfx::NATIVE_PIXMAP;
// Set a dummy id since this is for testing only.
handle.id = gfx::GpuMemoryBufferId(0);
// Set a dummy fd since this is for testing only.
handle.native_pixmap_handle.fds.push_back(base::FileDescriptor(0, false));
handle.native_pixmap_handle.planes.push_back(
gfx::NativePixmapPlane(size.width(), 0, size.width() * size.height()));
handle.native_pixmap_handle.planes.push_back(gfx::NativePixmapPlane(
size.width(), handle.native_pixmap_handle.planes[0].size,
size.width() * size.height() / 2));
auto mock_buffer = std::make_unique<MockGpuMemoryBuffer>();
ON_CALL(*mock_buffer, Map()).WillByDefault(Return(true));
ON_CALL(*mock_buffer, memory(0))
.WillByDefault(Return(reinterpret_cast<void*>(0xdeafbeef)));
ON_CALL(*mock_buffer, GetHandle()).WillByDefault(Return(handle));
return mock_buffer;
}
} // namespace unittest_internal
} // namespace media
......@@ -52,6 +52,20 @@ void MockVideoCaptureClient::OnIncomingCapturedData(
}
}
void MockVideoCaptureClient::OnIncomingCapturedGfxBuffer(
gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id) {
ASSERT_TRUE(buffer);
ASSERT_GT(buffer->GetSize().width() * buffer->GetSize().height(), 0);
if (frame_cb_) {
std::move(frame_cb_).Run();
}
}
// Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
VideoCaptureDevice::Client::Buffer MockVideoCaptureClient::ReserveOutputBuffer(
const gfx::Size& dimensions,
......
......@@ -8,6 +8,9 @@
#include "media/capture/video/video_capture_device.h"
#include "testing/gmock/include/gmock/gmock.h"
// TODO(crbug.com/838774):
// Consolidate the MockVideoCaptureClient implementations
namespace media {
namespace unittest_internal {
......@@ -40,6 +43,12 @@ class MockVideoCaptureClient : public VideoCaptureDevice::Client {
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id) override;
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override;
// Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
VideoPixelFormat format,
......
......@@ -483,17 +483,9 @@ void StreamBufferManager::SubmitCaptureResult(uint32_t frame_number) {
if (partial_result.buffer->status !=
cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) {
gfx::GpuMemoryBuffer* buffer = stream_context_->buffers[buffer_id].get();
auto buffer_handle = buffer->GetHandle();
size_t mapped_size = 0;
for (const auto& plane : buffer_handle.native_pixmap_handle.planes) {
mapped_size += plane.size;
}
// We are relying on the GpuMemoryBuffer being mapped contiguously on the
// virtual memory address space.
device_context_->SubmitCapturedData(
reinterpret_cast<uint8_t*>(buffer->memory(0)), mapped_size,
stream_context_->capture_format, partial_result.reference_time,
partial_result.timestamp);
device_context_->SubmitCapturedData(buffer, stream_context_->capture_format,
partial_result.reference_time,
partial_result.timestamp);
}
stream_context_->free_buffers.push(buffer_id);
partial_results_.erase(frame_number);
......
......@@ -16,8 +16,8 @@
#include "media/capture/video/chromeos/camera_buffer_factory.h"
#include "media/capture/video/chromeos/camera_device_context.h"
#include "media/capture/video/chromeos/camera_device_delegate.h"
#include "media/capture/video/chromeos/mock_gpu_memory_buffer_manager.h"
#include "media/capture/video/chromeos/mock_video_capture_client.h"
#include "media/capture/video/mock_gpu_memory_buffer_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -68,45 +68,31 @@ const VideoCaptureFormat kDefaultCaptureFormat(gfx::Size(1280, 720),
30.0,
PIXEL_FORMAT_NV12);
class MockCameraBufferFactory : public CameraBufferFactory {
class FakeCameraBufferFactory : public CameraBufferFactory {
public:
MOCK_METHOD2(CreateGpuMemoryBuffer,
std::unique_ptr<gfx::GpuMemoryBuffer>(const gfx::Size& size,
gfx::BufferFormat format));
MOCK_METHOD1(ResolveStreamBufferFormat,
ChromiumPixelFormat(cros::mojom::HalPixelFormat hal_format));
};
FakeCameraBufferFactory() {
gpu_memory_buffer_manager_ =
std::make_unique<unittest_internal::MockGpuMemoryBufferManager>();
}
std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format) override {
return unittest_internal::MockGpuMemoryBufferManager::
CreateFakeGpuMemoryBuffer(size, format,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gpu::kNullSurfaceHandle);
}
std::unique_ptr<gfx::GpuMemoryBuffer> CreateMockGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format) {
auto mock_buffer = std::make_unique<unittest_internal::MockGpuMemoryBuffer>();
gfx::GpuMemoryBufferHandle fake_handle;
fake_handle.native_pixmap_handle.fds.push_back(
base::FileDescriptor(0, false));
fake_handle.native_pixmap_handle.planes.push_back(
gfx::NativePixmapPlane(1280, 0, 1280 * 720));
fake_handle.native_pixmap_handle.planes.push_back(
gfx::NativePixmapPlane(1280, 0, 1280 * 720 / 2));
void* fake_mapped_address = reinterpret_cast<void*>(0xdeadbeef);
EXPECT_CALL(*mock_buffer, Map()).WillRepeatedly(Return(true));
EXPECT_CALL(*mock_buffer, memory(0))
.WillRepeatedly(Return(fake_mapped_address));
EXPECT_CALL(*mock_buffer, GetHandle()).WillRepeatedly(Return(fake_handle));
return mock_buffer;
}
ChromiumPixelFormat ResolveStreamBufferFormat(
cros::mojom::HalPixelFormat hal_format) override {
return ChromiumPixelFormat{PIXEL_FORMAT_NV12,
gfx::BufferFormat::YUV_420_BIPLANAR};
}
std::unique_ptr<CameraBufferFactory> CreateMockCameraBufferFactory() {
auto buffer_factory = std::make_unique<MockCameraBufferFactory>();
EXPECT_CALL(*buffer_factory, CreateGpuMemoryBuffer(_, _))
.WillRepeatedly(Invoke(CreateMockGpuMemoryBuffer));
EXPECT_CALL(*buffer_factory, ResolveStreamBufferFormat(_))
.WillRepeatedly(Return(ChromiumPixelFormat{
PIXEL_FORMAT_NV12, gfx::BufferFormat::YUV_420_BIPLANAR}));
return buffer_factory;
}
private:
std::unique_ptr<unittest_internal::MockGpuMemoryBufferManager>
gpu_memory_buffer_manager_;
};
} // namespace
......@@ -122,7 +108,8 @@ class StreamBufferManagerTest : public ::testing::Test {
stream_buffer_manager_ = std::make_unique<StreamBufferManager>(
std::move(callback_ops_request),
std::make_unique<MockStreamCaptureInterface>(), device_context_.get(),
CreateMockCameraBufferFactory(), base::ThreadTaskRunnerHandle::Get());
std::make_unique<FakeCameraBufferFactory>(),
base::ThreadTaskRunnerHandle::Get());
}
void TearDown() override { stream_buffer_manager_.reset(); }
......
......@@ -118,6 +118,14 @@ class MockClient : public VideoCaptureDevice::Client {
int frame_feedback_id) override {
frame_cb_.Run(format);
}
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {
frame_cb_.Run(frame_format);
}
// Virtual methods for capturing using Client's Buffers.
Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
VideoPixelFormat format,
......
......@@ -32,6 +32,13 @@ class MockClient : public VideoCaptureDevice::Client {
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {}
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {}
MOCK_METHOD3(ReserveOutputBuffer,
Buffer(const gfx::Size&, VideoPixelFormat, int));
......
......@@ -182,6 +182,13 @@ class MockVideoCaptureDeviceClient : public VideoCaptureDevice::Client {
base::TimeTicks,
base::TimeDelta,
int));
MOCK_METHOD6(OnIncomingCapturedGfxBuffer,
void(gfx::GpuMemoryBuffer* buffer,
const media::VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id));
MOCK_METHOD3(ReserveOutputBuffer,
Buffer(const gfx::Size&, VideoPixelFormat, int));
void OnIncomingCapturedBuffer(Buffer buffer,
......
// 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/mock_gpu_memory_buffer_manager.h"
#include <memory>
using ::testing::Return;
namespace media {
namespace unittest_internal {
namespace {
class FakeGpuMemoryBuffer : public gfx::GpuMemoryBuffer {
public:
FakeGpuMemoryBuffer(const gfx::Size& size, gfx::BufferFormat format)
: size_(size), format_(format) {
// We use only NV12 in unit tests.
EXPECT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, format);
size_t y_plane_size = size_.width() * size_.height();
size_t uv_plane_size = size_.width() * size_.height() / 2;
data_ = std::vector<uint8_t>(y_plane_size + uv_plane_size);
handle_.type = gfx::NATIVE_PIXMAP;
// Set a dummy id since this is for testing only.
handle_.id = gfx::GpuMemoryBufferId(0);
#if defined(OS_CHROMEOS)
// Set a dummy fd since this is for testing only.
handle_.native_pixmap_handle.fds.push_back(base::FileDescriptor(0, false));
handle_.native_pixmap_handle.planes.push_back(
gfx::NativePixmapPlane(size_.width(), 0, y_plane_size));
handle_.native_pixmap_handle.planes.push_back(gfx::NativePixmapPlane(
size_.width(), handle_.native_pixmap_handle.planes[0].size,
uv_plane_size));
#endif
}
~FakeGpuMemoryBuffer() override = default;
bool Map() override { return true; }
void* memory(size_t plane) override {
auto* data_ptr = data_.data();
size_t y_plane_size = size_.width() * size_.height();
switch (plane) {
case 0:
return reinterpret_cast<void*>(data_ptr);
case 1:
return reinterpret_cast<void*>(data_ptr + y_plane_size);
default:
NOTREACHED() << "Unsupported plane: " << plane;
return nullptr;
}
}
void Unmap() override {}
gfx::Size GetSize() const override { return size_; }
gfx::BufferFormat GetFormat() const override { return format_; }
int stride(size_t plane) const override {
switch (plane) {
case 0:
return size_.width();
case 1:
return size_.width();
default:
NOTREACHED() << "Unsupported plane: " << plane;
return 0;
}
}
void SetColorSpace(const gfx::ColorSpace& color_space) override {}
gfx::GpuMemoryBufferId GetId() const override { return handle_.id; }
gfx::GpuMemoryBufferHandle GetHandle() const override { return handle_; }
ClientBuffer AsClientBuffer() override {
NOTREACHED();
return ClientBuffer();
}
private:
gfx::Size size_;
gfx::BufferFormat format_;
std::vector<uint8_t> data_;
gfx::GpuMemoryBufferHandle handle_;
DISALLOW_IMPLICIT_CONSTRUCTORS(FakeGpuMemoryBuffer);
};
} // namespace
MockGpuMemoryBufferManager::MockGpuMemoryBufferManager() = default;
MockGpuMemoryBufferManager::~MockGpuMemoryBufferManager() = default;
// static
std::unique_ptr<gfx::GpuMemoryBuffer>
MockGpuMemoryBufferManager::CreateFakeGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
gpu::SurfaceHandle surface_handle) {
return std::make_unique<FakeGpuMemoryBuffer>(size, format);
}
} // namespace unittest_internal
} // namespace media
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_CAPTURE_VIDEO_CHROMEOS_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
#define MEDIA_CAPTURE_VIDEO_CHROMEOS_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
#ifndef MEDIA_CAPTURE_VIDEO_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
#define MEDIA_CAPTURE_VIDEO_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/common/sync_token.h"
......@@ -13,36 +13,6 @@
namespace media {
namespace unittest_internal {
class MockGpuMemoryBuffer : public gfx::GpuMemoryBuffer {
public:
MockGpuMemoryBuffer();
~MockGpuMemoryBuffer() override;
MOCK_METHOD0(Map, bool());
MOCK_METHOD1(memory, void*(size_t plane));
MOCK_METHOD0(Unmap, void());
MOCK_CONST_METHOD0(GetSize, gfx::Size());
MOCK_CONST_METHOD0(GetFormat, gfx::BufferFormat());
MOCK_CONST_METHOD1(stride, int(size_t plane));
MOCK_METHOD1(SetColorSpace, void(const gfx::ColorSpace& color_space));
MOCK_CONST_METHOD0(GetId, gfx::GpuMemoryBufferId());
MOCK_CONST_METHOD0(GetHandle, gfx::GpuMemoryBufferHandle());
MOCK_METHOD0(AsClientBuffer, ClientBuffer());
private:
DISALLOW_COPY_AND_ASSIGN(MockGpuMemoryBuffer);
};
class MockGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
public:
MockGpuMemoryBufferManager();
......@@ -60,7 +30,7 @@ class MockGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
void(gfx::GpuMemoryBuffer* buffer,
const gpu::SyncToken& sync_token));
std::unique_ptr<gfx::GpuMemoryBuffer> ReturnValidBuffer(
static std::unique_ptr<gfx::GpuMemoryBuffer> CreateFakeGpuMemoryBuffer(
const gfx::Size& size,
gfx::BufferFormat format,
gfx::BufferUsage usage,
......@@ -73,4 +43,4 @@ class MockGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
} // namespace unittest_internal
} // namespace media
#endif // MEDIA_CAPTURE_VIDEO_CHROMEOS_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
#endif // MEDIA_CAPTURE_VIDEO_MOCK_GPU_MEMORY_BUFFER_MANAGER_H_
......@@ -147,6 +147,22 @@ class CAPTURE_EXPORT VideoCaptureDevice
base::TimeDelta timestamp,
int frame_feedback_id = 0) = 0;
// Captured a new video frame, data for which is stored in the
// GpuMemoryBuffer pointed to by |buffer|. The format of the frame is
// described by |frame_format|. Since the memory buffer pointed to by
// |buffer| may be allocated with some size/address alignment requirement,
// this method takes into consideration the size and offset of each plane in
// |buffer| when creating the content of the output buffer.
// |clockwise_rotation|, |reference_time|, |timestamp|, and
// |frame_feedback_id| serve the same purposes as in OnIncomingCapturedData.
virtual void OnIncomingCapturedGfxBuffer(
gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) = 0;
// Reserve an output buffer into which contents can be captured directly.
// The returned Buffer will always be allocated with a memory size suitable
// for holding a packed video frame with pixels of |format| format, of
......
......@@ -30,6 +30,40 @@ bool IsFormatSupported(media::VideoPixelFormat pixel_format) {
return (pixel_format == media::PIXEL_FORMAT_I420 ||
pixel_format == media::PIXEL_FORMAT_Y16);
}
libyuv::RotationMode TranslateRotation(int rotation_degrees) {
DCHECK_EQ(0, rotation_degrees % 90)
<< " Rotation must be a multiple of 90, now: " << rotation_degrees;
libyuv::RotationMode rotation_mode = libyuv::kRotate0;
if (rotation_degrees == 90)
rotation_mode = libyuv::kRotate90;
else if (rotation_degrees == 180)
rotation_mode = libyuv::kRotate180;
else if (rotation_degrees == 270)
rotation_mode = libyuv::kRotate270;
return rotation_mode;
}
void GetI420BufferAccess(
const media::VideoCaptureDevice::Client::Buffer& buffer,
const gfx::Size& dimensions,
uint8_t** y_plane_data,
uint8_t** u_plane_data,
uint8_t** v_plane_data,
int* y_plane_stride,
int* uv_plane_stride) {
*y_plane_data = buffer.handle_provider->GetHandleForInProcessAccess()->data();
*u_plane_data = *y_plane_data + media::VideoFrame::PlaneSize(
media::PIXEL_FORMAT_I420,
media::VideoFrame::kYPlane, dimensions)
.GetArea();
*v_plane_data = *u_plane_data + media::VideoFrame::PlaneSize(
media::PIXEL_FORMAT_I420,
media::VideoFrame::kUPlane, dimensions)
.GetArea();
*y_plane_stride = dimensions.width();
*uv_plane_stride = *y_plane_stride / 2;
}
}
namespace media {
......@@ -139,15 +173,7 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData(
if (rotation == 90 || rotation == 270)
std::swap(destination_width, destination_height);
DCHECK_EQ(0, rotation % 90) << " Rotation must be a multiple of 90, now: "
<< rotation;
libyuv::RotationMode rotation_mode = libyuv::kRotate0;
if (rotation == 90)
rotation_mode = libyuv::kRotate90;
else if (rotation == 180)
rotation_mode = libyuv::kRotate180;
else if (rotation == 270)
rotation_mode = libyuv::kRotate270;
libyuv::RotationMode rotation_mode = TranslateRotation(rotation);
const gfx::Size dimensions(destination_width, destination_height);
Buffer buffer =
......@@ -164,19 +190,13 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData(
DCHECK(dimensions.height());
DCHECK(dimensions.width());
auto buffer_access = buffer.handle_provider->GetHandleForInProcessAccess();
uint8_t* y_plane_data = buffer_access->data();
uint8_t* u_plane_data =
y_plane_data +
VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kYPlane, dimensions)
.GetArea();
uint8_t* v_plane_data =
u_plane_data +
VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kUPlane, dimensions)
.GetArea();
const int yplane_stride = dimensions.width();
const int uv_plane_stride = yplane_stride / 2;
uint8_t* y_plane_data;
uint8_t* u_plane_data;
uint8_t* v_plane_data;
int yplane_stride, uv_plane_stride;
GetI420BufferAccess(buffer, dimensions, &y_plane_data, &u_plane_data,
&v_plane_data, &yplane_stride, &uv_plane_stride);
int crop_x = 0;
int crop_y = 0;
libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
......@@ -283,6 +303,70 @@ void VideoCaptureDeviceClient::OnIncomingCapturedData(
timestamp);
}
void VideoCaptureDeviceClient::OnIncomingCapturedGfxBuffer(
gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id) {
TRACE_EVENT0("media",
"VideoCaptureDeviceClient::OnIncomingCapturedGfxBuffer");
if (last_captured_pixel_format_ != frame_format.pixel_format) {
OnLog("Pixel format: " +
VideoPixelFormatToString(frame_format.pixel_format));
last_captured_pixel_format_ = frame_format.pixel_format;
}
if (!frame_format.IsValid())
return;
int destination_width = buffer->GetSize().width();
int destination_height = buffer->GetSize().height();
if (clockwise_rotation == 90 || clockwise_rotation == 270)
std::swap(destination_width, destination_height);
libyuv::RotationMode rotation_mode = TranslateRotation(clockwise_rotation);
const gfx::Size dimensions(destination_width, destination_height);
auto output_buffer =
ReserveOutputBuffer(dimensions, PIXEL_FORMAT_I420, frame_feedback_id);
uint8_t* y_plane_data;
uint8_t* u_plane_data;
uint8_t* v_plane_data;
int y_plane_stride, uv_plane_stride;
GetI420BufferAccess(output_buffer, dimensions, &y_plane_data, &u_plane_data,
&v_plane_data, &y_plane_stride, &uv_plane_stride);
int ret = -EINVAL;
switch (frame_format.pixel_format) {
case PIXEL_FORMAT_NV12:
ret = libyuv::NV12ToI420Rotate(
reinterpret_cast<uint8_t*>(buffer->memory(0)), buffer->stride(0),
reinterpret_cast<uint8_t*>(buffer->memory(1)), buffer->stride(1),
y_plane_data, y_plane_stride, u_plane_data, uv_plane_stride,
v_plane_data, uv_plane_stride, buffer->GetSize().width(),
buffer->GetSize().height(), rotation_mode);
break;
default:
LOG(ERROR) << "Unsupported format: "
<< VideoPixelFormatToString(frame_format.pixel_format);
}
if (ret) {
DLOG(WARNING) << "Failed to convert buffer's pixel format to I420 from "
<< VideoPixelFormatToString(frame_format.pixel_format);
return;
}
const VideoCaptureFormat output_format = VideoCaptureFormat(
dimensions, frame_format.frame_rate, PIXEL_FORMAT_I420);
OnIncomingCapturedBuffer(std::move(output_buffer), output_format,
reference_time, timestamp);
}
VideoCaptureDevice::Client::Buffer
VideoCaptureDeviceClient::ReserveOutputBuffer(const gfx::Size& frame_size,
VideoPixelFormat pixel_format,
......
......@@ -53,10 +53,16 @@ class CAPTURE_EXPORT VideoCaptureDeviceClient
void OnIncomingCapturedData(const uint8_t* data,
int length,
const VideoCaptureFormat& frame_format,
int rotation,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override;
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override;
Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
VideoPixelFormat format,
int frame_feedback_id) override;
......
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "media/base/limits.h"
#include "media/capture/video/mock_gpu_memory_buffer_manager.h"
#include "media/capture/video/mock_video_frame_receiver.h"
#include "media/capture/video/video_capture_buffer_pool_impl.h"
#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
......@@ -47,9 +48,11 @@ class VideoCaptureDeviceClientTest : public ::testing::Test {
VideoCaptureDeviceClientTest() {
scoped_refptr<VideoCaptureBufferPoolImpl> buffer_pool(
new VideoCaptureBufferPoolImpl(
std::make_unique<VideoCaptureBufferTrackerFactoryImpl>(), 1));
std::make_unique<VideoCaptureBufferTrackerFactoryImpl>(), 2));
auto controller = std::make_unique<MockVideoFrameReceiver>();
receiver_ = controller.get();
gpu_memory_buffer_manager_ =
std::make_unique<unittest_internal::MockGpuMemoryBufferManager>();
device_client_ = std::make_unique<VideoCaptureDeviceClient>(
std::move(controller), buffer_pool,
base::Bind(&ReturnNullPtrAsJpecDecoder));
......@@ -58,6 +61,8 @@ class VideoCaptureDeviceClientTest : public ::testing::Test {
protected:
MockVideoFrameReceiver* receiver_;
std::unique_ptr<unittest_internal::MockGpuMemoryBufferManager>
gpu_memory_buffer_manager_;
std::unique_ptr<VideoCaptureDeviceClient> device_client_;
private:
......@@ -83,6 +88,26 @@ TEST_F(VideoCaptureDeviceClientTest, Minimal) {
device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes,
kFrameFormat, 0 /*clockwise rotation*/,
base::TimeTicks(), base::TimeDelta());
const gfx::Size kBufferDimensions(10, 10);
const VideoCaptureFormat kFrameFormatNV12(
kBufferDimensions, 30.0f /*frame_rate*/, PIXEL_FORMAT_NV12);
std::unique_ptr<gfx::GpuMemoryBuffer> buffer =
gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer(
kBufferDimensions, gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle);
{
InSequence s;
const int expected_buffer_id = 1;
EXPECT_CALL(*receiver_, OnLog(_));
EXPECT_CALL(*receiver_, MockOnNewBufferHandle(expected_buffer_id));
EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(expected_buffer_id, _, _));
EXPECT_CALL(*receiver_, OnBufferRetired(expected_buffer_id));
}
device_client_->OnIncomingCapturedGfxBuffer(
buffer.get(), kFrameFormatNV12, 0 /*clockwise rotation*/,
base::TimeTicks(), base::TimeDelta());
// Releasing |device_client_| will also release |receiver_|.
device_client_.reset();
}
......@@ -102,6 +127,19 @@ TEST_F(VideoCaptureDeviceClientTest, FailsSilentlyGivenInvalidFrameFormat) {
device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes,
kFrameFormat, 0 /*clockwise rotation*/,
base::TimeTicks(), base::TimeDelta());
const gfx::Size kBufferDimensions(10, 10);
const VideoCaptureFormat kFrameFormatNV12(
kBufferDimensions, 30.0f /*frame_rate*/, PIXEL_FORMAT_NV12);
std::unique_ptr<gfx::GpuMemoryBuffer> buffer =
gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer(
kBufferDimensions, gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle);
EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)).Times(0);
device_client_->OnIncomingCapturedGfxBuffer(
buffer.get(), kFrameFormat, 0 /*clockwise rotation*/, base::TimeTicks(),
base::TimeDelta());
Mock::VerifyAndClearExpectations(receiver_);
}
......@@ -113,22 +151,27 @@ TEST_F(VideoCaptureDeviceClientTest, DropsFrameIfNoBuffer) {
PIXEL_FORMAT_I420);
EXPECT_CALL(*receiver_, OnLog(_)).Times(1);
// Simulate that receiver still holds |buffer_read_permission| for the first
// buffer when the second call to OnIncomingCapturedData comes in.
// Since we set up the buffer pool to max out at 1 buffer, this should cause
// two buffers when the third call to OnIncomingCapturedData comes in.
// Since we set up the buffer pool to max out at 2 buffer, this should cause
// |device_client_| to drop the frame.
std::unique_ptr<VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>
std::vector<std::unique_ptr<
VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>>
read_permission;
EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _))
.WillOnce(Invoke(
.Times(2)
.WillRepeatedly(Invoke(
[&read_permission](
int buffer_id,
std::unique_ptr<
VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>*
buffer_read_permission,
const gfx::Size&) {
read_permission = std::move(*buffer_read_permission);
read_permission.push_back(std::move(*buffer_read_permission));
}));
// Pass two frames. The second will be dropped.
// Pass three frames. The third will be dropped.
device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes,
kFrameFormat, 0 /*clockwise rotation*/,
base::TimeTicks(), base::TimeDelta());
device_client_->OnIncomingCapturedData(data, kScratchpadSizeInBytes,
kFrameFormat, 0 /*clockwise rotation*/,
base::TimeTicks(), base::TimeDelta());
......@@ -227,6 +270,37 @@ TEST_F(VideoCaptureDeviceClientTest, CheckRotationsAndCrops) {
Mock::VerifyAndClearExpectations(receiver_);
}
SizeAndRotation kSizeAndRotationsNV12[] = {{{6, 4}, 0, {6, 4}},
{{6, 4}, 90, {4, 6}},
{{6, 4}, 180, {6, 4}},
{{6, 4}, 270, {4, 6}}};
EXPECT_CALL(*receiver_, OnLog(_)).Times(1);
for (const auto& size_and_rotation : kSizeAndRotationsNV12) {
params.requested_format = VideoCaptureFormat(
size_and_rotation.input_resolution, 30.0f, PIXEL_FORMAT_NV12);
std::unique_ptr<gfx::GpuMemoryBuffer> buffer =
gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer(
size_and_rotation.input_resolution,
gfx::BufferFormat::YUV_420_BIPLANAR,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gpu::kNullSurfaceHandle);
gfx::Size coded_size;
EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _))
.Times(1)
.WillOnce(SaveArg<2>(&coded_size));
device_client_->OnIncomingCapturedGfxBuffer(
buffer.get(), params.requested_format, size_and_rotation.rotation,
base::TimeTicks(), base::TimeDelta());
EXPECT_EQ(coded_size.width(), size_and_rotation.output_resolution.width());
EXPECT_EQ(coded_size.height(),
size_and_rotation.output_resolution.height());
Mock::VerifyAndClearExpectations(receiver_);
}
}
} // namespace media
......@@ -71,6 +71,13 @@
#define MAYBE_CaptureMjpeg CaptureMjpeg
#define MAYBE_TakePhoto TakePhoto
#define MAYBE_GetPhotoState GetPhotoState
#define MAYBE_CaptureWithSize CaptureWithSize
#elif defined(OS_CHROMEOS)
#define MAYBE_AllocateBadSize DISABLED_AllocateBadSize
#define MAYBE_CaptureMjpeg CaptureMjpeg
#define MAYBE_TakePhoto TakePhoto
#define MAYBE_GetPhotoState GetPhotoState
#define MAYBE_CaptureWithSize CaptureWithSize
#elif defined(OS_LINUX)
// AllocateBadSize will hang when a real camera is attached and if more than one
// test is trying to use the camera (even across processes). Do NOT renable
......@@ -168,6 +175,17 @@ class MockVideoCaptureClient : public VideoCaptureDevice::Client {
main_thread_->PostTask(FROM_HERE, base::BindOnce(frame_cb_, format));
}
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {
ASSERT_TRUE(buffer);
ASSERT_GT(buffer->GetSize().width() * buffer->GetSize().height(), 0);
main_thread_->PostTask(FROM_HERE, base::BindOnce(frame_cb_, frame_format));
}
// Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
VideoPixelFormat format,
......
......@@ -34,6 +34,13 @@ class MockClient : public VideoCaptureDevice::Client {
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {}
void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {}
MOCK_METHOD3(ReserveOutputBuffer,
Buffer(const gfx::Size&, VideoPixelFormat, int));
......
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