blob: 6e7ea836e50b59e92bb7c79ec014983e9e934ad1 [file] [log] [blame]
// Copyright 2018 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/gpu/linux/platform_video_frame_utils.h"
#include "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/scoped_file.h"
#include "build/build_config.h"
#include "media/base/color_plane_layout.h"
#include "media/base/format_utils.h"
#include "media/base/scopedfd_helper.h"
#include "media/base/video_frame_layout.h"
#include "media/gpu/buffer_validation.h"
#include "media/gpu/macros.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap.h"
#if defined(OS_LINUX)
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#endif // defined(OS_LINUX)
namespace media {
namespace {
#if defined(OS_LINUX)
scoped_refptr<VideoFrame> CreateVideoFrameGpu(
gpu::GpuMemoryBufferFactory* factory,
VideoPixelFormat pixel_format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp,
gfx::BufferUsage buffer_usage) {
DCHECK(factory);
auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
if (!buffer_format)
return nullptr;
static base::AtomicSequenceNumber buffer_id_generator;
auto gmb_handle = factory->CreateGpuMemoryBuffer(
gfx::GpuMemoryBufferId(buffer_id_generator.GetNext()), coded_size,
*buffer_format, buffer_usage, gpu::kPlatformVideoFramePoolClientId,
gfx::kNullAcceleratedWidget);
if (gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP)
return nullptr;
DCHECK_EQ(VideoFrame::NumPlanes(pixel_format),
gmb_handle.native_pixmap_handle.planes.size());
std::vector<ColorPlaneLayout> planes;
for (const auto& plane : gmb_handle.native_pixmap_handle.planes)
planes.emplace_back(plane.stride, plane.offset, plane.size);
auto layout = VideoFrameLayout::CreateWithPlanes(
pixel_format, coded_size, std::move(planes),
VideoFrameLayout::kBufferAddressAlignment,
gmb_handle.native_pixmap_handle.modifier);
if (!layout)
return nullptr;
std::vector<base::ScopedFD> dmabuf_fds;
for (const auto& plane : gmb_handle.native_pixmap_handle.planes) {
int duped_fd = HANDLE_EINTR(dup(plane.fd.get()));
if (duped_fd == -1) {
DLOG(ERROR) << "Failed duplicating dmabuf fd";
return nullptr;
}
dmabuf_fds.emplace_back(duped_fd);
}
auto frame = VideoFrame::WrapExternalDmabufs(
*layout, visible_rect, visible_rect.size(), std::move(dmabuf_fds),
timestamp);
if (!frame)
return nullptr;
// Created |gmb_handle| must be owned by |frame|.
frame->AddDestructionObserver(
base::BindOnce(base::DoNothing::Once<gfx::GpuMemoryBufferHandle>(),
std::move(gmb_handle)));
// We also need to have the factory drop its reference to the native pixmap.
frame->AddDestructionObserver(
base::BindOnce(&gpu::GpuMemoryBufferFactory::DestroyGpuMemoryBuffer,
base::Unretained(factory), gmb_handle.id,
gpu::kPlatformVideoFramePoolClientId));
return frame;
}
#endif // defined(OS_LINUX)
} // namespace
scoped_refptr<VideoFrame> CreatePlatformVideoFrame(
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
VideoPixelFormat pixel_format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp,
gfx::BufferUsage buffer_usage) {
#if defined(OS_LINUX)
return CreateVideoFrameGpu(gpu_memory_buffer_factory, pixel_format,
coded_size, visible_rect, natural_size, timestamp,
buffer_usage);
#endif // defined(OS_LINUX)
NOTREACHED();
return nullptr;
}
base::Optional<VideoFrameLayout> GetPlatformVideoFrameLayout(
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
VideoPixelFormat pixel_format,
const gfx::Size& coded_size,
gfx::BufferUsage buffer_usage) {
// |visible_rect| and |natural_size| do not matter here. |coded_size| is set
// as a dummy variable.
auto frame = CreatePlatformVideoFrame(
gpu_memory_buffer_factory, pixel_format, coded_size,
gfx::Rect(coded_size), coded_size, base::TimeDelta(), buffer_usage);
return frame ? base::make_optional<VideoFrameLayout>(frame->layout())
: base::nullopt;
}
gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle(
const VideoFrame* video_frame) {
DCHECK(video_frame);
gfx::GpuMemoryBufferHandle handle;
switch (video_frame->storage_type()) {
case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
handle = video_frame->GetGpuMemoryBuffer()->CloneHandle();
break;
case VideoFrame::STORAGE_DMABUFS: {
handle.type = gfx::NATIVE_PIXMAP;
std::vector<base::ScopedFD> duped_fds =
DuplicateFDs(video_frame->DmabufFds());
const size_t num_planes = VideoFrame::NumPlanes(video_frame->format());
DCHECK_EQ(video_frame->layout().planes().size(), num_planes);
handle.native_pixmap_handle.modifier = video_frame->layout().modifier();
for (size_t i = 0; i < num_planes; ++i) {
const auto& plane = video_frame->layout().planes()[i];
handle.native_pixmap_handle.planes.emplace_back(
plane.stride, plane.offset, plane.size, std::move(duped_fds[i]));
}
} break;
default:
NOTREACHED() << "Unsupported storage type: "
<< video_frame->storage_type();
}
if (!handle.is_null() && handle.type == gfx::NATIVE_PIXMAP &&
!VerifyGpuMemoryBufferHandle(video_frame->format(),
video_frame->coded_size(), handle)) {
VLOGF(1) << "Created GpuMemoryBufferHandle is invalid";
}
return handle;
}
scoped_refptr<gfx::NativePixmapDmaBuf> CreateNativePixmapDmaBuf(
const VideoFrame* video_frame) {
DCHECK(video_frame);
// Create a native pixmap from the frame's memory buffer handle.
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle =
CreateGpuMemoryBufferHandle(video_frame);
if (gpu_memory_buffer_handle.is_null() ||
gpu_memory_buffer_handle.type != gfx::NATIVE_PIXMAP) {
VLOGF(1) << "Failed to create native GpuMemoryBufferHandle";
return nullptr;
}
auto buffer_format =
VideoPixelFormatToGfxBufferFormat(video_frame->layout().format());
if (!buffer_format) {
VLOGF(1) << "Unexpected video frame format";
return nullptr;
}
auto native_pixmap = base::MakeRefCounted<gfx::NativePixmapDmaBuf>(
video_frame->coded_size(), *buffer_format,
std::move(gpu_memory_buffer_handle.native_pixmap_handle));
DCHECK(native_pixmap->AreDmaBufFdsValid());
return native_pixmap;
}
} // namespace media