android: libyuv: Introduce PostProcessorYuv

This adds PostProcessorYuv. It supports NV12 buffer scaling
using libyuv.

Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Hirokazu Honda
2021-02-04 22:04:20 +00:00
committed by Laurent Pinchart
parent 08ce394465
commit 84297540b6
3 changed files with 185 additions and 0 deletions

View File

@@ -49,6 +49,7 @@ android_hal_sources = files([
'jpeg/exif.cpp',
'jpeg/post_processor_jpeg.cpp',
'jpeg/thumbnailer.cpp',
'yuv/post_processor_yuv.cpp'
])
android_camera_metadata_sources = files([

View File

@@ -0,0 +1,142 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2021, Google Inc.
*
* post_processor_yuv.cpp - Post Processor using libyuv
*/
#include "post_processor_yuv.h"
#include <libyuv/scale.h>
#include <libcamera/formats.h>
#include <libcamera/geometry.h>
#include <libcamera/pixel_format.h>
#include "libcamera/internal/formats.h"
#include "libcamera/internal/log.h"
using namespace libcamera;
LOG_DEFINE_CATEGORY(YUV)
int PostProcessorYuv::configure(const StreamConfiguration &inCfg,
const StreamConfiguration &outCfg)
{
if (inCfg.pixelFormat != outCfg.pixelFormat) {
LOG(YUV, Error) << "Pixel format conversion is not supported"
<< " (from " << inCfg.pixelFormat.toString()
<< " to " << outCfg.pixelFormat.toString() << ")";
return -EINVAL;
}
if (inCfg.size < outCfg.size) {
LOG(YUV, Error) << "Up-scaling is not supported"
<< " (from " << inCfg.size.toString()
<< " to " << outCfg.size.toString() << ")";
return -EINVAL;
}
if (inCfg.pixelFormat != formats::NV12) {
LOG(YUV, Error) << "Unsupported format " << inCfg.pixelFormat
<< " (only NV12 is supported)";
return -EINVAL;
}
calculateLengths(inCfg, outCfg);
return 0;
}
int PostProcessorYuv::process(const FrameBuffer &source,
libcamera::MappedBuffer *destination,
[[maybe_unused]] const CameraMetadata &requestMetadata,
[[maybe_unused]] CameraMetadata *metadata)
{
if (!isValidBuffers(source, *destination))
return -EINVAL;
const MappedFrameBuffer sourceMapped(&source, PROT_READ);
if (!sourceMapped.isValid()) {
LOG(YUV, Error) << "Failed to mmap camera frame buffer";
return -EINVAL;
}
int ret = libyuv::NV12Scale(sourceMapped.maps()[0].data(),
sourceStride_[0],
sourceMapped.maps()[1].data(),
sourceStride_[1],
sourceSize_.width, sourceSize_.height,
destination->maps()[0].data(),
destinationStride_[0],
destination->maps()[1].data(),
destinationStride_[1],
destinationSize_.width,
destinationSize_.height,
libyuv::FilterMode::kFilterBilinear);
if (ret) {
LOG(YUV, Error) << "Failed NV12 scaling: " << ret;
return -EINVAL;
}
return 0;
}
bool PostProcessorYuv::isValidBuffers(const FrameBuffer &source,
const libcamera::MappedBuffer &destination) const
{
if (source.planes().size() != 2) {
LOG(YUV, Error) << "Invalid number of source planes: "
<< source.planes().size();
return false;
}
if (destination.maps().size() != 2) {
LOG(YUV, Error) << "Invalid number of destination planes: "
<< destination.maps().size();
return false;
}
if (source.planes()[0].length < sourceLength_[0] ||
source.planes()[1].length < sourceLength_[1]) {
LOG(YUV, Error)
<< "The source planes lengths are too small, actual size: {"
<< source.planes()[0].length << ", "
<< source.planes()[1].length
<< "}, expected size: {"
<< sourceLength_[0] << ", "
<< sourceLength_[1] << "}";
return false;
}
if (destination.maps()[0].size() < destinationLength_[0] ||
destination.maps()[1].size() < destinationLength_[1]) {
LOG(YUV, Error)
<< "The destination planes lengths are too small, actual size: {"
<< destination.maps()[0].size() << ", "
<< destination.maps()[1].size()
<< "}, expected size: {"
<< sourceLength_[0] << ", "
<< sourceLength_[1] << "}";
return false;
}
return true;
}
void PostProcessorYuv::calculateLengths(const StreamConfiguration &inCfg,
const StreamConfiguration &outCfg)
{
sourceSize_ = inCfg.size;
destinationSize_ = outCfg.size;
const PixelFormatInfo &nv12Info = PixelFormatInfo::info(formats::NV12);
for (unsigned int i = 0; i < 2; i++) {
sourceStride_[i] = inCfg.stride;
destinationStride_[i] = nv12Info.stride(destinationSize_.width, i, 1);
const unsigned int vertSubSample =
nv12Info.planes[i].verticalSubSampling;
sourceLength_[i] = sourceStride_[i] *
((sourceSize_.height + vertSubSample - 1) / vertSubSample);
destinationLength_[i] = destinationStride_[i] *
((destinationSize_.height + vertSubSample - 1) / vertSubSample);
}
}

View File

@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2021, Google Inc.
*
* post_processor_yuv.h - Post Processor using libyuv
*/
#ifndef __ANDROID_POST_PROCESSOR_YUV_H__
#define __ANDROID_POST_PROCESSOR_YUV_H__
#include "../post_processor.h"
#include <libcamera/geometry.h>
class CameraDevice;
class PostProcessorYuv : public PostProcessor
{
public:
PostProcessorYuv() = default;
int configure(const libcamera::StreamConfiguration &incfg,
const libcamera::StreamConfiguration &outcfg) override;
int process(const libcamera::FrameBuffer &source,
libcamera::MappedBuffer *destination,
const CameraMetadata &requestMetadata,
CameraMetadata *metadata) override;
private:
bool isValidBuffers(const libcamera::FrameBuffer &source,
const libcamera::MappedBuffer &destination) const;
void calculateLengths(const libcamera::StreamConfiguration &inCfg,
const libcamera::StreamConfiguration &outCfg);
libcamera::Size sourceSize_;
libcamera::Size destinationSize_;
unsigned int sourceLength_[2] = {};
unsigned int destinationLength_[2] = {};
unsigned int sourceStride_[2] = {};
unsigned int destinationStride_[2] = {};
};
#endif /* __ANDROID_POST_PROCESSOR_YUV_H__ */