blob: 8c1c7886f891246d6c9d23ed958d6016ed2be415 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/chromeos/libyuv_image_processor_backend.h"
#include <sys/mman.h>
#include "base/containers/contains.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/checked_math.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "media/gpu/chromeos/fourcc.h"
#include "media/gpu/chromeos/video_frame_resource.h"
#include "media/gpu/macros.h"
#include "media/gpu/video_frame_mapper.h"
#include "media/gpu/video_frame_mapper_factory.h"
#include "third_party/libyuv/include/libyuv/convert.h"
#include "third_party/libyuv/include/libyuv/convert_from.h"
#include "third_party/libyuv/include/libyuv/convert_from_argb.h"
#include "third_party/libyuv/include/libyuv/scale.h"
namespace media {
namespace {
enum class SupportResult {
Supported,
SupportedWithI420Pivot,
SupportedWithNV12Pivot,
Unsupported,
};
enum class Transform {
kConversion,
kScaling,
};
static constexpr struct {
uint32_t input;
uint32_t output;
Transform transform;
SupportResult support_result;
} kSupportFormatConversionArray[] = {
#define CONV(in, out, trans, result) \
{Fourcc::in, Fourcc::out, Transform::trans, SupportResult::result}
// Conversion.
#if BUILDFLAG(IS_LINUX)
CONV(NV12, AR24, kConversion, Supported),
#endif
CONV(NV12, NV12, kConversion, Supported),
CONV(YM16, NV12, kConversion, Supported),
CONV(YM16, YU12, kConversion, Supported),
CONV(YU12, NV12, kConversion, Supported),
CONV(YU12, YU12, kConversion, Supported),
CONV(YUYV, NV12, kConversion, Supported),
CONV(YUYV, YU12, kConversion, Supported),
CONV(YV12, NV12, kConversion, Supported),
CONV(MM21, NV12, kConversion, Supported),
CONV(MT2T, P010, kConversion, Supported),
// Scaling.
CONV(NV12, NV12, kScaling, Supported),
CONV(YM16, NV12, kScaling, SupportedWithNV12Pivot),
CONV(YM16, YU12, kScaling, SupportedWithI420Pivot),
CONV(YU12, YU12, kScaling, Supported),
CONV(YUYV, NV12, kScaling, SupportedWithNV12Pivot),
CONV(YUYV, YU12, kScaling, SupportedWithI420Pivot),
#undef CONV
};
SupportResult IsConversionSupported(Fourcc input_fourcc,
Fourcc output_fourcc,
Transform transform) {
const auto single_input_fourcc = input_fourcc.ToSinglePlanar();
const auto single_output_fourcc = output_fourcc.ToSinglePlanar();
if (!single_input_fourcc || !single_output_fourcc)
return SupportResult::Unsupported;
// Compare fourccs by formatting single planar formats because LibyuvIP can
// process either single- or multi-planar formats.
for (const auto& conv : kSupportFormatConversionArray) {
const auto conv_input_fourcc = Fourcc::FromUint32(conv.input);
const auto conv_output_fourcc = Fourcc::FromUint32(conv.output);
if (!conv_input_fourcc || !conv_output_fourcc)
continue;
const auto single_conv_input_fourcc = conv_input_fourcc->ToSinglePlanar();
const auto single_conv_output_fourcc = conv_output_fourcc->ToSinglePlanar();
if (!single_conv_input_fourcc || !single_conv_output_fourcc)
continue;
if (single_input_fourcc == single_conv_input_fourcc &&
single_output_fourcc == single_conv_output_fourcc &&
transform == conv.transform) {
return conv.support_result;
}
}
return SupportResult::Unsupported;
}
} // namespace
// static
std::unique_ptr<ImageProcessorBackend> LibYUVImageProcessorBackend::Create(
const PortConfig& input_config,
const PortConfig& output_config,
OutputMode output_mode,
ErrorCB error_cb) {
return CreateWithTaskRunner(input_config, output_config, output_mode,
error_cb,
base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_VISIBLE}));
}
// static
std::unique_ptr<ImageProcessorBackend>
LibYUVImageProcessorBackend::CreateWithTaskRunner(
const PortConfig& input_config,
const PortConfig& output_config,
OutputMode output_mode,
ErrorCB error_cb,
scoped_refptr<base::SequencedTaskRunner> backend_task_runner) {
VLOGF(2);
DCHECK_EQ(output_mode, OutputMode::IMPORT)
<< "Only OutputMode::IMPORT supported";
if (!gfx::Rect(input_config.size).Contains(input_config.visible_rect)) {
VLOGF(1) << "Input size should contain input visible rect.";
return nullptr;
}
if (!gfx::Rect(output_config.size).Contains(output_config.visible_rect)) {
VLOGF(1) << "Output size should contain output visible rect.";
return nullptr;
}
// LibYUVImageProcessorBackend supports only memory-based video frame for
// input.
std::unique_ptr<VideoFrameMapper> input_frame_mapper;
if (input_config.storage_type == VideoFrame::STORAGE_DMABUFS ||
input_config.storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
// The LibYUVImageProcessorBackend is not currently used to read from
// Intel media compressed buffers, so we don't need the VideoFrameMapper
// to support those.
input_frame_mapper = VideoFrameMapperFactory::CreateMapper(
input_config.fourcc.ToVideoPixelFormat(), input_config.storage_type,
/*force_linear_buffer_mapper=*/true,
/*must_support_intel_media_compressed_buffers=*/false);
}
if (!input_frame_mapper &&
!VideoFrame::IsStorageTypeMappable(input_config.storage_type)) {
VLOGF(2) << "Unsupported input storage type";
return nullptr;
}
std::unique_ptr<VideoFrameMapper> output_frame_mapper;
if (output_config.storage_type == VideoFrame::STORAGE_DMABUFS ||
output_config.storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
// The LibYUVImageProcessorBackend is not currently used to write onto
// Intel media compressed buffers, so we don't need the VideoFrameMapper
// to support those.
output_frame_mapper = VideoFrameMapperFactory::CreateMapper(
output_config.fourcc.ToVideoPixelFormat(), output_config.storage_type,
/*force_linear_buffer_mapper=*/true,
/*must_support_intel_media_compressed_buffers=*/false);
}
if (!output_frame_mapper &&
!VideoFrame::IsStorageTypeMappable(output_config.storage_type)) {
VLOGF(2) << "Unsupported output storage type";
return nullptr;
}
const gfx::Size& input_size = input_config.visible_rect.size();
const gfx::Size& output_size = output_config.visible_rect.size();
Transform transform = Transform::kConversion;
if (input_size != output_size) {
transform = Transform::kScaling;
}
SupportResult res = IsConversionSupported(input_config.fourcc,
output_config.fourcc, transform);
if (res == SupportResult::Unsupported) {
VLOGF(2) << "Conversion from " << input_size.ToString() << "/"
<< input_config.fourcc.ToString() << " to "
<< output_size.ToString() << "/" << output_config.fourcc.ToString()
<< " is not supported";
return nullptr;
}
scoped_refptr<FrameResource> intermediate_frame;
if (res == SupportResult::SupportedWithI420Pivot ||
res == SupportResult::SupportedWithNV12Pivot) {
intermediate_frame = VideoFrameResource::Create(VideoFrame::CreateFrame(
res == SupportResult::SupportedWithI420Pivot ? PIXEL_FORMAT_I420
: PIXEL_FORMAT_NV12,
input_config.visible_rect.size(),
gfx::Rect(input_config.visible_rect.size()),
input_config.visible_rect.size(), base::TimeDelta()));
if (!intermediate_frame) {
VLOGF(1) << "Failed to create intermediate frame";
return nullptr;
}
}
// This intermediate frame is only needed in the event of a crop operation
// that does not start with the upper left corner at (0,0). Tiled formats such
// as MM21 can not easily be converted starting at arbitrary origins. With
// this intermediate buffer the tiled format can be converted to a linear
// format that can be easily cropped.
scoped_refptr<FrameResource> crop_intermediate_frame;
if (input_config.visible_rect.origin() != gfx::Point(0, 0) &&
(input_config.fourcc == Fourcc(Fourcc::MM21) ||
input_config.fourcc == Fourcc(Fourcc::MT2T))) {
if (transform != Transform::kScaling) {
crop_intermediate_frame =
VideoFrameResource::Create(VideoFrame::CreateFrame(
output_config.fourcc.ToVideoPixelFormat(), input_config.size,
input_config.visible_rect, input_config.size, base::TimeDelta()));
if (!crop_intermediate_frame) {
VLOGF(1) << "Failed to create cropping intermediate frame";
return nullptr;
}
} else {
VLOGF(1) << "Scaling and cropping simultaneously are not supported for "
"MM21/M2T2.";
return nullptr;
}
}
auto processor =
base::WrapUnique<ImageProcessorBackend>(new LibYUVImageProcessorBackend(
std::move(input_frame_mapper), std::move(output_frame_mapper),
std::move(intermediate_frame), std::move(crop_intermediate_frame),
input_config, output_config, OutputMode::IMPORT, std::move(error_cb),
std::move(backend_task_runner)));
VLOGF(2) << "LibYUVImageProcessorBackend created for converting from "
<< input_config.ToString() << " to " << output_config.ToString();
return processor;
}
LibYUVImageProcessorBackend::LibYUVImageProcessorBackend(
std::unique_ptr<VideoFrameMapper> input_frame_mapper,
std::unique_ptr<VideoFrameMapper> output_frame_mapper,
scoped_refptr<FrameResource> intermediate_frame,
scoped_refptr<FrameResource> crop_intermediate_frame,
const PortConfig& input_config,
const PortConfig& output_config,
OutputMode output_mode,
ErrorCB error_cb,
scoped_refptr<base::SequencedTaskRunner> backend_task_runner)
: ImageProcessorBackend(input_config,
output_config,
output_mode,
std::move(error_cb),
std::move(backend_task_runner)),
input_frame_mapper_(std::move(input_frame_mapper)),
output_frame_mapper_(std::move(output_frame_mapper)),
intermediate_frame_(std::move(intermediate_frame)),
crop_intermediate_frame_(std::move(crop_intermediate_frame)) {}
LibYUVImageProcessorBackend::~LibYUVImageProcessorBackend() {
DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
}
std::string LibYUVImageProcessorBackend::type() const {
return "LibYUVImageProcessor";
}
void LibYUVImageProcessorBackend::ProcessFrame(
scoped_refptr<FrameResource> input_frame,
scoped_refptr<FrameResource> output_frame,
FrameResourceReadyCB cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
DVLOGF(4);
if (input_frame->storage_type() == VideoFrame::STORAGE_DMABUFS ||
input_frame->storage_type() == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
DCHECK_NE(input_frame_mapper_.get(), nullptr);
int mapping_permissions = PROT_READ;
if (input_frame->storage_type() != VideoFrame::STORAGE_DMABUFS)
mapping_permissions |= PROT_WRITE;
scoped_refptr<VideoFrame> mapped_input_frame =
input_frame_mapper_->MapFrame(input_frame, mapping_permissions);
if (!mapped_input_frame) {
VLOGF(1) << "Failed to map input FrameResource";
error_cb_.Run();
return;
}
input_frame = VideoFrameResource::Create(std::move(mapped_input_frame));
}
// We don't replace |output_frame| with a mapped frame, because |output_frame|
// is the output of ImageProcessor.
scoped_refptr<FrameResource> mapped_frame = output_frame;
if (output_frame->storage_type() == VideoFrame::STORAGE_DMABUFS ||
output_frame->storage_type() == VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
DCHECK_NE(output_frame_mapper_.get(), nullptr);
scoped_refptr<VideoFrame> mapped_output_frame =
output_frame_mapper_->MapFrame(output_frame, PROT_READ | PROT_WRITE);
if (!mapped_output_frame) {
VLOGF(1) << "Failed to map output FrameResource";
error_cb_.Run();
return;
}
mapped_frame = VideoFrameResource::Create(std::move(mapped_output_frame));
}
int res;
{
TRACE_EVENT2("media", "LibYUVImageProcessorBackend::Process", "input_frame",
input_frame->AsHumanReadableString(), "output_frame",
mapped_frame->AsHumanReadableString());
SCOPED_UMA_HISTOGRAM_TIMER("LibYUVImageProcessorBackend::Process");
if (input_config_.visible_rect.origin() == gfx::Point(0, 0) ||
(input_config_.fourcc != Fourcc(Fourcc::MM21) &&
input_config_.fourcc != Fourcc(Fourcc::MT2T))) {
res = DoConversion(input_frame.get(), mapped_frame.get());
} else {
res = DoConversion(input_frame.get(), crop_intermediate_frame_.get());
// This is cropping routine that should be able to support any (known)
// pixel format).
if (!res) {
for (size_t plane = 0;
plane < VideoFrame::NumPlanes(crop_intermediate_frame_->format());
plane++) {
const uint8_t* src_row_ptr =
crop_intermediate_frame_->visible_data(plane);
uint8_t* dst_row_ptr = mapped_frame->GetWritableVisibleData(plane);
for (size_t row = 0;
row < VideoFrame::Rows(
plane, crop_intermediate_frame_->format(),
crop_intermediate_frame_->visible_rect().height());
row++) {
memcpy(dst_row_ptr, src_row_ptr,
VideoFrame::Columns(
plane, crop_intermediate_frame_->format(),
crop_intermediate_frame_->visible_rect().width()) *
VideoFrame::BytesPerElement(
crop_intermediate_frame_->format(), plane));
src_row_ptr += crop_intermediate_frame_->row_bytes(plane);
dst_row_ptr += mapped_frame->row_bytes(plane);
}
}
}
}
}
if (res != 0) {
VLOGF(1) << "libyuv returns non-zero code: " << res;
error_cb_.Run();
return;
}
output_frame->set_timestamp(input_frame->timestamp());
output_frame->set_color_space(input_frame->ColorSpace());
std::move(cb).Run(std::move(output_frame));
}
int LibYUVImageProcessorBackend::DoConversion(const FrameResource* const input,
FrameResource* const output) {
DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
#define Y_U_V_DATA(fr) \
fr->visible_data(VideoFrame::kYPlane), fr->stride(VideoFrame::kYPlane), \
fr->visible_data(VideoFrame::kUPlane), fr->stride(VideoFrame::kUPlane), \
fr->visible_data(VideoFrame::kVPlane), fr->stride(VideoFrame::kVPlane)
#define Y_U_V_DATA_W(fr) \
fr->GetWritableVisibleData(VideoFrame::kYPlane), \
fr->stride(VideoFrame::kYPlane), \
fr->GetWritableVisibleData(VideoFrame::kUPlane), \
fr->stride(VideoFrame::kUPlane), \
fr->GetWritableVisibleData(VideoFrame::kVPlane), \
fr->stride(VideoFrame::kVPlane)
#define Y_V_U_DATA(fr) \
fr->visible_data(VideoFrame::kYPlane), fr->stride(VideoFrame::kYPlane), \
fr->visible_data(VideoFrame::kVPlane), fr->stride(VideoFrame::kVPlane), \
fr->visible_data(VideoFrame::kUPlane), fr->stride(VideoFrame::kUPlane)
#define Y_UV_DATA(fr) \
fr->visible_data(VideoFrame::kYPlane), fr->stride(VideoFrame::kYPlane), \
fr->visible_data(VideoFrame::kUVPlane), fr->stride(VideoFrame::kUVPlane)
#define Y_UV_DATA_W(fr) \
fr->GetWritableVisibleData(VideoFrame::kYPlane), \
fr->stride(VideoFrame::kYPlane), \
fr->GetWritableVisibleData(VideoFrame::kUVPlane), \
fr->stride(VideoFrame::kUVPlane)
#define YUY2_DATA(fr) \
fr->visible_data(VideoFrame::kYPlane), fr->stride(VideoFrame::kYPlane)
#define Y_UV_DATA_10BIT(fr) \
reinterpret_cast<const uint16_t*>(fr->visible_data(VideoFrame::kYPlane)), \
fr->stride(VideoFrame::kYPlane), \
reinterpret_cast<const uint16_t*>( \
fr->visible_data(VideoFrame::kUVPlane)), \
fr->stride(VideoFrame::kUVPlane)
#define Y_UV_DATA_W_10BIT(fr) \
reinterpret_cast<uint16_t*>( \
fr->GetWritableVisibleData(VideoFrame::kYPlane)), \
fr->stride(VideoFrame::kYPlane), \
reinterpret_cast<uint16_t*>( \
fr->GetWritableVisibleData(VideoFrame::kUVPlane)), \
fr->stride(VideoFrame::kUVPlane)
#if BUILDFLAG(IS_LINUX)
#define ARGB_DATA(fr) \
fr->GetWritableVisibleData(VideoFrame::kARGBPlane), \
fr->stride(VideoFrame::kARGBPlane)
#endif
#define LIBYUV_FUNC(func, i, o) \
libyuv::func(i, o, output->visible_rect().width(), \
output->visible_rect().height())
if (output->format() == PIXEL_FORMAT_NV12) {
switch (input->format()) {
case PIXEL_FORMAT_I420:
return LIBYUV_FUNC(I420ToNV12, Y_U_V_DATA(input), Y_UV_DATA_W(output));
case PIXEL_FORMAT_YV12:
return LIBYUV_FUNC(I420ToNV12, Y_V_U_DATA(input), Y_UV_DATA_W(output));
case PIXEL_FORMAT_NV12:
// MM21 mode.
if (input_config_.fourcc == Fourcc(Fourcc::MM21)) {
return libyuv::MM21ToNV12(input->data(VideoFrame::kYPlane),
input->stride(VideoFrame::kYPlane),
input->data(VideoFrame::kUVPlane),
input->stride(VideoFrame::kUVPlane),
output->writable_data(VideoFrame::kYPlane),
output->stride(VideoFrame::kYPlane),
output->writable_data(VideoFrame::kUVPlane),
output->stride(VideoFrame::kUVPlane),
std::min(output->coded_size().width(),
input->coded_size().width()),
std::min(output->coded_size().height(),
input->coded_size().height()));
}
// Scaling mode.
return libyuv::NV12Scale(
Y_UV_DATA(input), input->visible_rect().width(),
input->visible_rect().height(), Y_UV_DATA_W(output),
output->visible_rect().width(), output->visible_rect().height(),
libyuv::kFilterBilinear);
case PIXEL_FORMAT_YUY2:
if (input->visible_rect().size() == output->visible_rect().size()) {
return LIBYUV_FUNC(YUY2ToNV12, YUY2_DATA(input), Y_UV_DATA_W(output));
} else {
DCHECK_EQ(intermediate_frame_->format(), PIXEL_FORMAT_NV12);
int ret = libyuv::YUY2ToNV12(
YUY2_DATA(input), Y_UV_DATA_W(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height());
if (ret != 0)
return ret;
return libyuv::NV12Scale(
Y_UV_DATA(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height(), Y_UV_DATA_W(output),
output->visible_rect().width(), output->visible_rect().height(),
libyuv::kFilterBilinear);
}
case PIXEL_FORMAT_I422:
if (input->visible_rect().size() == output->visible_rect().size()) {
return LIBYUV_FUNC(I422ToNV21, Y_V_U_DATA(input),
Y_UV_DATA_W(output));
} else {
DCHECK_EQ(intermediate_frame_->format(), PIXEL_FORMAT_NV12);
int ret = libyuv::I422ToNV21(
Y_V_U_DATA(input), Y_UV_DATA_W(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height());
if (ret != 0)
return ret;
return libyuv::NV12Scale(
Y_UV_DATA(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height(), Y_UV_DATA_W(output),
output->visible_rect().width(), output->visible_rect().height(),
libyuv::kFilterBilinear);
}
default:
VLOGF(1) << "Unexpected input format: " << input->format();
return -1;
}
}
if (output->format() == PIXEL_FORMAT_I420) {
switch (input->format()) {
case PIXEL_FORMAT_I420:
return libyuv::I420Scale(
Y_U_V_DATA(input), input->visible_rect().width(),
input->visible_rect().height(), Y_U_V_DATA_W(output),
output->visible_rect().width(), output->visible_rect().height(),
libyuv::kFilterBilinear);
case PIXEL_FORMAT_YUY2:
if (input->visible_rect().size() == output->visible_rect().size()) {
return LIBYUV_FUNC(YUY2ToI420, YUY2_DATA(input),
Y_U_V_DATA_W(output));
} else {
DCHECK_EQ(intermediate_frame_->format(), PIXEL_FORMAT_I420);
int ret = libyuv::YUY2ToI420(
YUY2_DATA(input), Y_U_V_DATA_W(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height());
if (ret != 0)
return ret;
return libyuv::I420Scale(
Y_U_V_DATA(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height(),
Y_U_V_DATA_W(output), output->visible_rect().width(),
output->visible_rect().height(), libyuv::kFilterBilinear);
}
case PIXEL_FORMAT_I422:
if (input->visible_rect().size() == output->visible_rect().size()) {
return LIBYUV_FUNC(I422ToI420, Y_U_V_DATA(input),
Y_U_V_DATA_W(output));
} else {
DCHECK_EQ(intermediate_frame_->format(), PIXEL_FORMAT_I420);
int ret = libyuv::I422ToI420(
Y_U_V_DATA(input), Y_U_V_DATA_W(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height());
if (ret != 0)
return ret;
return libyuv::I420Scale(
Y_U_V_DATA(intermediate_frame_),
intermediate_frame_->visible_rect().width(),
intermediate_frame_->visible_rect().height(),
Y_U_V_DATA_W(output), output->visible_rect().width(),
output->visible_rect().height(), libyuv::kFilterBilinear);
}
default:
VLOGF(1) << "Unexpected input format: " << input->format();
return -1;
}
}
if (output->format() == PIXEL_FORMAT_P016LE) {
if (input_config_.fourcc == Fourcc(Fourcc::MT2T)) {
// stride is 5/4 because MT2T is a packed 10bit format
const uint32_t src_stride_mt2t =
(input->stride(VideoFrame::kYPlane) * 5) >> 2;
int libyuv_result = libyuv::MT2TToP010(
input->visible_data(VideoFrame::kYPlane), src_stride_mt2t,
input->visible_data(VideoFrame::kUVPlane), src_stride_mt2t,
reinterpret_cast<uint16_t*>(
output->GetWritableVisibleData(VideoFrame::kYPlane)),
output->stride(VideoFrame::kYPlane) >> 1,
reinterpret_cast<uint16_t*>(
output->GetWritableVisibleData(VideoFrame::kUVPlane)),
output->stride(VideoFrame::kUVPlane) >> 1,
output->visible_rect().width(), output->visible_rect().height());
if (libyuv_result) {
return libyuv_result;
}
return 0;
}
}
#if BUILDFLAG(IS_LINUX)
if (output->format() == PIXEL_FORMAT_ARGB) {
if (input_config_.fourcc == Fourcc(Fourcc::NV12)) {
return LIBYUV_FUNC(NV12ToARGB, Y_UV_DATA(input),
ARGB_DATA(output));
}
}
#endif
#undef Y_U_V_DATA
#undef Y_V_U_DATA
#undef Y_UV_DATA
#undef LIBYUV_FUNC
VLOGF(1) << "Unexpected output format: " << output->format();
return -1;
}
bool LibYUVImageProcessorBackend::needs_linear_output_buffers() const {
return true;
}
std::vector<Fourcc> LibYUVImageProcessorBackend::GetSupportedOutputFormats(
Fourcc input_format) {
std::vector<Fourcc> supported_formats;
for (const auto& conv : kSupportFormatConversionArray) {
if (Fourcc::FromUint32(conv.input) &&
*Fourcc::FromUint32(conv.input) == input_format &&
Fourcc::FromUint32(conv.output))
supported_formats.emplace_back(*Fourcc::FromUint32(conv.output));
}
return supported_formats;
}
bool LibYUVImageProcessorBackend::supports_incoherent_buffers() const {
return true;
}
} // namespace media