1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// Copyright 2019 The Chromium Authors
// 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 <unistd.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/color_plane_layout.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
#include "media/gpu/buffer_validation.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/rect.h"
namespace chromeos_camera {
bool VerifyMjpegBufferHandle(const gfx::GpuMemoryBufferHandle& gmb_handle) {
if (gmb_handle.native_pixmap_handle().planes[0].offset != 0u) {
DLOG(ERROR) << "Invalid DMA buf plane offset";
return false;
}
// For MJPEG, we expect the byte size to be at least as large as the stride
// (see b/142105578).
if (base::strict_cast<uint64_t>(
gmb_handle.native_pixmap_handle().planes[0].stride) >
gmb_handle.native_pixmap_handle().planes[0].size) {
DLOG(ERROR) << "Invalid DMA buf plane stride or size";
return false;
}
const auto dma_buf_fd = gmb_handle.native_pixmap_handle().planes[0].fd.get();
const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END);
if (data_size == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to get the size of the dma-buf";
return false;
}
if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to reset the file offset of the dma-buf";
return false;
}
if (!base::IsValueInRangeForNumericType<uint64_t>(data_size) ||
base::checked_cast<uint64_t>(data_size) <
gmb_handle.native_pixmap_handle().planes[0].size) {
DLOG(ERROR) << "Invalid DMA buf plane size";
return false;
}
return true;
}
scoped_refptr<media::VideoFrame> ConstructVideoFrame(
std::vector<mojom::DmaBufPlanePtr> dma_buf_planes,
media::VideoPixelFormat pixel_format,
const gfx::Size& coded_size,
uint64_t modifier) {
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);
gfx::NativePixmapHandle native_pixmap_handle;
native_pixmap_handle.planes.resize(num_planes);
native_pixmap_handle.modifier = modifier;
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;
}
if (dma_buf_planes[i]->stride <= 0) {
DLOG(ERROR) << "Invalid DMA buf stride";
return nullptr;
}
native_pixmap_handle.planes[i].stride =
base::checked_cast<uint32_t>(dma_buf_planes[i]->stride);
native_pixmap_handle.planes[i].offset =
base::strict_cast<uint64_t>(dma_buf_planes[i]->offset);
native_pixmap_handle.planes[i].size =
base::strict_cast<uint64_t>(dma_buf_planes[i]->size);
native_pixmap_handle.planes[i].fd = handle.TakeFD();
}
// TODO(dcheng): It's a bit silly to move this into a GpuMemoryBufferHandle
// just so it can be verified, only to extract it again after this.
gfx::GpuMemoryBufferHandle gmb_handle(std::move(native_pixmap_handle));
if (pixel_format == media::PIXEL_FORMAT_MJPEG) {
if (!VerifyMjpegBufferHandle(gmb_handle)) {
return nullptr;
}
} else {
if (!media::VerifyGpuMemoryBufferHandle(pixel_format, coded_size,
gmb_handle)) {
return nullptr;
}
}
native_pixmap_handle = std::move(gmb_handle).native_pixmap_handle();
std::vector<base::ScopedFD> dma_buf_fds(num_planes);
std::vector<media::ColorPlaneLayout> planes(num_planes);
for (size_t i = 0; i < num_planes; ++i) {
dma_buf_fds[i] = std::move(native_pixmap_handle.planes[i].fd);
planes[i] = media::ColorPlaneLayout(
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 std::optional<media::VideoFrameLayout> layout =
media::VideoFrameLayout::CreateWithPlanes(
pixel_format, coded_size, std::move(planes),
media::VideoFrameLayout::kBufferAddressAlignment, modifier);
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
|