blob: bfb4547c572c7eeaed8a81418d60efb90a6a32da [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/test/vaapi_dmabuf_video_frame_mapper.h"
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "media/gpu/format_utils.h"
#include "media/gpu/vaapi/vaapi_picture_factory.h"
#include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "media/video/picture.h"
#if defined(OS_POSIX)
#include "media/gpu/vaapi/vaapi_picture_native_pixmap.h"
#endif
#define VLOGF(level) VLOG(level) << __func__ << "(): "
namespace media {
namespace test {
namespace {
constexpr VAImageFormat kImageFormatNV12{.fourcc = VA_FOURCC_NV12,
.byte_order = VA_LSB_FIRST,
.bits_per_pixel = 12};
void DeallocateBuffers(std::unique_ptr<ScopedVAImage> va_image) {
// Destructing ScopedVAImage releases its owned memory.
DCHECK(va_image->IsValid());
}
scoped_refptr<VideoFrame> CreateMappedVideoFrame(
const VideoFrameLayout& layout,
const gfx::Rect& visible_rect,
std::unique_ptr<ScopedVAImage> va_image) {
// ScopedVAImage manages the resource of mapped data. That is, ScopedVAImage's
// dtor releases the mapped resource.
const size_t num_planes = layout.num_planes();
if (num_planes != va_image->image()->num_planes) {
VLOGF(1) << "The number of planes is not same between layout and VAImage, "
<< "(layout: " << num_planes
<< ", VAImage: " << va_image->image()->num_planes << ")";
return nullptr;
}
std::vector<int32_t> strides(num_planes, 0);
std::vector<uint8_t*> addrs(num_planes, nullptr);
for (size_t i = 0; i < num_planes; i++) {
strides[i] = va_image->image()->pitches[i];
addrs[i] = static_cast<uint8_t*>(va_image->va_buffer()->data()) +
va_image->image()->offsets[i];
}
auto video_frame = VideoFrame::WrapExternalYuvData(
layout.format(), layout.coded_size(), visible_rect, visible_rect.size(),
strides[0], strides[1], strides[2], addrs[0], addrs[1], addrs[2],
base::TimeDelta());
if (!video_frame)
return nullptr;
video_frame->AddDestructionObserver(
base::BindOnce(DeallocateBuffers, std::move(va_image)));
return video_frame;
}
} // namespace
// static
std::unique_ptr<VideoFrameMapper> VaapiDmaBufVideoFrameMapper::Create() {
auto video_frame_mapper = base::WrapUnique(new VaapiDmaBufVideoFrameMapper);
if (video_frame_mapper->vaapi_wrapper_ == nullptr) {
return nullptr;
}
return video_frame_mapper;
}
// While kDecode and H264PROFILE_MAIN are set here, the mode and profile are
// not required for VaapiWrapper to perform pixel format conversion.
// TODO(crbug.com/898423): Create a VaapiWrapper only for pixel format
// conversion. Either mode or profile isn't required to create the VaapiWrapper.
VaapiDmaBufVideoFrameMapper::VaapiDmaBufVideoFrameMapper()
: vaapi_wrapper_(VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kDecode,
H264PROFILE_MAIN,
base::DoNothing())),
vaapi_picture_factory_(new VaapiPictureFactory()) {}
VaapiDmaBufVideoFrameMapper::~VaapiDmaBufVideoFrameMapper() {}
scoped_refptr<VideoFrame> VaapiDmaBufVideoFrameMapper::Map(
scoped_refptr<VideoFrame> video_frame) const {
DCHECK(vaapi_wrapper_);
DCHECK(vaapi_picture_factory_);
if (!video_frame->HasDmaBufs()) {
return nullptr;
}
if (video_frame->format() != PIXEL_FORMAT_NV12) {
NOTIMPLEMENTED() << " Unsupported PixelFormat: " << video_frame->format();
return nullptr;
}
const gfx::Size& coded_size = video_frame->coded_size();
constexpr int32_t kDummyPictureBufferId = 0;
// Passing empty callbacks is ok, because given PictureBuffer doesn't have
// texture id and thus these callbacks will never called.
auto va_picture = vaapi_picture_factory_->Create(
vaapi_wrapper_, MakeGLContextCurrentCallback(), BindGLImageCallback(),
PictureBuffer(kDummyPictureBufferId, coded_size));
if (!va_picture) {
VLOGF(1) << "Failed to create VaapiPicture.";
return nullptr;
}
gfx::GpuMemoryBufferHandle gmb_handle;
#if defined(OS_POSIX)
gmb_handle =
VaapiPictureNativePixmap::CreateGpuMemoryBufferHandleFromVideoFrame(
video_frame.get());
#endif
if (gmb_handle.is_null()) {
VLOGF(1) << "Failed to CreateGMBHandleFromVideoFrame.";
return nullptr;
}
if (!va_picture->ImportGpuMemoryBufferHandle(
VideoPixelFormatToGfxBufferFormat(video_frame->format()),
std::move(gmb_handle))) {
VLOGF(1) << "Failed in ImportGpuMemoryBufferHandle.";
return nullptr;
}
// Map tiled NV12 buffer by CreateVaImage so that mapped buffers can be
// accessed as non-tiled NV12 buffer.
constexpr VideoPixelFormat kConvertedFormat = PIXEL_FORMAT_NV12;
VAImageFormat va_image_format = kImageFormatNV12;
auto va_image = vaapi_wrapper_->CreateVaImage(
va_picture->va_surface_id(), &va_image_format, video_frame->coded_size());
if (!va_image || !va_image->IsValid()) {
VLOGF(1) << "Failed in CreateVaImage.";
return nullptr;
}
auto layout =
VideoFrameLayout::Create(kConvertedFormat, video_frame->coded_size());
if (!layout) {
VLOGF(1) << "Failed to create VideoFrameLayout.";
return nullptr;
}
return CreateMappedVideoFrame(*layout, video_frame->visible_rect(),
std::move(va_image));
}
} // namespace test
} // namespace media