Commit 138a8290 authored by Ren-Pei Zeng's avatar Ren-Pei Zeng Committed by Commit Bot

chromeos_camera: Add MJDA Mojo interface for decoding with DMA buffers

Original MjpegDecodeAccelerator (MJDA) Mojo interface only permits
shared memory buffers and I420 output format. This CL adds
DecodeWithDmaBuf() function that enables DMA buffers and different
output pixel formats.

Bug: b:120057531
Test: Preview in camera apps with MJPEG stream.
Change-Id: Ic5dbf2b0bf2ba13e295388db70b1ade7a117fb80
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1547470
Commit-Queue: Ren-Pei Zeng <kamesan@chromium.org>
Reviewed-by: default avatarRicky Liang <jcliang@chromium.org>
Reviewed-by: default avatarJorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarWei Lee <wtlee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#693156}
parent 1b197823
......@@ -6,6 +6,8 @@ import("//build/config/ui.gni")
import("//media/gpu/args.gni")
import("//testing/test.gni")
assert(is_chromeos)
source_set("jpeg_encode_accelerator") {
sources = [
"jpeg_encode_accelerator.cc",
......@@ -46,6 +48,24 @@ source_set("mojo_mjpeg_decode_accelerator") {
]
}
source_set("utils") {
sources = [
"dmabuf_utils.cc",
"dmabuf_utils.h",
]
public_deps = [
"//base",
"//components/chromeos_camera/common",
"//media",
"//ui/gfx/geometry",
]
deps = [
"//mojo/public/cpp/system",
]
}
# TODO(crbug.com/960243): Don't compile these codes for build without vaapi/v4l2
# supported.
source_set("jpeg_encode_accelerator_service") {
......@@ -66,6 +86,7 @@ source_set("jpeg_encode_accelerator_service") {
deps = [
":jpeg_encode_accelerator",
":utils",
"//components/chromeos_camera/common",
"//media",
"//media/gpu:buildflags",
......@@ -101,6 +122,7 @@ source_set("mjpeg_decode_accelerator_service") {
deps = [
":mjpeg_decode_accelerator",
":utils",
"//components/chromeos_camera/common",
"//media",
"//media/gpu:buildflags",
......
......@@ -6,6 +6,7 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("common") {
sources = [
"dmabuf.mojom",
"jpeg_encode_accelerator.mojom",
"mjpeg_decode_accelerator.mojom",
]
......
// Copyright 2019 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.
module chromeos_camera.mojom;
import "media/mojo/mojom/media_types.mojom";
// This structure defines a DMA-buf buffer plane. |fd_handle| holds the DMA-buf
// file descriptor. The layout is specified by |stride|, |offset| and |size|.
struct DmaBufPlane {
handle fd_handle;
int32 stride;
uint32 offset;
uint32 size;
};
// This structure defines a simplified version of media::VideoFrame backed by
// DMA-bufs (see media/base/video_frame.h) for Chrome OS usage. The video frame
// has pixel format |format| and coded size |coded_width|x|coded_height|.
// Per-plane DMA-buf FDs and layouts are defined in |planes|.
struct DmaBufVideoFrame {
media.mojom.VideoPixelFormat format;
uint32 coded_width;
uint32 coded_height;
array<DmaBufPlane> planes;
};
......@@ -4,6 +4,7 @@
module chromeos_camera.mojom;
import "components/chromeos_camera/common/dmabuf.mojom";
import "media/mojo/mojom/media_types.mojom";
// Encode errors (see components/chromeos_camera/jpeg_encode_accelerator.h).
......@@ -17,13 +18,6 @@ enum EncodeStatus {
PLATFORM_FAILURE,
};
struct DmaBufPlane {
handle fd_handle;
int32 stride;
uint32 offset;
uint32 size;
};
// GPU process interface exposed to the browser for encoding JPEG images.
interface JpegEncodeAccelerator {
// Initializes the JPEG encoder. Should be called once per encoder
......
......@@ -4,6 +4,7 @@
module chromeos_camera.mojom;
import "components/chromeos_camera/common/dmabuf.mojom";
import "media/mojo/mojom/media_types.mojom";
import "mojo/public/mojom/base/time.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
......@@ -61,11 +62,22 @@ interface MjpegDecodeAccelerator {
// Returns |buffer_id| and |error| in a callback to notify the
// decode status. |buffer_id| is the id of |input_buffer| and |error| is the
// error code.
// TODO(kamesan): deprecate this function when all clients have switched to
// DecodeWithDmaBuf().
DecodeWithFD(int32 buffer_id, handle input_fd, uint32 input_buffer_size,
int32 coded_size_width, int32 coded_size_height,
handle output_fd, uint32 output_buffer_size)
=> (int32 buffer_id, DecodeError error);
// Decodes one MJPEG image with the given input and output buffers.
// |task_id| is used to distinguish different tasks. The input image is stored
// in the DMA buffer described by |src_dmabuf_fd|, |src_size|, and
// |src_offset|. The decoded result will be put into |dst_frame| backed by DMA
// buffer. Returns the decode status |error| in the Mojo callback.
DecodeWithDmaBuf(int32 task_id, handle src_dmabuf_fd, uint32 src_size,
uint32 src_offset, DmaBufVideoFrame dst_frame)
=> (DecodeError error);
// TODO(c.padhi): This method might not be required, see
// http://crbug.com/699255.
Uninitialize();
......
// Copyright 2019 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 "components/chromeos_camera/dmabuf_utils.h"
#include <utility>
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/time/time.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/rect.h"
namespace chromeos_camera {
scoped_refptr<media::VideoFrame> ConstructVideoFrame(
std::vector<mojom::DmaBufPlanePtr> dma_buf_planes,
media::VideoPixelFormat pixel_format,
const gfx::Size& coded_size) {
const size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
if (num_planes != dma_buf_planes.size()) {
DLOG(ERROR) << "The number of DMA buf planes does not match the format";
return nullptr;
}
if (coded_size.IsEmpty()) {
DLOG(ERROR) << "Invalid coded size: " << coded_size.width() << ", "
<< coded_size.height();
return nullptr;
}
const gfx::Rect visible_rect(coded_size);
std::vector<base::ScopedFD> dma_buf_fds(num_planes);
std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
for (size_t i = 0; i < num_planes; ++i) {
mojo::PlatformHandle handle =
mojo::UnwrapPlatformHandle(std::move(dma_buf_planes[i]->fd_handle));
if (!handle.is_valid()) {
DLOG(ERROR) << "Invalid DMA buf file descriptor";
return nullptr;
}
dma_buf_fds[i] = handle.TakeFD();
planes[i] = media::VideoFrameLayout::Plane(
dma_buf_planes[i]->stride,
base::strict_cast<size_t>(dma_buf_planes[i]->offset),
base::strict_cast<size_t>(dma_buf_planes[i]->size));
}
const base::Optional<media::VideoFrameLayout> layout =
media::VideoFrameLayout::CreateWithPlanes(pixel_format, coded_size,
std::move(planes));
if (!layout) {
DLOG(ERROR) << "Failed to create video frame layout";
return nullptr;
}
return media::VideoFrame::WrapExternalDmabufs(
*layout, // layout
visible_rect, // visible_rect
coded_size, // natural_size
std::move(dma_buf_fds), // dmabuf_fds
base::TimeDelta()); // timestamp
}
} // namespace chromeos_camera
// Copyright 2019 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 COMPONENTS_CHROMEOS_CAMERA_DMABUF_UTILS_H_
#define COMPONENTS_CHROMEOS_CAMERA_DMABUF_UTILS_H_
#include <stdint.h>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "components/chromeos_camera/common/dmabuf.mojom.h"
#include "media/base/video_types.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class VideoFrame;
}
namespace chromeos_camera {
scoped_refptr<media::VideoFrame> ConstructVideoFrame(
std::vector<mojom::DmaBufPlanePtr> dma_buf_planes,
media::VideoPixelFormat pixel_format,
const gfx::Size& coded_size);
} // namespace chromeos_camera
#endif // COMPONENTS_CHROMEOS_CAMERA_DMABUF_UTILS_H_
......@@ -19,6 +19,8 @@
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "components/chromeos_camera/common/dmabuf.mojom.h"
#include "components/chromeos_camera/dmabuf_utils.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
......@@ -32,45 +34,6 @@ namespace {
const int kJpegQuality = 90;
scoped_refptr<media::VideoFrame> ConstructVideoFrame(
std::vector<chromeos_camera::mojom::DmaBufPlanePtr> dma_buf_planes,
media::VideoPixelFormat pixel_format,
int32_t width,
int32_t height) {
size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
if (num_planes != dma_buf_planes.size()) {
DLOG(ERROR) << "The amount of DMA buf planes does not match the format.";
return nullptr;
}
if (width <= 0 || height <= 0) {
DLOG(ERROR) << "Width and height should > 0: " << width << ", " << height;
return nullptr;
}
gfx::Size coded_size(width, height);
gfx::Rect visible_rect(coded_size);
std::vector<base::ScopedFD> dma_buf_fds(num_planes);
std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
for (size_t i = 0; i < num_planes; ++i) {
dma_buf_fds[i] =
mojo::UnwrapPlatformHandle(std::move(dma_buf_planes[i]->fd_handle))
.TakeFD();
planes[i].stride = dma_buf_planes[i]->stride;
planes[i].offset = dma_buf_planes[i]->offset;
planes[i].size = dma_buf_planes[i]->size;
}
auto layout = media::VideoFrameLayout::CreateWithPlanes(
pixel_format, coded_size, std::move(planes));
return media::VideoFrame::WrapExternalDmabufs(
*layout, // layout
visible_rect, // visible_rect
coded_size, // natural_size
std::move(dma_buf_fds), // dmabuf_fds
base::TimeDelta()); // timestamp
}
media::VideoPixelFormat ToVideoPixelFormat(uint32_t fourcc_fmt) {
switch (fourcc_fmt) {
case V4L2_PIX_FMT_NV12:
......@@ -284,7 +247,9 @@ void MojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
int32_t coded_size_height,
EncodeWithDmaBufCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (coded_size_width <= 0 || coded_size_height <= 0) {
const gfx::Size coded_size(coded_size_width, coded_size_height);
if (coded_size.IsEmpty()) {
std::move(callback).Run(
0, ::chromeos_camera::JpegEncodeAccelerator::Status::INVALID_ARGUMENT);
return;
......@@ -303,16 +268,14 @@ void MojoJpegEncodeAcceleratorService::EncodeWithDmaBuf(
}
auto input_video_frame = ConstructVideoFrame(
std::move(input_planes), ToVideoPixelFormat(input_format),
coded_size_width, coded_size_height);
std::move(input_planes), ToVideoPixelFormat(input_format), coded_size);
if (!input_video_frame) {
std::move(callback).Run(
0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
return;
}
auto output_video_frame =
ConstructVideoFrame(std::move(output_planes), media::PIXEL_FORMAT_MJPEG,
coded_size_width, coded_size_height);
auto output_video_frame = ConstructVideoFrame(
std::move(output_planes), media::PIXEL_FORMAT_MJPEG, coded_size);
if (!output_video_frame) {
std::move(callback).Run(
0, ::chromeos_camera::JpegEncodeAccelerator::Status::PLATFORM_FAILURE);
......
......@@ -11,14 +11,23 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/platform_file.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/shared_memory_handle.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/numerics/safe_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/base/bind_to_current_loop.h"
#include "components/chromeos_camera/common/dmabuf.mojom.h"
#include "components/chromeos_camera/dmabuf_utils.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/video_frame.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace {
......@@ -119,15 +128,16 @@ void MojoMjpegDecodeAcceleratorService::Decode(
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
TRACE_EVENT0("jpeg", "MojoMjpegDecodeAcceleratorService::Decode");
DCHECK_EQ(decode_cb_map_.count(input_buffer.id()), 0u);
if (decode_cb_map_.count(input_buffer.id()) != 0) {
DCHECK_EQ(mojo_cb_map_.count(input_buffer.id()), 0u);
if (mojo_cb_map_.count(input_buffer.id()) != 0) {
NotifyDecodeStatus(
input_buffer.id(),
::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
return;
}
decode_cb_map_[input_buffer.id()] = std::move(callback);
mojo_cb_map_[input_buffer.id()] =
base::BindOnce(std::move(callback), input_buffer.id());
if (!VerifyDecodeParams(coded_size, &output_handle, output_buffer_size)) {
NotifyDecodeStatus(
......@@ -185,7 +195,6 @@ void MojoMjpegDecodeAcceleratorService::DecodeWithFD(
mojo::ScopedHandle output_handle,
uint32_t output_buffer_size,
DecodeWithFDCallback callback) {
#if defined(OS_CHROMEOS)
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::PlatformFile input_fd;
base::PlatformFile output_fd;
......@@ -226,9 +235,51 @@ void MojoMjpegDecodeAcceleratorService::DecodeWithFD(
Decode(std::move(in_buffer), coded_size, std::move(output_scoped_handle),
output_buffer_size, std::move(callback));
#else
NOTREACHED();
#endif
}
void MojoMjpegDecodeAcceleratorService::DecodeWithDmaBuf(
int32_t task_id,
mojo::ScopedHandle src_dmabuf_fd,
uint32_t src_size,
uint32_t src_offset,
mojom::DmaBufVideoFramePtr dst_frame,
DecodeWithDmaBufCallback callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
TRACE_EVENT0("jpeg", __FUNCTION__);
if (src_size == 0) {
LOG(ERROR) << "Input buffer size should be positive";
std::move(callback).Run(
::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
return;
}
mojo::PlatformHandle src_handle =
mojo::UnwrapPlatformHandle(std::move(src_dmabuf_fd));
if (!src_handle.is_valid()) {
LOG(ERROR) << "Invalid input DMA-buf FD";
std::move(callback).Run(
::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
return;
}
const gfx::Size coded_size(base::checked_cast<int>(dst_frame->coded_width),
base::checked_cast<int>(dst_frame->coded_height));
scoped_refptr<media::VideoFrame> frame = ConstructVideoFrame(
std::move(dst_frame->planes), dst_frame->format, coded_size);
if (!frame) {
LOG(ERROR) << "Failed to create video frame";
std::move(callback).Run(
::chromeos_camera::MjpegDecodeAccelerator::Error::INVALID_ARGUMENT);
return;
}
DCHECK_EQ(mojo_cb_map_.count(task_id), 0u);
mojo_cb_map_[task_id] = std::move(callback);
DCHECK(accelerator_);
accelerator_->Decode(task_id, src_handle.TakeFD(),
base::strict_cast<size_t>(src_size),
base::strict_cast<off_t>(src_offset), std::move(frame));
}
void MojoMjpegDecodeAcceleratorService::Uninitialize() {
......@@ -241,16 +292,16 @@ void MojoMjpegDecodeAcceleratorService::NotifyDecodeStatus(
::chromeos_camera::MjpegDecodeAccelerator::Error error) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto iter = decode_cb_map_.find(bitstream_buffer_id);
DCHECK(iter != decode_cb_map_.end());
if (iter == decode_cb_map_.end()) {
auto iter = mojo_cb_map_.find(bitstream_buffer_id);
DCHECK(iter != mojo_cb_map_.end());
if (iter == mojo_cb_map_.end()) {
// Silently ignoring abnormal case.
return;
}
DecodeCallback decode_cb = std::move(iter->second);
decode_cb_map_.erase(iter);
std::move(decode_cb).Run(bitstream_buffer_id, error);
MojoCallback mojo_cb = std::move(iter->second);
mojo_cb_map_.erase(iter);
std::move(mojo_cb).Run(error);
}
} // namespace chromeos_camera
......@@ -7,8 +7,10 @@
#include <stdint.h>
#include <map>
#include <memory>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
......@@ -36,7 +38,10 @@ class MojoMjpegDecodeAcceleratorService
::chromeos_camera::MjpegDecodeAccelerator::Error error) override;
private:
using DecodeCallbackMap = std::unordered_map<int32_t, DecodeCallback>;
// A common wrapper type for Mojo callbacks of Decode* functions.
using MojoCallback = base::OnceCallback<void(
::chromeos_camera::MjpegDecodeAccelerator::Error)>;
using MojoCallbackMap = std::map<int32_t, MojoCallback>;
// This constructor internally calls
// GpuMjpegDecodeAcceleratorFactory::GetAcceleratorFactories() to
......@@ -58,6 +63,12 @@ class MojoMjpegDecodeAcceleratorService
mojo::ScopedHandle output_fd,
uint32_t output_buffer_size,
DecodeWithFDCallback callback) override;
void DecodeWithDmaBuf(int32_t task_id,
mojo::ScopedHandle src_dmabuf_fd,
uint32_t src_size,
uint32_t src_offset,
mojom::DmaBufVideoFramePtr dst_frame,
DecodeWithDmaBufCallback callback) override;
void Uninitialize() override;
void NotifyDecodeStatus(
......@@ -67,8 +78,8 @@ class MojoMjpegDecodeAcceleratorService
const std::vector<GpuMjpegDecodeAcceleratorFactory::CreateAcceleratorCB>
accelerator_factory_functions_;
// A map from bitstream_buffer_id to DecodeCallback.
DecodeCallbackMap decode_cb_map_;
// A map from |task_id| to MojoCallback.
MojoCallbackMap mojo_cb_map_;
std::unique_ptr<::chromeos_camera::MjpegDecodeAccelerator> accelerator_;
......
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