Commit 2badf012 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Commit Bot

media/gpu/v4l2vda: move some IP-related code to helper class

As we are adding ImageProcessor support to the V4L2SVDA, quite a bit of
code is going to be similar to the one already present in V4L2VDA. In
order to limit code duplication, move the most obvious bits into a
helper class for VDA which both classes can use.

Bug: b:132589320
Test: vdaunittest and vdatests pass on Minnie.
Change-Id: I6b0594b705acfd8f112335c9a56b3c262a5bca78
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1731510
Commit-Queue: Alexandre Courbot <acourbot@chromium.org>
Reviewed-by: default avatarHirokazu Honda <hiroh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#685463}
parent 4c023309
......@@ -43,6 +43,8 @@ source_set("v4l2") {
"v4l2_slice_video_decoder.h",
"v4l2_stateful_workaround.cc",
"v4l2_stateful_workaround.h",
"v4l2_vda_helpers.cc",
"v4l2_vda_helpers.h",
"v4l2_video_decode_accelerator.cc",
"v4l2_video_decode_accelerator.h",
"v4l2_video_encode_accelerator.cc",
......
// 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 "media/gpu/v4l2/v4l2_vda_helpers.h"
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/v4l2_device.h"
#include "media/gpu/v4l2/v4l2_image_processor.h"
namespace media {
namespace v4l2_vda_helpers {
namespace {
base::Optional<VideoFrameLayout> CreateLayout(uint32_t fourcc,
const gfx::Size& size) {
// V4L2 specific format hack:
// If VDA's output format is V4L2_PIX_FMT_MT21C, which is a platform specific
// format and now is only used for MT8173 VDA output and its image processor
// input, we set VideoFrameLayout for image processor's input with format
// PIXEL_FORMAT_NV12 as NV12's layout is the same as MT21.
if (fourcc == V4L2_PIX_FMT_MT21C) {
size_t num_planes = 2;
return VideoFrameLayout::CreateMultiPlanar(
PIXEL_FORMAT_NV12, size,
std::vector<VideoFrameLayout::Plane>(num_planes));
} else {
VideoPixelFormat pixel_format =
V4L2Device::V4L2PixFmtToVideoPixelFormat(fourcc);
if (pixel_format == PIXEL_FORMAT_UNKNOWN)
return base::nullopt;
size_t num_planes = VideoFrame::NumPlanes(pixel_format);
if (num_planes == 1) {
return VideoFrameLayout::Create(pixel_format, size);
} else {
return VideoFrameLayout::CreateMultiPlanar(
pixel_format, size, std::vector<VideoFrameLayout::Plane>(num_planes));
}
}
}
} // namespace
uint32_t FindImageProcessorInputFormat(V4L2Device* vda_device) {
std::vector<uint32_t> processor_input_formats =
V4L2ImageProcessor::GetSupportedInputFormats();
struct v4l2_fmtdesc fmtdesc = {};
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
while (vda_device->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
if (std::find(processor_input_formats.begin(),
processor_input_formats.end(),
fmtdesc.pixelformat) != processor_input_formats.end()) {
DVLOGF(3) << "Image processor input format=" << fmtdesc.description;
return fmtdesc.pixelformat;
}
++fmtdesc.index;
}
return 0;
}
uint32_t FindImageProcessorOutputFormat(V4L2Device* ip_device) {
// Prefer YVU420 and NV12 because ArcGpuVideoDecodeAccelerator only supports
// single physical plane.
static constexpr uint32_t kPreferredFormats[] = {V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YVU420};
auto preferred_formats_first = [](uint32_t a, uint32_t b) -> bool {
auto* iter_a = std::find(std::begin(kPreferredFormats),
std::end(kPreferredFormats), a);
auto* iter_b = std::find(std::begin(kPreferredFormats),
std::end(kPreferredFormats), b);
return iter_a < iter_b;
};
std::vector<uint32_t> processor_output_formats =
V4L2ImageProcessor::GetSupportedOutputFormats();
// Move the preferred formats to the front.
std::sort(processor_output_formats.begin(), processor_output_formats.end(),
preferred_formats_first);
for (uint32_t processor_output_format : processor_output_formats) {
if (ip_device->CanCreateEGLImageFrom(processor_output_format)) {
DVLOGF(3) << "Image processor output format=" << processor_output_format;
return processor_output_format;
}
}
return 0;
}
std::unique_ptr<ImageProcessor> CreateImageProcessor(
uint32_t vda_output_format,
uint32_t ip_output_format,
const gfx::Size& vda_output_coded_size,
const gfx::Size& ip_output_coded_size,
const gfx::Size& visible_size,
size_t nb_buffers,
scoped_refptr<V4L2Device> image_processor_device,
ImageProcessor::OutputMode image_processor_output_mode,
ImageProcessor::ErrorCB error_cb) {
base::Optional<VideoFrameLayout> input_layout =
CreateLayout(vda_output_format, vda_output_coded_size);
if (!input_layout) {
VLOGF(1) << "Invalid input layout";
return nullptr;
}
base::Optional<VideoFrameLayout> output_layout =
CreateLayout(ip_output_format, ip_output_coded_size);
if (!output_layout) {
VLOGF(1) << "Invalid output layout";
return nullptr;
}
// TODO(crbug.com/917798): Use ImageProcessorFactory::Create() once we remove
// |image_processor_device_| from V4L2VideoDecodeAccelerator.
auto image_processor = V4L2ImageProcessor::Create(
image_processor_device,
ImageProcessor::PortConfig(*input_layout, vda_output_format, visible_size,
{VideoFrame::STORAGE_DMABUFS}),
ImageProcessor::PortConfig(*output_layout, visible_size,
{VideoFrame::STORAGE_DMABUFS}),
image_processor_output_mode, nb_buffers, std::move(error_cb));
if (!image_processor)
return nullptr;
if (image_processor->output_layout().coded_size() != ip_output_coded_size) {
VLOGF(1) << "Image processor should be able to use the requested output "
<< "coded size " << ip_output_coded_size.ToString()
<< " without adjusting to "
<< image_processor->output_layout().coded_size().ToString();
return nullptr;
}
if (image_processor->input_layout().coded_size() != vda_output_coded_size) {
VLOGF(1) << "Image processor should be able to take the output coded "
<< "size of decoder " << vda_output_coded_size.ToString()
<< " without adjusting to "
<< image_processor->input_layout().coded_size().ToString();
return nullptr;
}
return image_processor;
}
} // namespace v4l2_vda_helpers
} // namespace media
// 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 MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
#define MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "media/gpu/image_processor.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class V4L2Device;
// Helper static methods to be shared between V4L2VideoDecodeAccelerator and
// V4L2SliceVideoDecodeAccelerator. This avoids some code duplication between
// these very similar classes.
// Note: this namespace can be removed once the V4L2VDA is deprecated.
namespace v4l2_vda_helpers {
// Returns a usable input format of image processor. Return 0 if not found.
uint32_t FindImageProcessorInputFormat(V4L2Device* vda_device);
// Return a usable output format of image processor. Return 0 if not found.
uint32_t FindImageProcessorOutputFormat(V4L2Device* ip_device);
// Create and return an image processor for the given parameters, or nullptr
// if it cannot be created.
//
// |vda_output_format| is the output format of the VDA, i.e. the IP's input
// format.
// |ip_output_format| is the output format that the IP must produce.
// |vda_output_coded_size| is the coded size of the VDA output buffers (i.e.
// the input coded size for the IP).
// |ip_output_coded_size| is the coded size of the output buffers that the IP
// must produce.
// |visible_size| is the visible size of both the input and output buffers.
// |nb_buffers| is the exact number of output buffers that the IP must create.
// |image_processor_output_mode| specifies whether the IP must allocate its
// own buffers or rely on imported ones.
// |error_cb| is the error callback passed to V4L2ImageProcessor::Create().
std::unique_ptr<ImageProcessor> CreateImageProcessor(
uint32_t vda_output_format,
uint32_t ip_output_format,
const gfx::Size& vda_output_coded_size,
const gfx::Size& ip_output_coded_size,
const gfx::Size& visible_size,
size_t nb_buffers,
scoped_refptr<V4L2Device> image_processor_device,
ImageProcessor::OutputMode image_processor_output_mode,
ImageProcessor::ErrorCB error_cb);
} // namespace v4l2_vda_helpers
} // namespace media
#endif // MEDIA_GPU_V4L2_V4L2_VDA_HELPERS_H_
......@@ -35,6 +35,7 @@
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/v4l2_image_processor.h"
#include "media/gpu/v4l2/v4l2_stateful_workaround.h"
#include "media/gpu/v4l2/v4l2_vda_helpers.h"
#include "media/video/h264_parser.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gl/gl_context.h"
......@@ -2328,12 +2329,14 @@ bool V4L2VideoDecodeAccelerator::SetupFormats() {
VLOGF(1) << "Image processor not available";
return false;
}
output_format_fourcc_ = FindImageProcessorInputFormat();
output_format_fourcc_ =
v4l2_vda_helpers::FindImageProcessorInputFormat(device_.get());
if (output_format_fourcc_ == 0) {
VLOGF(1) << "Can't find a usable input format from image processor";
return false;
}
egl_image_format_fourcc_ = FindImageProcessorOutputFormat();
egl_image_format_fourcc_ =
v4l2_vda_helpers::FindImageProcessorOutputFormat(device_.get());
if (egl_image_format_fourcc_ == 0) {
VLOGF(1) << "Can't find a usable output format from image processor";
return false;
......@@ -2361,55 +2364,6 @@ bool V4L2VideoDecodeAccelerator::SetupFormats() {
return true;
}
uint32_t V4L2VideoDecodeAccelerator::FindImageProcessorInputFormat() {
std::vector<uint32_t> processor_input_formats =
V4L2ImageProcessor::GetSupportedInputFormats();
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
while (device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
if (std::find(processor_input_formats.begin(),
processor_input_formats.end(),
fmtdesc.pixelformat) != processor_input_formats.end()) {
DVLOGF(3) << "Image processor input format=" << fmtdesc.description;
return fmtdesc.pixelformat;
}
++fmtdesc.index;
}
return 0;
}
uint32_t V4L2VideoDecodeAccelerator::FindImageProcessorOutputFormat() {
// Prefer YVU420 and NV12 because ArcGpuVideoDecodeAccelerator only supports
// single physical plane.
static const uint32_t kPreferredFormats[] = {V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YVU420};
auto preferred_formats_first = [](uint32_t a, uint32_t b) -> bool {
auto* iter_a = std::find(std::begin(kPreferredFormats),
std::end(kPreferredFormats), a);
auto* iter_b = std::find(std::begin(kPreferredFormats),
std::end(kPreferredFormats), b);
return iter_a < iter_b;
};
std::vector<uint32_t> processor_output_formats =
V4L2ImageProcessor::GetSupportedOutputFormats();
// Move the preferred formats to the front.
std::sort(processor_output_formats.begin(), processor_output_formats.end(),
preferred_formats_first);
for (uint32_t processor_output_format : processor_output_formats) {
if (device_->CanCreateEGLImageFrom(processor_output_format)) {
DVLOGF(3) << "Image processor output format=" << processor_output_format;
return processor_output_format;
}
}
return 0;
}
bool V4L2VideoDecodeAccelerator::ResetImageProcessor() {
VLOGF(2);
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
......@@ -2431,80 +2385,23 @@ bool V4L2VideoDecodeAccelerator::CreateImageProcessor() {
(output_mode_ == Config::OutputMode::ALLOCATE
? ImageProcessor::OutputMode::ALLOCATE
: ImageProcessor::OutputMode::IMPORT);
size_t num_planes = 0;
base::Optional<VideoFrameLayout> input_layout;
// V4L2 specific format hack:
// If VDA's output format is V4L2_PIX_FMT_MT21C, which is a platform specific
// format and now is only used for MT8173 VDA output and its image processor
// input, we set VideoFrameLayout for image processor's input with format
// PIXEL_FORMAT_NV12 as NV12's layout is the same as MT21.
if (output_format_fourcc_ == V4L2_PIX_FMT_MT21C) {
num_planes = 2;
input_layout = VideoFrameLayout::CreateMultiPlanar(
PIXEL_FORMAT_NV12, coded_size_,
std::vector<VideoFrameLayout::Plane>(num_planes));
} else {
num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(output_format_fourcc_);
if (num_planes == 1) {
input_layout = VideoFrameLayout::Create(
V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc_),
coded_size_);
} else {
input_layout = VideoFrameLayout::CreateMultiPlanar(
V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc_),
coded_size_, std::vector<VideoFrameLayout::Plane>(num_planes));
}
}
if (!input_layout) {
VLOGF(1) << "Invalid input layout";
return false;
}
base::Optional<VideoFrameLayout> output_layout;
num_planes = V4L2Device::GetNumPlanesOfV4L2PixFmt(egl_image_format_fourcc_);
if (num_planes == 1) {
output_layout = VideoFrameLayout::Create(
V4L2Device::V4L2PixFmtToVideoPixelFormat(egl_image_format_fourcc_),
egl_image_size_);
} else {
output_layout = VideoFrameLayout::CreateMultiPlanar(
V4L2Device::V4L2PixFmtToVideoPixelFormat(egl_image_format_fourcc_),
egl_image_size_, std::vector<VideoFrameLayout::Plane>(num_planes));
}
if (!output_layout) {
VLOGF(1) << "Invalid output layout";
return false;
}
// Unretained(this) is safe for ErrorCB because |decoder_thread_| is owned by
// this V4L2VideoDecodeAccelerator and |this| must be valid when ErrorCB is
// executed.
// TODO(crbug.com/917798): Use ImageProcessorFactory::Create() once we remove
// |image_processor_device_| from V4L2VideoDecodeAccelerator.
image_processor_ = V4L2ImageProcessor::Create(
image_processor_device_,
ImageProcessor::PortConfig(*input_layout, output_format_fourcc_,
visible_size_, {VideoFrame::STORAGE_DMABUFS}),
ImageProcessor::PortConfig(*output_layout, visible_size_,
{VideoFrame::STORAGE_DMABUFS}),
image_processor_output_mode, output_buffer_map_.size(),
image_processor_ = v4l2_vda_helpers::CreateImageProcessor(
output_format_fourcc_, egl_image_format_fourcc_, coded_size_,
egl_image_size_, visible_size_, output_buffer_map_.size(),
image_processor_device_, image_processor_output_mode,
// Unretained(this) is safe for ErrorCB because |decoder_thread_| is owned
// by this V4L2VideoDecodeAccelerator and |this| must be valid when
// ErrorCB is executed.
base::BindRepeating(&V4L2VideoDecodeAccelerator::ImageProcessorError,
base::Unretained(this)));
if (!image_processor_) {
VLOGF(1) << "Initialize image processor failed";
NOTIFY_ERROR(PLATFORM_FAILURE);
return false;
}
DCHECK(image_processor_->output_layout().coded_size() == egl_image_size_);
if (image_processor_->input_layout().coded_size() != coded_size_) {
VLOGF(1) << "Image processor should be able to take the output coded "
<< "size of decoder " << coded_size_.ToString()
<< " without adjusting to "
<< image_processor_->input_layout().coded_size().ToString();
VLOGF(1) << "Error creating image processor";
NOTIFY_ERROR(PLATFORM_FAILURE);
return false;
}
return true;
}
......
......@@ -391,10 +391,6 @@ class MEDIA_GPU_EXPORT V4L2VideoDecodeAccelerator
// Set input and output formats before starting decode.
bool SetupFormats();
// Return a usable input format of image processor. Return 0 if not found.
uint32_t FindImageProcessorInputFormat();
// Return a usable output format of image processor. Return 0 if not found.
uint32_t FindImageProcessorOutputFormat();
// Reset image processor and drop all processing frames.
bool ResetImageProcessor();
......
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