Commit 9e85e676 authored by Christian Fremerey's avatar Christian Fremerey Committed by Commit Bot

[Video Capture Service] Add support for raw file descriptors for virtual devices

Follow-up for https://chromium-review.googlesource.com/c/chromium/src/+/1043646.
Here we extend the support for raw file descriptors to the virtual device
functionality.

There is an incompatibility in the serialization of shared memory handles
between the Mojo libraries used in Chromium vs. the older ones used in
ChromiumOS. As a workaround, this CL allows clients of the virtual device API of
the video capture service to ask it to use raw file descriptors for sharing
shared memory handles instead.
Note, this is only supported on platforms that use file descriptors for
base::SharedMemoryHandle, i.e. Posix platforms.

Design Doc: https://docs.google.com/document/d/1cmtWA8yua5sqjZC0tswVUZwJeQR-KSZBr3Mj1jUagKk/edit?usp=sharing

Bug: 847598
Change-Id: I8a49e5181a622e14ea050cde27ad91ff7feda30d
Reviewed-on: https://chromium-review.googlesource.com/1053210
Commit-Queue: Christian Fremerey <chfremer@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarEmircan Uysaler <emircan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#573868}
parent 903f2225
......@@ -40,6 +40,7 @@ class MockDeviceFactory : public video_capture::mojom::DeviceFactory {
void AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
video_capture::mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device)
override {
DoAddVirtualDevice(device_info, producer.get(), &virtual_device);
......
......@@ -70,6 +70,7 @@ class MockDeviceFactory : public video_capture::mojom::DeviceFactory {
void AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
video_capture::mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
video_capture::mojom::SharedMemoryVirtualDeviceRequest virtual_device)
override {
DoAddVirtualDevice(device_info, producer.get(), &virtual_device);
......
......@@ -233,9 +233,12 @@ class SharedMemoryDeviceExerciser : public VirtualDeviceExerciser,
video_capture::mojom::DeviceFactoryPtr* factory,
const media::VideoCaptureDeviceInfo& info) override {
video_capture::mojom::ProducerPtr producer;
static const bool kSendBufferHandlesToProducerAsRawFileDescriptors = false;
producer_binding_.Bind(mojo::MakeRequest(&producer));
(*factory)->AddSharedMemoryVirtualDevice(
info, std::move(producer), mojo::MakeRequest(&virtual_device_));
info, std::move(producer),
kSendBufferHandlesToProducerAsRawFileDescriptors,
mojo::MakeRequest(&virtual_device_));
}
void PushNextFrame(base::TimeDelta timestamp) override {
virtual_device_->RequestFrameBuffer(
......@@ -250,12 +253,14 @@ class SharedMemoryDeviceExerciser : public VirtualDeviceExerciser,
}
// video_capture::mojom::Producer implementation.
void OnNewBufferHandle(int32_t buffer_id,
mojo::ScopedSharedBufferHandle buffer_handle,
OnNewBufferHandleCallback callback) override {
void OnNewBuffer(int32_t buffer_id,
media::mojom::VideoBufferHandlePtr buffer_handle,
OnNewBufferCallback callback) override {
CHECK(buffer_handle->is_shared_buffer_handle());
auto handle_provider =
std::make_unique<media::SharedMemoryHandleProvider>();
handle_provider->InitFromMojoHandle(std::move(buffer_handle));
handle_provider->InitFromMojoHandle(
std::move(buffer_handle->get_shared_buffer_handle()));
outgoing_buffer_id_to_buffer_map_.insert(
std::make_pair(buffer_id, std::move(handle_provider)));
std::move(callback).Run();
......
......@@ -68,10 +68,6 @@ class StubBufferHandleProvider
return base::SharedMemoryHandle();
}
uint32_t GetMemorySizeInBytes() override {
return static_cast<uint32_t>(mapped_size_);
}
std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
override {
return std::make_unique<StubBufferHandle>(mapped_size_, data_);
......
......@@ -43,11 +43,12 @@ class CAPTURE_EXPORT SharedMemoryHandleProvider
uint32_t memory_size_in_bytes);
#endif // defined(OS_LINUX)
uint32_t GetMemorySizeInBytes();
// Implementation of Buffer::HandleProvider:
mojo::ScopedSharedBufferHandle GetHandleForInterProcessTransit(
bool read_only) override;
base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC() override;
uint32_t GetMemorySizeInBytes() override;
std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
override;
......
......@@ -7,6 +7,7 @@
#include "base/memory/ref_counted.h"
#include "media/capture/capture_export.h"
#include "media/capture/mojom/video_capture_types.mojom.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/system/buffer.h"
#include "ui/gfx/geometry/size.h"
......@@ -50,7 +51,8 @@ class CAPTURE_EXPORT VideoCaptureBufferPool
virtual base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC(
int buffer_id) = 0;
virtual uint32_t GetMemorySizeInBytes(int buffer_id) = 0;
virtual mojom::SharedMemoryViaRawFileDescriptorPtr
CreateSharedMemoryViaRawFileDescriptorStruct(int buffer_id) = 0;
// Try and obtain a read/write access to the buffer.
virtual std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess(
......
......@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "media/capture/video/video_capture_buffer_handle.h"
#include "media/capture/video/video_capture_buffer_tracker.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/buffer_format_util.h"
namespace media {
......@@ -53,7 +54,12 @@ VideoCaptureBufferPoolImpl::GetNonOwnedSharedMemoryHandleForLegacyIPC(
return tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC();
}
uint32_t VideoCaptureBufferPoolImpl::GetMemorySizeInBytes(int buffer_id) {
mojom::SharedMemoryViaRawFileDescriptorPtr
VideoCaptureBufferPoolImpl::CreateSharedMemoryViaRawFileDescriptorStruct(
int buffer_id) {
// This requires platforms where base::SharedMemoryHandle is backed by a
// file descriptor.
#if defined(OS_LINUX)
base::AutoLock lock(lock_);
VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
......@@ -62,7 +68,17 @@ uint32_t VideoCaptureBufferPoolImpl::GetMemorySizeInBytes(int buffer_id) {
return 0u;
}
return tracker->GetMemorySizeInBytes();
auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
result->file_descriptor_handle = mojo::WrapPlatformFile(
base::SharedMemory::DuplicateHandle(
tracker->GetNonOwnedSharedMemoryHandleForLegacyIPC())
.GetHandle());
result->shared_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
return result;
#else
NOTREACHED();
return mojom::SharedMemoryViaRawFileDescriptorPtr();
#endif
}
std::unique_ptr<VideoCaptureBufferHandle>
......
......@@ -40,7 +40,8 @@ class CAPTURE_EXPORT VideoCaptureBufferPoolImpl
bool read_only) override;
base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC(
int buffer_id) override;
uint32_t GetMemorySizeInBytes(int buffer_id) override;
mojom::SharedMemoryViaRawFileDescriptorPtr
CreateSharedMemoryViaRawFileDescriptorStruct(int buffer_id) override;
std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess(
int buffer_id) override;
int ReserveForProducer(const gfx::Size& dimensions,
......
......@@ -99,7 +99,6 @@ class CAPTURE_EXPORT VideoCaptureDevice
bool read_only) = 0;
virtual base::SharedMemoryHandle
GetNonOwnedSharedMemoryHandleForLegacyIPC() = 0;
virtual uint32_t GetMemorySizeInBytes() = 0;
virtual std::unique_ptr<VideoCaptureBufferHandle>
GetHandleForInProcessAccess() = 0;
};
......
......@@ -22,7 +22,6 @@
#include "media/capture/video/video_capture_jpeg_decoder.h"
#include "media/capture/video/video_frame_receiver.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/libyuv/include/libyuv.h"
namespace {
......@@ -87,9 +86,6 @@ class BufferPoolBufferHandleProvider
override {
return buffer_pool_->GetNonOwnedSharedMemoryHandleForLegacyIPC(buffer_id_);
}
uint32_t GetMemorySizeInBytes() override {
return buffer_pool_->GetMemorySizeInBytes(buffer_id_);
}
std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
override {
return buffer_pool_->GetHandleForInProcessAccess(buffer_id_);
......@@ -416,7 +412,8 @@ VideoCaptureDeviceClient::ReserveOutputBuffer(const gfx::Size& frame_size,
break;
case VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor:
buffer_handle->set_shared_memory_via_raw_file_descriptor(
CreateSharedMemoryViaRawFileDescriptorStruct(buffer_id));
buffer_pool_->CreateSharedMemoryViaRawFileDescriptorStruct(
buffer_id));
break;
case VideoCaptureBufferType::kMailboxHolder:
NOTREACHED();
......@@ -429,26 +426,6 @@ VideoCaptureDeviceClient::ReserveOutputBuffer(const gfx::Size& frame_size,
return MakeBufferStruct(buffer_pool_, buffer_id, frame_feedback_id);
}
mojom::SharedMemoryViaRawFileDescriptorPtr
VideoCaptureDeviceClient::CreateSharedMemoryViaRawFileDescriptorStruct(
int buffer_id) {
// This requires platforms where base::SharedMemoryHandle is backed by a
// file descriptor.
#if defined(OS_LINUX)
auto result = mojom::SharedMemoryViaRawFileDescriptor::New();
result->file_descriptor_handle = mojo::WrapPlatformFile(
base::SharedMemory::DuplicateHandle(
buffer_pool_->GetNonOwnedSharedMemoryHandleForLegacyIPC(buffer_id))
.GetHandle());
result->shared_memory_size_in_bytes =
buffer_pool_->GetMemorySizeInBytes(buffer_id);
return result;
#else
NOTREACHED();
return mojom::SharedMemoryViaRawFileDescriptorPtr();
#endif
}
void VideoCaptureDeviceClient::OnIncomingCapturedBuffer(
Buffer buffer,
const VideoCaptureFormat& format,
......
......@@ -101,9 +101,6 @@ class CAPTURE_EXPORT VideoCaptureDeviceClient
base::TimeDelta timestamp,
int frame_feedback_id);
mojom::SharedMemoryViaRawFileDescriptorPtr
CreateSharedMemoryViaRawFileDescriptorStruct(int buffer_id);
const VideoCaptureBufferType target_buffer_type_;
// The receiver to which we post events.
......
......@@ -134,6 +134,7 @@ void DeviceFactoryMediaToMojoAdapter::CreateDevice(
void DeviceFactoryMediaToMojoAdapter::AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
mojom::SharedMemoryVirtualDeviceRequest virtual_device_request) {
NOTIMPLEMENTED();
}
......
......@@ -38,6 +38,7 @@ class DeviceFactoryMediaToMojoAdapter : public mojom::DeviceFactory {
void AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
mojom::SharedMemoryVirtualDeviceRequest virtual_device) override;
void AddTextureVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
......
......@@ -44,11 +44,17 @@ interface DeviceFactory {
// factory using the given |device_info|. The returned |virtual_device| is to
// be used by the caller to subsequently push video frames. These frames will
// appear to clients of the device as if they were produced by the device.
// If |send_buffer_handles_to_producer_as_raw_file_descriptors| is set to
// true, buffers requested via
// |SharedMemoryVirtualDevice.RequestFrameBuffer()| will be shared with
// |producer| as |VideoBufferHandle.shared_memory_via_raw_file_descriptor|,
// and otherwise as |VideoBufferHandle.shared_buffer_handle|.
// The virtual device is removed either when the caller releases
// |virtual_device| or the given |producer| is closed.
AddSharedMemoryVirtualDevice(
media.mojom.VideoCaptureDeviceInfo device_info,
Producer producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
SharedMemoryVirtualDevice& virtual_device);
// Similar to AddSharedMemoryVirtualDevice() but for virtual devices that
......
......@@ -4,10 +4,12 @@
module video_capture.mojom;
import "media/capture/mojom/video_capture_types.mojom";
// An interface for providing buffer info updates to the producer of
// video frames for the associated |VirtualDevice| interface.
interface Producer {
OnNewBufferHandle(int32 buffer_id, handle<shared_buffer> buffer_handle)
OnNewBuffer(int32 buffer_id, media.mojom.VideoBufferHandle buffer_handle)
=> ();
OnBufferRetired(int32 buffer_id);
};
......@@ -16,7 +16,7 @@
namespace {
void OnNewBufferHandleAcknowleged(
void OnNewBufferAcknowleged(
video_capture::mojom::SharedMemoryVirtualDevice::RequestFrameBufferCallback
callback,
int32_t buffer_id) {
......@@ -29,9 +29,12 @@ namespace video_capture {
SharedMemoryVirtualDeviceMojoAdapter::SharedMemoryVirtualDeviceMojoAdapter(
std::unique_ptr<service_manager::ServiceContextRef> service_ref,
mojom::ProducerPtr producer)
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors)
: service_ref_(std::move(service_ref)),
producer_(std::move(producer)),
send_buffer_handles_to_producer_as_raw_file_descriptors_(
send_buffer_handles_to_producer_as_raw_file_descriptors),
buffer_pool_(new media::VideoCaptureBufferPoolImpl(
std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>(),
max_buffer_pool_buffer_count())) {}
......@@ -89,17 +92,26 @@ void SharedMemoryVirtualDeviceMojoAdapter::RequestFrameBuffer(
}
known_buffer_ids_.push_back(buffer_id);
// Share buffer handle with producer.
media::mojom::VideoBufferHandlePtr buffer_handle =
media::mojom::VideoBufferHandle::New();
if (send_buffer_handles_to_producer_as_raw_file_descriptors_) {
buffer_handle->set_shared_memory_via_raw_file_descriptor(
buffer_pool_->CreateSharedMemoryViaRawFileDescriptorStruct(
buffer_id));
} else {
buffer_handle->set_shared_buffer_handle(
buffer_pool_->GetHandleForInterProcessTransit(buffer_id,
true /*read_only*/));
}
// Invoke the response back only after the producer have acked
// that it has received the newly created buffer. This is need
// because the |producer_| and the |callback| are bound to different
// message pipes, so the order for calls to |producer_| and |callback|
// is not guaranteed.
producer_->OnNewBufferHandle(
buffer_id,
buffer_pool_->GetHandleForInterProcessTransit(buffer_id,
false /* read_only */),
base::BindOnce(&OnNewBufferHandleAcknowleged, base::Passed(&callback),
buffer_id));
producer_->OnNewBuffer(buffer_id, std::move(buffer_handle),
base::BindOnce(&OnNewBufferAcknowleged,
base::Passed(&callback), buffer_id));
return;
}
std::move(callback).Run(buffer_id);
......
......@@ -21,7 +21,8 @@ class SharedMemoryVirtualDeviceMojoAdapter
public:
SharedMemoryVirtualDeviceMojoAdapter(
std::unique_ptr<service_manager::ServiceContextRef> service_ref,
mojom::ProducerPtr producer);
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors = false);
~SharedMemoryVirtualDeviceMojoAdapter() override;
// mojom::SharedMemoryVirtualDevice implementation.
......@@ -57,6 +58,7 @@ class SharedMemoryVirtualDeviceMojoAdapter
const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
mojom::ReceiverPtr receiver_;
mojom::ProducerPtr producer_;
const bool send_buffer_handles_to_producer_as_raw_file_descriptors_;
scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
std::vector<int> known_buffer_ids_;
SEQUENCE_CHECKER(sequence_checker_);
......
......@@ -80,6 +80,7 @@ TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
}));
factory_->AddSharedMemoryVirtualDevice(
info, std::move(producer_proxy),
false /* send_buffer_handles_to_producer_as_raw_file_descriptors */,
mojo::MakeRequest(&virtual_device_proxy));
factory_->GetDeviceInfos(device_info_receiver_.Get());
wait_loop.Run();
......@@ -125,6 +126,7 @@ TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
.WillOnce(InvokeWithoutArgs([&wait_loop]() { wait_loop.Quit(); }));
factory_->AddSharedMemoryVirtualDevice(
info, std::move(producer_proxy),
false /* send_buffer_handles_to_producer_as_raw_file_descriptors */,
mojo::MakeRequest(&virtual_device_proxy));
factory_->CreateDevice(virtual_device_id, mojo::MakeRequest(&device_proxy),
create_device_proxy_callback.Get());
......
......@@ -11,11 +11,10 @@ MockProducer::MockProducer(mojom::ProducerRequest request)
MockProducer::~MockProducer() = default;
void MockProducer::OnNewBufferHandle(
int32_t buffer_id,
mojo::ScopedSharedBufferHandle buffer_handle,
OnNewBufferHandleCallback callback) {
DoOnNewBufferHandle(buffer_id, &buffer_handle, callback);
void MockProducer::OnNewBuffer(int32_t buffer_id,
media::mojom::VideoBufferHandlePtr buffer_handle,
OnNewBufferCallback callback) {
DoOnNewBuffer(buffer_id, &buffer_handle, callback);
}
} // namespace video_capture
......@@ -18,14 +18,14 @@ class MockProducer : public mojom::Producer {
~MockProducer() override;
// Use forwarding method to work around gmock not supporting move-only types.
void OnNewBufferHandle(int32_t buffer_id,
mojo::ScopedSharedBufferHandle buffer_handle,
OnNewBufferHandleCallback callback) override;
void OnNewBuffer(int32_t buffer_id,
media::mojom::VideoBufferHandlePtr buffer_handle,
OnNewBufferCallback callback) override;
MOCK_METHOD3(DoOnNewBufferHandle,
MOCK_METHOD3(DoOnNewBuffer,
void(int32_t,
mojo::ScopedSharedBufferHandle*,
OnNewBufferHandleCallback& callback));
media::mojom::VideoBufferHandlePtr*,
OnNewBufferCallback& callback));
MOCK_METHOD1(OnBufferRetired, void(int32_t));
private:
......
......@@ -56,12 +56,12 @@ class VirtualDeviceTest : public ::testing::Test {
void VerifyAndGetMaxFrameBuffers() {
base::RunLoop wait_loop;
EXPECT_CALL(*producer_, DoOnNewBufferHandle(_, _, _))
EXPECT_CALL(*producer_, DoOnNewBuffer(_, _, _))
.Times(SharedMemoryVirtualDeviceMojoAdapter::
max_buffer_pool_buffer_count())
.WillRepeatedly(
Invoke([](int32_t buffer_id, mojo::ScopedSharedBufferHandle* handle,
mojom::Producer::OnNewBufferHandleCallback& callback) {
.WillRepeatedly(Invoke(
[](int32_t buffer_id, media::mojom::VideoBufferHandlePtr* handle,
mojom::Producer::OnNewBufferCallback& callback) {
std::move(callback).Run();
}));
// Should receive valid buffer for up to the maximum buffer count.
......@@ -113,7 +113,7 @@ TEST_F(VirtualDeviceTest, OnFrameReadyInBufferWithoutReceiver) {
// Verify there is a buffer available now, without creating a new
// buffer.
EXPECT_CALL(*producer_, DoOnNewBufferHandle(_, _, _)).Times(0);
EXPECT_CALL(*producer_, DoOnNewBuffer(_, _, _)).Times(0);
device_adapter_->RequestFrameBuffer(
kTestFrameSize, kTestPixelFormat,
base::Bind(&VirtualDeviceTest::OnFrameBufferReceived,
......@@ -151,7 +151,7 @@ TEST_F(VirtualDeviceTest, OnFrameReadyInBufferWithReceiver) {
// Verify that requesting a buffer doesn't create a new one, will reuse
// the available buffer in the pool.
base::RunLoop wait_loop2;
EXPECT_CALL(*producer_, DoOnNewBufferHandle(_, _, _)).Times(0);
EXPECT_CALL(*producer_, DoOnNewBuffer(_, _, _)).Times(0);
base::MockCallback<
mojom::SharedMemoryVirtualDevice::RequestFrameBufferCallback>
request_frame_buffer_callback;
......
......@@ -129,6 +129,7 @@ void VirtualDeviceEnabledDeviceFactory::CreateDevice(
void VirtualDeviceEnabledDeviceFactory::AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
mojom::SharedMemoryVirtualDeviceRequest virtual_device_request) {
auto device_id = device_info.descriptor.device_id;
auto virtual_device_iter = virtual_devices_by_id_.find(device_id);
......@@ -143,7 +144,8 @@ void VirtualDeviceEnabledDeviceFactory::AddSharedMemoryVirtualDevice(
OnVirtualDeviceProducerConnectionErrorOrClose,
base::Unretained(this), device_id));
auto device = std::make_unique<SharedMemoryVirtualDeviceMojoAdapter>(
service_ref_->Clone(), std::move(producer));
service_ref_->Clone(), std::move(producer),
send_buffer_handles_to_producer_as_raw_file_descriptors);
auto producer_binding =
std::make_unique<mojo::Binding<mojom::SharedMemoryVirtualDevice>>(
device.get(), std::move(virtual_device_request));
......
......@@ -32,6 +32,7 @@ class VirtualDeviceEnabledDeviceFactory : public mojom::DeviceFactory {
void AddSharedMemoryVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
mojom::ProducerPtr producer,
bool send_buffer_handles_to_producer_as_raw_file_descriptors,
mojom::SharedMemoryVirtualDeviceRequest virtual_device) override;
void AddTextureVirtualDevice(
const media::VideoCaptureDeviceInfo& device_info,
......
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