blob: c66327964b25c4986d1f74ced97609b52c89fa39 [file] [log] [blame]
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "BufferAdapter"
#include "BufferAdapter.h"
#include <libcamera/base/log.h>
#include <cutils/properties.h>
#include <hardware/hardware.h>
#include <inttypes.h>
#include <memory>
#include <string>
#include <cutils/properties.h>
using namespace libcamera;
LOG_DECLARE_CATEGORY(HAL)
#define HANDLE_EINTR(x) \
({ \
decltype(x) eintr_wrapper_result; \
do \
{ \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
/* This enumeration must match the one in <gralloc_drm.h>.
* The functions supported by this gralloc's temporary private API are listed
* below. Use of these functions is highly discouraged and should only be
* reserved for cases where no alternative to get same information (such as
* querying ANativeWindow) exists.
*/
// clang-format off
enum {
GRALLOC_DRM_GET_STRIDE,
GRALLOC_DRM_GET_FORMAT,
GRALLOC_DRM_GET_DIMENSIONS,
GRALLOC_DRM_GET_BACKING_STORE,
GRALLOC_DRM_GET_BUFFER_INFO,
GRALLOC_DRM_GET_USAGE,
};
namespace {
const struct SupportedFormat {
int halFormat;
// |drmFormat| is set to 0 for multi-planar formats. The actual format will be resolved in
// |SupportedPlanarFormat|.
uint32_t drmFormat;
} kSupportedFormats[] = {
{HAL_PIXEL_FORMAT_BGRA_8888, DRM_FORMAT_ARGB8888},
{HAL_PIXEL_FORMAT_RGBA_8888, DRM_FORMAT_ABGR8888},
{HAL_PIXEL_FORMAT_RGBX_8888, DRM_FORMAT_XBGR8888},
{HAL_PIXEL_FORMAT_YCbCr_422_I, DRM_FORMAT_YUYV},
// There is no exact match for HAL_PIXEL_FORMAT_BLOB in DRM format. Use DRM_FORMAT_R8
// because it is one byte per pixel.
{HAL_PIXEL_FORMAT_BLOB, DRM_FORMAT_R8},
{HAL_PIXEL_FORMAT_YV12, 0},
{HAL_PIXEL_FORMAT_YCbCr_420_888, 0},
{HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 0},
// Add more buffer formats when needed.
};
const struct SupportedPlanarFormat {
int halFormat;
bool crcb;
bool semiplanar;
uint32_t drmFormat;
} kSupportedPlanarFormats[] = {
{HAL_PIXEL_FORMAT_YV12, true, false, DRM_FORMAT_YVU420},
{HAL_PIXEL_FORMAT_YCbCr_420_888, false, false, DRM_FORMAT_YUV420},
{HAL_PIXEL_FORMAT_YCbCr_420_888, true, false, DRM_FORMAT_YVU420},
{HAL_PIXEL_FORMAT_YCbCr_420_888, false, true, DRM_FORMAT_NV12},
{HAL_PIXEL_FORMAT_YCbCr_420_888, true, true, DRM_FORMAT_NV21},
{HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, false, false, DRM_FORMAT_YUV420},
{HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, true, false, DRM_FORMAT_YVU420},
{HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, false, true, DRM_FORMAT_NV12},
{HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, true, true, DRM_FORMAT_NV21},
// Add more buffer formats when needed.
};
static inline int getBpp(uint32_t drmFormat) {
switch (drmFormat) {
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XBGR8888:
return 4;
case DRM_FORMAT_BGR888:
return 3;
case DRM_FORMAT_RGB565:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_MTISP_SXYZW10:
return 2;
case DRM_FORMAT_YVU420:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV12:
case DRM_FORMAT_R8:
return 1;
default:
LOG(HAL, Error) << "Unsupported DRM fourcc " << drmFormat
<< ".Forgot to add switch case here?";
return 0;
}
}
// A helper function to get the singleton instance of the gralloc module.
static const gralloc_module_t* GetGrallocModule() {
static const gralloc_module_t* sGrallocModule;
static std::mutex sGrallocModuleLock;
std::unique_lock<std::mutex> l(sGrallocModuleLock);
if (!sGrallocModule) {
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
reinterpret_cast<const hw_module_t**>(&sGrallocModule))) {
LOG(HAL, Error) << "Failed to initialize gralloc module";
return nullptr;
}
}
return sGrallocModule;
}
} // end of anonymous namespace
namespace android {
camera3_stream_buffer_t BufferAdapter::prepareStreamBufferPtr(const camera3_stream_buffer* buffer) {
std::unique_lock<std::mutex> bufferLock(mBufferHandlesLock);
buffer_handle_t* nativeHandle = buffer->buffer;
// Use the pointer value of buffer_handle_t as buffer ID.
uint64_t bufferId = reinterpret_cast<uint64_t>(nativeHandle);
uint32_t drmFormat = 0;
uint32_t width, height;
uint32_t numPlanes;
std::vector<uint32_t> strides;
std::vector<uint32_t> offsets;
AdaptedHandle &adaptedBuffer = mAdaptedBufferHandles[bufferId];
adaptedBuffer.buffer_handle = reinterpret_cast<buffer_handle_t>(&adaptedBuffer.camera_buffer_handle);
camera_buffer_handle_t *handle_ptr = &adaptedBuffer.camera_buffer_handle;
// Resolve DRM format fourcc from Android format.
int androidFormat;
int ret;
ret = mGrallocModule->perform(mGrallocModule, GRALLOC_DRM_GET_FORMAT, *nativeHandle,
&androidFormat);
if (ret) {
LOG(HAL, Error) << "Failed to get format";
return *buffer;
}
auto value = std::find_if(
std::begin(kSupportedFormats), std::end(kSupportedFormats),
[androidFormat](struct SupportedFormat f) { return f.halFormat == androidFormat; });
if (value == std::end(kSupportedFormats)) {
LOG(HAL, Error) << "Unsupported android format: " << androidFormat;
return *buffer;
}
drmFormat = value->drmFormat;
if (drmFormat) {
// Single-planar buffer.
numPlanes = 1;
uint32_t stride;
ret = mGrallocModule->perform(mGrallocModule, GRALLOC_DRM_GET_STRIDE, *nativeHandle,
&stride);
if (ret) {
LOG(HAL, Error) << "Failed to get stride with format: " << androidFormat;
return *buffer;
}
strides.push_back(stride * getBpp(drmFormat));
offsets.push_back(0);
} else {
// Multi-planar buffer.
drmFormat = resolveFormat(*nativeHandle, androidFormat, buffer->stream->usage, &numPlanes,
&strides, &offsets);
}
ret = mGrallocModule->perform(mGrallocModule, GRALLOC_DRM_GET_DIMENSIONS, *nativeHandle, &width,
&height);
if (ret) {
LOG(HAL, Error) << "Failed to get dimensions with format: " << androidFormat;
return *buffer;
}
const uint32_t numFds = static_cast<uint32_t>((*nativeHandle)->numFds);
if (!strcmp(mGrallocModule->common.name, "CrOS Gralloc")) {
// minigbm cros gralloc always have the first numPlanes count of fds to match each
// plane buffer. The numFds can be (numPlanes + 1) for mapper4 metadata.
if (numFds < numPlanes) {
LOG(HAL, Error) << "numFds(" << numFds << ") must be at least numPlanes(" << numPlanes << ") for CrOS Gralloc";
return *buffer;
}
for (uint32_t i = 0; i < numPlanes; i++) {
int dupFd = HANDLE_EINTR(dup((*nativeHandle)->data[i]));
if (dupFd < 0) {
LOG(HAL, Error) << "Failed to dup buffer fd: " << strerror(errno);
return *buffer;
}
handle_ptr->fds[i] = dupFd;
}
} else if (numFds == 1) {
// We assume all planes use the same fd if there's only one fd in the buffer handle (i.e.
// in drm_gralloc case). We can remove this code path once we have fully transitioned to
// cros_gralloc.
for (size_t i = 0; i < numPlanes; ++i) {
int dupFd = HANDLE_EINTR(dup((*nativeHandle)->data[0]));
if (dupFd == -1) {
LOG(HAL, Error) << "Failed to dup buffer fd: " << strerror(errno);
return *buffer;
}
handle_ptr->fds[i] = dupFd;
}
} else {
if (numFds != numPlanes) {
LOG(HAL, Error) << "numFds does not match the number of planes";
return *buffer;
}
for (size_t i = 0; i < numPlanes; ++i) {
int dupFd = HANDLE_EINTR(dup((*nativeHandle)->data[i]));
if (dupFd == -1) {
LOG(HAL, Error) << "Failed to dup buffer fd: " << strerror(errno);
return *buffer;
}
handle_ptr->fds[i] = dupFd;
}
}
// When we return the buffer in process_capture_result, the frameworks expect to see the
// same buffer_handle_t that it passed down before. Store the received buffer_handle_t
// so we can restore the |buffer| field in camera3_stream_buffer_t to the original value
// later in process_capture_result.
mBufferHandles[bufferId] = nativeHandle;
camera3_stream_buffer_t new_camera3_stream_buffer = {};
new_camera3_stream_buffer = (*buffer);
handle_ptr->buffer_id = bufferId;
handle_ptr->drm_format = drmFormat;
handle_ptr->hal_pixel_format = androidFormat;
handle_ptr->width = width;
handle_ptr->height = height;
std::copy(strides.begin(), strides.end(), handle_ptr->strides);
std::copy(offsets.begin(), offsets.end(), handle_ptr->offsets);
new_camera3_stream_buffer.buffer = &adaptedBuffer.buffer_handle;
return new_camera3_stream_buffer;
}
bool BufferAdapter::init() {
mGrallocModule = GetGrallocModule();
if (!mGrallocModule) {
return false;
}
return true;
}
int BufferAdapter::decodeStreamBufferPtr(const camera3_stream_buffer_t* ptr,
camera3_stream_buffer_t* outBuffer) {
std::unique_lock<std::mutex> bufferLock(mBufferHandlesLock);
camera_buffer_handle_t *reint_cast_from_buffer_handle_t
= const_cast<camera_buffer_handle_t*>(reinterpret_cast<const camera_buffer_handle_t*>(*ptr->buffer));
uint64_t bufferId = reint_cast_from_buffer_handle_t->buffer_id;
if (mBufferHandles.count(bufferId) == 0) {
LOG(HAL, Error) << "Invalid buffer ID: " << reint_cast_from_buffer_handle_t->buffer_id;
return -EINVAL;
}
if (!mBufferHandles.contains(bufferId)) {
LOG(HAL, Error) << "cannot find buffer " << bufferId;
return -EINVAL;
}
outBuffer->buffer = mBufferHandles[bufferId];
for (int i = 0; i < kMaxPlanes; i++) {
if (reint_cast_from_buffer_handle_t->fds[i] == -1) {
continue;
}
if (close(reint_cast_from_buffer_handle_t->fds[i])) {
LOG(HAL, Error) << "Failed to close FD: " << reint_cast_from_buffer_handle_t->fds[i];
} else {
reint_cast_from_buffer_handle_t->fds[i] = -1;
}
}
mBufferHandles.erase(bufferId);
mAdaptedBufferHandles.erase(bufferId);
return 0;
}
uint32_t BufferAdapter::resolveFormat(buffer_handle_t buffer, int androidFormat, uint32_t usage,
uint32_t* outNumPlanes, std::vector<uint32_t>* outStrides,
std::vector<uint32_t>* outOffsets) {
struct android_ycbcr ycbcr;
memset(&ycbcr, 0, sizeof(ycbcr));
// When we call lock_ycbcr() with zero usage, the offsets are filled in |ycbcr.{y|cb|cr}|.
int ret = mGrallocModule->lock_ycbcr(mGrallocModule, buffer, 0, 0, 0, 0, 0, &ycbcr);
if (ret && androidFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
// HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED can be single-planar RGBX8888 or any multi-planar
// YUV 420 formats. Currently we rely on cros_gralloc to only give us YUV 420 formats since
// we don't have a way to tell 420 from 422 with lock_ycbcr(). If we fail to lock_ycbcr()
// the implementation defined buffer then we assume it's RGBX8888; otherwise we leave it to
// resolveFormat to resolve the exact YUV 420 format. (b/32077885)
uint32_t drmFormat = DRM_FORMAT_XBGR8888;
// Try to override it to a private format for reprocessing if usage flags include
// HW_CAMERA_READ.
if (usage & GRALLOC_USAGE_HW_CAMERA_READ) {
uint32_t privateFormat = getPrivateFormat();
if (privateFormat) {
drmFormat = privateFormat;
}
}
*outNumPlanes = 1;
uint32_t stride;
ret = mGrallocModule->perform(mGrallocModule, GRALLOC_DRM_GET_STRIDE, buffer, &stride);
if (ret) {
LOG(HAL, Error) << "Failed to get stride with format: " << androidFormat;
return 0;
}
outStrides->push_back(stride * getBpp(drmFormat));
outOffsets->push_back(0);
return drmFormat;
}
if (ret) {
LOG(HAL, Error) << "Failed to lock_ycbcr with format: " << androidFormat;
return 0;
}
mGrallocModule->unlock(mGrallocModule, buffer);
uint32_t offsets[3] = {0, 0, 0};
uint32_t strides[3] = {0, 0, 0};
offsets[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.y));
offsets[1] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.cb));
offsets[2] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.cr));
strides[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.ystride));
strides[1] = strides[2] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ycbcr.cstride));
bool crcb = false;
if (offsets[1] > offsets[2]) {
crcb = true;
std::swap(offsets[1], offsets[2]);
}
bool semiplanar = true;
*outNumPlanes = 2;
outStrides->push_back(strides[0]);
outStrides->push_back(strides[1]);
outOffsets->push_back(offsets[0]);
outOffsets->push_back(offsets[1]);
if (ycbcr.chroma_step == 1) {
semiplanar = false;
*outNumPlanes = 3;
outStrides->push_back(strides[2]);
outOffsets->push_back(offsets[2]);
}
auto value = std::find_if(
std::begin(kSupportedPlanarFormats), std::end(kSupportedPlanarFormats),
[androidFormat, crcb, semiplanar](struct SupportedPlanarFormat f) {
return f.halFormat == androidFormat && f.crcb == crcb && f.semiplanar == semiplanar;
});
if (value == std::end(kSupportedPlanarFormats)) {
LOG(HAL, Error) << "Unsupported planar format: " << androidFormat << " (crcb=" << crcb << ", semiplanar=" << semiplanar << ")";
return 0;
}
return value->drmFormat;
}
uint32_t BufferAdapter::getPrivateFormat() {
const char* prop_key = "ro.product.device";
std::unique_ptr<char[]> device(new char[PROPERTY_VALUE_MAX]);
if (property_get(prop_key, device.get(), "") == 0) {
LOG(HAL, Error) << "Failed to read system property: " << prop_key;
}
// The value of ro.product.device is ${BOARD}_(cheets|bertha)
// See
// http://cs/chromeos_internal/src/private-overlays/project-cheets-private/scripts/board_specific_setup.sh?l=917&rcl=59fdaf4641708433c5ecdcf34fa15e9e36e5aa7e
std::string device_string(device.get());
size_t pos = device_string.rfind("_");
if (pos == std::string::npos) {
LOG(HAL, Error) << prop_key << " has unexpected format: " << device_string.c_str();
return 1;
}
std::string board = device_string.substr(0, pos);
if (board == "kukui") {
return DRM_FORMAT_MTISP_SXYZW10;
}
return 0;
}
} // end of namespace android