summaryrefslogtreecommitdiff
path: root/src/android/yuv
AgeCommit message (Expand)Author
2021-09-21android: yuv: Remove CameraDevice forward-declareUmang Jain
2021-09-07libcamera: formats: Add planeSize() helpers to PixelFormatInfoLaurent Pinchart
2021-08-31libcamera: mapped_framebuffer: Rename maps() to planes()Hirokazu Honda
2021-08-10libcamera: MappedFrameBuffer: Use typed Flags<MapModes>Kieran Bingham
2021-08-10libcamera: Give MappedFrameBuffer its own implementationKieran Bingham
2021-06-25libcamera/base: Move extended base functionalityKieran Bingham
2021-03-03android: post_processor: Use CameraBuffer APIJacopo Mondi
2021-02-19android: libyuv: Introduce PostProcessorYuvHirokazu Honda
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* 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/base/log.h>

#include <libcamera/formats.h>
#include <libcamera/geometry.h>
#include <libcamera/pixel_format.h>

#include "libcamera/internal/formats.h"
#include "libcamera/internal/mapped_framebuffer.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
				<< " to " << outCfg.pixelFormat << ")";
		return -EINVAL;
	}

	if (inCfg.size < outCfg.size) {
		LOG(YUV, Error) << "Up-scaling is not supported"
				<< " (from " << inCfg.size
				<< " to " << outCfg.size << ")";
		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;
}

void PostProcessorYuv::process(Camera3RequestDescriptor::StreamBuffer *streamBuffer)
{
	const FrameBuffer &source = *streamBuffer->srcBuffer;
	CameraBuffer *destination = streamBuffer->dstBuffer.get();

	if (!isValidBuffers(source, *destination)) {
		processComplete.emit(streamBuffer, PostProcessor::Status::Error);
		return;
	}

	const MappedFrameBuffer sourceMapped(&source, MappedFrameBuffer::MapFlag::Read);
	if (!sourceMapped.isValid()) {
		LOG(YUV, Error) << "Failed to mmap camera frame buffer";
		processComplete.emit(streamBuffer, PostProcessor::Status::Error);
		return;
	}

	int ret = libyuv::NV12Scale(sourceMapped.planes()[0].data(),
				    sourceStride_[0],
				    sourceMapped.planes()[1].data(),
				    sourceStride_[1],
				    sourceSize_.width, sourceSize_.height,
				    destination->plane(0).data(),
				    destinationStride_[0],
				    destination->plane(1).data(),
				    destinationStride_[1],
				    destinationSize_.width,
				    destinationSize_.height,
				    libyuv::FilterMode::kFilterBilinear);
	if (ret) {
		LOG(YUV, Error) << "Failed NV12 scaling: " << ret;
		processComplete.emit(streamBuffer, PostProcessor::Status::Error);
		return;
	}

	processComplete.emit(streamBuffer, PostProcessor::Status::Success);
}

bool PostProcessorYuv::isValidBuffers(const FrameBuffer &source,
				      const CameraBuffer &destination) const
{
	if (source.planes().size() != 2) {
		LOG(YUV, Error) << "Invalid number of source planes: "
				<< source.planes().size();
		return false;
	}
	if (destination.numPlanes() != 2) {
		LOG(YUV, Error) << "Invalid number of destination planes: "
				<< destination.numPlanes();
		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.plane(0).size() < destinationLength_[0] ||
	    destination.plane(1).size() < destinationLength_[1]) {
		LOG(YUV, Error)
			<< "The destination planes lengths are too small, actual size: {"
			<< destination.plane(0).size() << ", "
			<< destination.plane(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);

		sourceLength_[i] = nv12Info.planeSize(sourceSize_.height, i,
						      sourceStride_[i]);
		destinationLength_[i] = nv12Info.planeSize(destinationSize_.height, i,
							   destinationStride_[i]);
	}
}