| /* |
| * Copyright 2024 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "camera_buffer_manager_impl.h" |
| |
| #include <cerrno> |
| #include <drm_fourcc.h> |
| #include <gbm.h> |
| #include <map> |
| #include <sys/mman.h> |
| #include <utility> |
| #include <vector> |
| |
| #include <linux/videodev2.h> |
| |
| #include <libcamera/base/log.h> |
| |
| #include <hardware/gralloc.h> |
| #include <system/graphics.h> |
| |
| #include "allocator.h" |
| #include "camera_buffer_handle.h" |
| #include "minigbm_allocator.h" |
| |
| using namespace libcamera; |
| |
| LOG_DECLARE_CATEGORY(HAL) |
| |
| // A private gralloc usage flag to force allocation of YUV420 buffer. This |
| // usage flag is only valid when allocating HAL_PIXEL_FORMAT_YCbCr_420_888 |
| // flexible YUV buffers. |
| const uint32_t GRALLOC_USAGE_FORCE_I420 = 0x10000000U; |
| |
| template<typename T> |
| class NoDestructor |
| { |
| public: |
| static_assert(!(std::is_trivially_constructible_v<T> && |
| std::is_trivially_destructible_v<T>), |
| "T is trivially constructible and destructible; please use a " |
| "constinit object of type T directly instead"); |
| |
| static_assert( |
| !std::is_trivially_destructible_v<T>, |
| "T is trivially destructible; please use a function-local static " |
| "of type T directly instead"); |
| |
| // Not constexpr; just write static constexpr T x = ...; if the value should |
| // be a constexpr. |
| template<typename... Args> |
| explicit NoDestructor(Args &&...args) |
| { |
| new (storage_) T(std::forward<Args>(args)...); |
| } |
| |
| // Allows copy and move construction of the contained type, to allow |
| // construction from an initializer list, e.g. for std::vector. |
| explicit NoDestructor(const T &x) { new (storage_) T(x); } |
| explicit NoDestructor(T &&x) { new (storage_) T(std::move(x)); } |
| |
| NoDestructor(const NoDestructor &) = delete; |
| NoDestructor &operator=(const NoDestructor &) = delete; |
| |
| ~NoDestructor() = default; |
| |
| const T &operator*() const { return *get(); } |
| T &operator*() { return *get(); } |
| |
| const T *operator->() const { return get(); } |
| T *operator->() { return get(); } |
| |
| const T *get() const { return reinterpret_cast<const T *>(storage_); } |
| T *get() { return reinterpret_cast<T *>(storage_); } |
| |
| private: |
| alignas(T) char storage_[sizeof(T)]; |
| |
| #if defined(LEAK_SANITIZER) |
| // TODO(crbug.com/40562930): This is a hack to work around the fact |
| // that LSan doesn't seem to treat NoDestructor as a root for reachability |
| // analysis. This means that code like this: |
| // static base::NoDestructor<std::vector<int>> v({1, 2, 3}); |
| // is considered a leak. Using the standard leak sanitizer annotations to |
| // suppress leaks doesn't work: std::vector is implicitly constructed before |
| // calling the base::NoDestructor constructor. |
| // |
| // Unfortunately, I haven't been able to demonstrate this issue in simpler |
| // reproductions: until that's resolved, hold an explicit pointer to the |
| // placement-new'd object in leak sanitizer mode to help LSan realize that |
| // objects allocated by the contained type are still reachable. |
| T *storage_ptr_ = reinterpret_cast<T *>(storage_); |
| #endif // defined(LEAK_SANITIZER) |
| }; |
| |
| namespace crosLibcamera { |
| |
| namespace { |
| |
| inline std::string FormatToString(int32_t format) |
| { |
| return std::string(reinterpret_cast<char *>(&format), 4); |
| } |
| |
| std::map<uint32_t, std::vector<uint32_t>> kSupportedHalFormats{ |
| { HAL_PIXEL_FORMAT_BLOB, { DRM_FORMAT_R8 } }, |
| { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, |
| { DRM_FORMAT_NV12, DRM_FORMAT_XBGR8888, DRM_FORMAT_MTISP_SXYZW10 } }, |
| { HAL_PIXEL_FORMAT_RGBX_8888, { DRM_FORMAT_XBGR8888 } }, |
| { HAL_PIXEL_FORMAT_RGBA_8888, { DRM_FORMAT_ABGR8888 } }, |
| { HAL_PIXEL_FORMAT_YCbCr_420_888, { DRM_FORMAT_NV12 } }, |
| // Map to DRM_FORMAT_ABGR8888 because DRM_FORMAT_VYUY or DRM_FORMAT_YUYV is |
| // not generally supported by minigbm. |
| { HAL_PIXEL_FORMAT_YCbCr_422_I, { DRM_FORMAT_ABGR8888 } }, |
| { HAL_PIXEL_FORMAT_YCBCR_P010, { DRM_FORMAT_P010 } }, |
| { HAL_PIXEL_FORMAT_Y8, { DRM_FORMAT_R8 } }, |
| }; |
| |
| uint32_t GetGbmUseFlags(uint32_t hal_format, uint32_t hal_usage) |
| { |
| uint32_t gbm_flags = 0; |
| if (hal_format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || |
| !(hal_usage & GRALLOC_USAGE_HW_CAMERA_READ)) { |
| // The default GBM flags for non-private-reprocessing camera buffers. |
| gbm_flags = GBM_BO_USE_SW_READ_OFTEN | GBM_BO_USE_SW_WRITE_OFTEN; |
| } |
| |
| if (hal_usage & GRALLOC_USAGE_HW_CAMERA_READ) { |
| gbm_flags |= GBM_BO_USE_CAMERA_READ; |
| } |
| if (hal_usage & GRALLOC_USAGE_HW_CAMERA_WRITE) { |
| gbm_flags |= GBM_BO_USE_CAMERA_WRITE; |
| } |
| if (hal_usage & GRALLOC_USAGE_HW_TEXTURE) { |
| gbm_flags |= GBM_BO_USE_TEXTURING; |
| } |
| if (hal_usage & GRALLOC_USAGE_HW_RENDER) { |
| gbm_flags |= GBM_BO_USE_RENDERING; |
| } |
| if (hal_usage & GRALLOC_USAGE_HW_COMPOSER) { |
| gbm_flags |= GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; |
| } |
| if (hal_usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) { |
| gbm_flags |= GBM_BO_USE_HW_VIDEO_ENCODER; |
| } |
| return gbm_flags; |
| } |
| |
| bool IsMatchingFormat(int32_t hal_pixel_format, uint32_t drm_format) |
| { |
| switch (hal_pixel_format) { |
| case HAL_PIXEL_FORMAT_RGBA_8888: |
| return drm_format == DRM_FORMAT_ABGR8888; |
| case HAL_PIXEL_FORMAT_RGBX_8888: |
| return drm_format == DRM_FORMAT_XBGR8888; |
| case HAL_PIXEL_FORMAT_BGRA_8888: |
| return drm_format == DRM_FORMAT_ARGB8888; |
| case HAL_PIXEL_FORMAT_YCrCb_420_SP: |
| return drm_format == DRM_FORMAT_NV21; |
| case HAL_PIXEL_FORMAT_YCbCr_422_I: |
| return drm_format == DRM_FORMAT_ABGR8888; |
| case HAL_PIXEL_FORMAT_BLOB: |
| return drm_format == DRM_FORMAT_R8; |
| case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: |
| // We can't really check implementation defined formats. |
| return true; |
| case HAL_PIXEL_FORMAT_YCbCr_420_888: |
| return (drm_format == DRM_FORMAT_YUV420 || |
| drm_format == DRM_FORMAT_YVU420 || |
| drm_format == DRM_FORMAT_NV21 || drm_format == DRM_FORMAT_NV12); |
| case HAL_PIXEL_FORMAT_YV12: |
| return drm_format == DRM_FORMAT_YVU420; |
| } |
| return false; |
| } |
| |
| size_t GetChromaStep(uint32_t drm_format) |
| { |
| switch (drm_format) { |
| case DRM_FORMAT_P010: |
| return 4; |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_NV21: |
| return 2; |
| case DRM_FORMAT_YUV420: |
| case DRM_FORMAT_YVU420: |
| return 1; |
| } |
| return 0; |
| } |
| |
| uint8_t *GetPlaneAddr(const android_ycbcr &ycbcr, |
| uint32_t drm_format, |
| size_t plane) |
| { |
| void *result = nullptr; |
| if (plane == 0) { |
| result = ycbcr.y; |
| } else if (plane == 1) { |
| switch (drm_format) { |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_P010: |
| case DRM_FORMAT_YUV420: |
| result = ycbcr.cb; |
| break; |
| |
| case DRM_FORMAT_NV21: |
| case DRM_FORMAT_YVU420: |
| result = ycbcr.cr; |
| break; |
| } |
| } else if (plane == 2) { |
| switch (drm_format) { |
| case DRM_FORMAT_YUV420: |
| result = ycbcr.cr; |
| break; |
| |
| case DRM_FORMAT_YVU420: |
| result = ycbcr.cb; |
| break; |
| } |
| } |
| if (result == nullptr) { |
| LOG(HAL, Error) << "Unsupported DRM pixel format: " |
| << FormatToString(drm_format); |
| } |
| return reinterpret_cast<uint8_t *>(result); |
| } |
| |
| } // namespace |
| |
| void BufferHandleDeleter::operator()(buffer_handle_t *handle) |
| { |
| if (handle) { |
| auto *buf_mgr = crosLibcamera::CameraBufferManager::GetInstance(); |
| if (buf_mgr && *handle != nullptr) { |
| buf_mgr->Free(*handle); |
| } |
| delete handle; |
| } |
| } |
| |
| // |
| // ScopedMapping implementations. |
| // |
| |
| ScopedMapping::ScopedMapping(buffer_handle_t buffer) |
| : buf_(buffer) |
| { |
| for (size_t i = 0; i < num_planes(); ++i) { |
| planes_[i] = { |
| .stride = CameraBufferManager::GetPlaneStride(buf_, i), |
| .size = CameraBufferManager::GetPlaneSize(buf_, i), |
| }; |
| } |
| auto *buf_mgr = CameraBufferManager::GetInstance(); |
| if (buf_mgr->Register(buf_) != 0) { |
| LOG(HAL, Error) << "Cannot register buffer"; |
| Invalidate(); |
| return; |
| } |
| if (num_planes() == 1) { |
| void **addr_to_void = reinterpret_cast<void **>(&planes_[0].addr); |
| if (buf_mgr->Lock(buf_, 0, 0, 0, width(), height(), addr_to_void) != 0) { |
| LOG(HAL, Error) << "Cannot Lock buffer"; |
| Invalidate(); |
| return; |
| } |
| } else { |
| android_ycbcr ycbcr = {}; |
| if (buf_mgr->LockYCbCr(buf_, 0, 0, 0, width(), height(), &ycbcr) != 0) { |
| LOG(HAL, Error) << "Cannot Lock buffer"; |
| Invalidate(); |
| return; |
| } |
| for (size_t i = 0; i < num_planes(); ++i) { |
| planes_[i].addr = GetPlaneAddr(ycbcr, drm_format(), i); |
| } |
| } |
| } |
| |
| ScopedMapping::~ScopedMapping() |
| { |
| Invalidate(); |
| } |
| |
| ScopedMapping::ScopedMapping(ScopedMapping &&other) |
| { |
| *this = std::move(other); |
| } |
| |
| ScopedMapping &ScopedMapping::operator=(ScopedMapping &&other) |
| { |
| if (this != &other) { |
| Invalidate(); |
| planes_ = other.planes_; |
| buf_ = other.buf_; |
| other.planes_.fill(Plane()); |
| other.buf_ = nullptr; |
| } |
| return *this; |
| } |
| |
| uint32_t ScopedMapping::width() const |
| { |
| return CameraBufferManager::GetWidth(buf_); |
| } |
| |
| uint32_t ScopedMapping::height() const |
| { |
| return CameraBufferManager::GetHeight(buf_); |
| } |
| |
| uint32_t ScopedMapping::drm_format() const |
| { |
| return CameraBufferManager::GetDrmPixelFormat(buf_); |
| } |
| |
| uint32_t ScopedMapping::v4l2_format() const |
| { |
| return CameraBufferManager::GetV4L2PixelFormat(buf_); |
| } |
| |
| uint32_t ScopedMapping::hal_pixel_format() const |
| { |
| return CameraBufferManager::GetHalPixelFormat(buf_); |
| } |
| |
| uint32_t ScopedMapping::num_planes() const |
| { |
| return CameraBufferManager::GetNumPlanes(buf_); |
| } |
| |
| ScopedMapping::Plane ScopedMapping::plane(int plane) const |
| { |
| if (plane > planes_.size()) { |
| return Plane(); |
| } |
| return planes_[plane]; |
| } |
| |
| bool ScopedMapping::is_valid() const |
| { |
| return buf_ != nullptr; |
| } |
| |
| void ScopedMapping::Invalidate() |
| { |
| if (buf_ == nullptr) { |
| return; |
| } |
| auto *buf_mgr = CameraBufferManager::GetInstance(); |
| buf_mgr->Unlock(buf_); |
| buf_mgr->Deregister(buf_); |
| planes_.fill(Plane()); |
| buf_ = nullptr; |
| } |
| |
| // |
| // CameraBufferManagerImpl implementations. |
| // |
| |
| // static |
| CameraBufferManager *CameraBufferManager::GetInstance() |
| { |
| static NoDestructor<CameraBufferManagerImpl> instance( |
| crosLibcamera::CreateMinigbmAllocator()); |
| if (!instance->allocator_) { |
| LOG(HAL, Error) |
| << "Failed to create the buffer allocator for CameraBufferManager"; |
| return nullptr; |
| } |
| return instance.get(); |
| } |
| |
| // static |
| bool CameraBufferManager::IsValidBuffer(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return false; |
| } |
| if (!IsMatchingFormat(handle->hal_pixel_format, handle->drm_format)) { |
| LOG(HAL, Error) << "HAL pixel format " << handle->hal_pixel_format |
| << " does not match DRM format " |
| << FormatToString(handle->drm_format); |
| return false; |
| } |
| return true; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetWidth(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| |
| return handle->width; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetHeight(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| |
| return handle->height; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetNumPlanes(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| |
| switch (handle->drm_format) { |
| case DRM_FORMAT_ABGR1555: |
| case DRM_FORMAT_ABGR2101010: |
| case DRM_FORMAT_ABGR4444: |
| case DRM_FORMAT_ABGR8888: |
| case DRM_FORMAT_ARGB1555: |
| case DRM_FORMAT_ARGB2101010: |
| case DRM_FORMAT_ARGB4444: |
| case DRM_FORMAT_ARGB8888: |
| case DRM_FORMAT_AYUV: |
| case DRM_FORMAT_BGR233: |
| case DRM_FORMAT_BGR565: |
| case DRM_FORMAT_BGR888: |
| case DRM_FORMAT_BGRA1010102: |
| case DRM_FORMAT_BGRA4444: |
| case DRM_FORMAT_BGRA5551: |
| case DRM_FORMAT_BGRA8888: |
| case DRM_FORMAT_BGRX1010102: |
| case DRM_FORMAT_BGRX4444: |
| case DRM_FORMAT_BGRX5551: |
| case DRM_FORMAT_BGRX8888: |
| case DRM_FORMAT_C8: |
| case DRM_FORMAT_GR88: |
| case DRM_FORMAT_R8: |
| case DRM_FORMAT_RG88: |
| case DRM_FORMAT_RGB332: |
| case DRM_FORMAT_RGB565: |
| case DRM_FORMAT_RGB888: |
| case DRM_FORMAT_RGBA1010102: |
| case DRM_FORMAT_RGBA4444: |
| case DRM_FORMAT_RGBA5551: |
| case DRM_FORMAT_RGBA8888: |
| case DRM_FORMAT_RGBX1010102: |
| case DRM_FORMAT_RGBX4444: |
| case DRM_FORMAT_RGBX5551: |
| case DRM_FORMAT_RGBX8888: |
| case DRM_FORMAT_UYVY: |
| case DRM_FORMAT_VYUY: |
| case DRM_FORMAT_XBGR1555: |
| case DRM_FORMAT_XBGR2101010: |
| case DRM_FORMAT_XBGR4444: |
| case DRM_FORMAT_XBGR8888: |
| case DRM_FORMAT_XRGB1555: |
| case DRM_FORMAT_XRGB2101010: |
| case DRM_FORMAT_XRGB4444: |
| case DRM_FORMAT_XRGB8888: |
| case DRM_FORMAT_YUYV: |
| case DRM_FORMAT_YVYU: |
| case DRM_FORMAT_MTISP_SXYZW10: |
| return 1; |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_NV21: |
| case DRM_FORMAT_P010: |
| return 2; |
| case DRM_FORMAT_YUV420: |
| case DRM_FORMAT_YVU420: |
| return 3; |
| } |
| |
| LOG(HAL, Error) << "Unknown format: " << FormatToString(handle->drm_format); |
| return 0; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetV4L2PixelFormat(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| |
| uint32_t num_planes = GetNumPlanes(buffer); |
| if (!num_planes) { |
| return 0; |
| } |
| |
| bool is_mplane = false; |
| if (num_planes > 1) { |
| // Check if the buffer has multiple physical planes by checking the offsets |
| // of each plane. If any of the offsets is zero, then we assume the buffer |
| // is of multi-planar format. |
| for (size_t i = 1; i < num_planes; ++i) { |
| if (!handle->offsets[i]) { |
| is_mplane = true; |
| } |
| } |
| } |
| |
| switch (handle->drm_format) { |
| case DRM_FORMAT_ARGB8888: |
| return V4L2_PIX_FMT_ABGR32; |
| |
| // There is no standard V4L2 pixel format corresponding to |
| // DRM_FORMAT_xBGR8888. We use our own V4L2 format extension |
| // V4L2_PIX_FMT_RGBX32 here. |
| case DRM_FORMAT_ABGR8888: |
| return V4L2_PIX_FMT_RGBX32; |
| case DRM_FORMAT_XBGR8888: |
| return V4L2_PIX_FMT_RGBX32; |
| |
| // DRM_FORMAT_R8 is used as the underlying buffer format for |
| // HAL_PIXEL_FORMAT_BLOB which corresponds to JPEG buffer. |
| case DRM_FORMAT_R8: |
| return V4L2_PIX_FMT_JPEG; |
| |
| // Semi-planar formats. |
| case DRM_FORMAT_NV12: |
| return is_mplane ? V4L2_PIX_FMT_NV12M : V4L2_PIX_FMT_NV12; |
| case DRM_FORMAT_NV21: |
| return is_mplane ? V4L2_PIX_FMT_NV21M : V4L2_PIX_FMT_NV21; |
| case DRM_FORMAT_P010: |
| return is_mplane ? V4L2_PIX_FMT_P010M : V4L2_PIX_FMT_P010; |
| |
| // Multi-planar formats. |
| case DRM_FORMAT_YUV420: |
| return is_mplane ? V4L2_PIX_FMT_YUV420M : V4L2_PIX_FMT_YUV420; |
| case DRM_FORMAT_YVU420: |
| return is_mplane ? V4L2_PIX_FMT_YVU420M : V4L2_PIX_FMT_YVU420; |
| } |
| |
| LOG(HAL, Error) << "Could not convert format " |
| << FormatToString(handle->drm_format) << " to V4L2 pixel format"; |
| return 0; |
| } |
| |
| // static |
| size_t CameraBufferManager::GetPlaneStride(buffer_handle_t buffer, |
| size_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| if (plane >= GetNumPlanes(buffer)) { |
| LOG(HAL, Error) << "Invalid plane: " << plane; |
| return 0; |
| } |
| return handle->strides[plane]; |
| } |
| |
| #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) |
| |
| // static |
| size_t CameraBufferManager::GetPlaneSize(buffer_handle_t buffer, size_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| if (plane >= GetNumPlanes(buffer)) { |
| LOG(HAL, Error) << "Invalid plane: " << plane; |
| return 0; |
| } |
| uint32_t vertical_subsampling; |
| switch (handle->drm_format) { |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_NV21: |
| case DRM_FORMAT_P010: |
| case DRM_FORMAT_YUV420: |
| case DRM_FORMAT_YVU420: |
| vertical_subsampling = (plane == 0) ? 1 : 2; |
| break; |
| default: |
| vertical_subsampling = 1; |
| } |
| return (handle->strides[plane] * |
| DIV_ROUND_UP(handle->height, vertical_subsampling)); |
| } |
| |
| // static |
| off_t CameraBufferManager::GetPlaneOffset(buffer_handle_t buffer, |
| size_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -1; |
| } |
| if (plane >= GetNumPlanes(buffer)) { |
| LOG(HAL, Error) << "Invalid plane: " << plane; |
| return -1; |
| } |
| return handle->offsets[plane]; |
| } |
| |
| // static |
| uint64_t CameraBufferManager::GetModifier(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return DRM_FORMAT_MOD_INVALID; |
| } |
| |
| return handle->modifier; |
| } |
| |
| // static |
| int CameraBufferManager::GetPlaneFd(buffer_handle_t buffer, size_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -1; |
| } |
| if (plane >= GetNumPlanes(buffer)) { |
| LOG(HAL, Error) << "Invalid plane: " << plane; |
| return -1; |
| } |
| return handle->fds[plane]; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetHalPixelFormat(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| return handle->hal_pixel_format; |
| } |
| |
| // static |
| uint32_t CameraBufferManager::GetDrmPixelFormat(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return 0; |
| } |
| return handle->drm_format; |
| } |
| |
| CameraBufferManagerImpl::CameraBufferManagerImpl( |
| std::unique_ptr<Allocator> gbm_allocator) |
| : allocator_(std::move(gbm_allocator)) |
| { |
| } |
| |
| int CameraBufferManagerImpl::Allocate(size_t width, |
| size_t height, |
| uint32_t hal_format, |
| uint32_t hal_usage, |
| buffer_handle_t *out_buffer, |
| uint32_t *out_stride) |
| { |
| std::lock_guard l(lock_); |
| |
| uint32_t gbm_flags = 0; |
| uint32_t drm_format = ResolveFormat(hal_format, hal_usage, &gbm_flags); |
| if (!drm_format) { |
| return -EINVAL; |
| } |
| |
| BufferContext context = { |
| .bo = allocator_->CreateBo(width, height, drm_format, gbm_flags) |
| }; |
| if (!context.bo) { |
| LOG(HAL, Error) << "Failed to create GBM bo"; |
| return -ENOMEM; |
| } |
| |
| auto handle = std::make_unique<camera_buffer_handle_t>(); |
| handle->base.version = sizeof(handle->base); |
| handle->base.numInts = kCameraBufferHandleNumInts; |
| handle->base.numFds = kCameraBufferHandleNumFds; |
| handle->magic = kCameraBufferMagic; |
| handle->buffer_id = context.bo->GetId(); |
| handle->drm_format = drm_format; |
| handle->hal_pixel_format = hal_format; |
| handle->hal_usage_flags = hal_usage; |
| handle->width = width; |
| handle->height = height; |
| auto desc = context.bo->Describe(); |
| for (size_t i = 0; i < desc.num_planes; ++i) { |
| handle->fds[i] = context.bo->GetPlaneFd(i); |
| handle->strides[i] = desc.planes[i].row_stride; |
| handle->offsets[i] = desc.planes[i].offset; |
| } |
| handle->modifier = desc.format_modifier; |
| |
| if (desc.num_planes == 1) { |
| *out_stride = handle->strides[0]; |
| } else { |
| *out_stride = 0; |
| } |
| *out_buffer = reinterpret_cast<buffer_handle_t>(handle.release()); |
| |
| context.refcount = 1; |
| buffers_[*out_buffer] = std::move(context); |
| return 0; |
| } |
| |
| // static |
| ScopedBufferHandle CameraBufferManager::AllocateScopedBuffer( |
| size_t width, size_t height, uint32_t hal_format, uint32_t hal_usage) |
| { |
| auto *buf_mgr = CameraBufferManager::GetInstance(); |
| ScopedBufferHandle buffer(new buffer_handle_t(nullptr)); |
| uint32_t stride; |
| if (buf_mgr->Allocate(width, height, hal_format, hal_usage, buffer.get(), |
| &stride) != 0) { |
| LOG(HAL, Error) << "Failed to allocate buffer"; |
| return nullptr; |
| } |
| LOG(HAL, Info) << "Buffer allocated -"; |
| LOG(HAL, Info) << "\tplanes: " << CameraBufferManager::GetNumPlanes(*buffer); |
| LOG(HAL, Info) << "\twidth: " << CameraBufferManager::GetWidth(*buffer); |
| LOG(HAL, Info) << "\theight: " << CameraBufferManager::GetHeight(*buffer); |
| LOG(HAL, Info) << "\tformat: " |
| << FormatToString(CameraBufferManager::GetDrmPixelFormat(*buffer)); |
| for (size_t i = 0; i < CameraBufferManager::GetNumPlanes(*buffer); ++i) { |
| LOG(HAL, Info) << "\tplane" << i |
| << " fd: " << CameraBufferManager::GetPlaneFd(*buffer, i); |
| LOG(HAL, Info) << "\tplane" << i |
| << " offset: " << CameraBufferManager::GetPlaneOffset(*buffer, i); |
| LOG(HAL, Info) << "\tplane" << i |
| << " stride: " << CameraBufferManager::GetPlaneStride(*buffer, i); |
| } |
| return buffer; |
| } |
| |
| int CameraBufferManagerImpl::Free(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| |
| Deregister(buffer); |
| delete handle; |
| return 0; |
| } |
| |
| int CameraBufferManagerImpl::Register(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| |
| std::lock_guard l(lock_); |
| |
| auto context_it = buffers_.find(buffer); |
| if (context_it != buffers_.end()) { |
| ++context_it->second.refcount; |
| return 0; |
| } |
| |
| // Import the buffer if we haven't done so. |
| uint32_t num_planes = GetNumPlanes(buffer); |
| if (num_planes == 0) { |
| return -EINVAL; |
| } |
| ImportData data = { |
| .desc = { |
| .drm_format = handle->drm_format, |
| .width = static_cast<int>(handle->width), |
| .height = static_cast<int>(handle->height), |
| .gbm_flags = GBM_BO_USE_CAMERA_READ | GBM_BO_USE_CAMERA_WRITE | |
| GBM_BO_USE_SW_READ_OFTEN | GBM_BO_USE_SW_WRITE_OFTEN, |
| .num_planes = static_cast<int>(num_planes), |
| .format_modifier = handle->modifier, |
| } |
| }; |
| for (size_t i = 0; i < num_planes; ++i) { |
| // GBM doesn't need plane size and pixel_stride to import a BO. |
| data.desc.planes[i] = { |
| .offset = static_cast<int>(handle->offsets[i]), |
| .row_stride = static_cast<int>(handle->strides[i]), |
| }; |
| data.plane_fd[i] = handle->fds[i]; |
| } |
| |
| BufferContext context = { .bo = allocator_->ImportBo(data), .refcount = 1 }; |
| if (!context.bo) { |
| LOG(HAL, Error) << "Failed to import buffer 0x" << std::hex |
| << handle->buffer_id; |
| return -EIO; |
| } |
| |
| buffers_[buffer] = std::move(context); |
| return 0; |
| } |
| |
| int CameraBufferManagerImpl::Deregister(buffer_handle_t buffer) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| |
| std::lock_guard l(lock_); |
| |
| auto context_it = buffers_.find(buffer); |
| if (context_it == buffers_.end()) { |
| LOG(HAL, Error) << "Unknown buffer 0x" << std::hex << handle->buffer_id; |
| return -EINVAL; |
| } |
| --context_it->second.refcount; |
| if (context_it->second.refcount == 0) { |
| buffers_.erase(context_it); |
| } |
| return 0; |
| } |
| |
| int CameraBufferManagerImpl::Lock(buffer_handle_t buffer, |
| uint32_t hal_usage, |
| uint32_t x, |
| uint32_t y, |
| uint32_t width, |
| uint32_t height, |
| void **out_addr) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| uint32_t num_planes = GetNumPlanes(buffer); |
| if (!num_planes) { |
| return -EINVAL; |
| } |
| if (num_planes > 1) { |
| LOG(HAL, Error) << "Lock called on multi-planar buffer 0x" << std::hex |
| << handle->buffer_id; |
| return -EINVAL; |
| } |
| if ((hal_usage & handle->hal_usage_flags) != hal_usage) { |
| LOG(HAL, Error) << "Incompatible usage flags: " << hal_usage |
| << " (original usage flags: " << handle->hal_usage_flags << ")"; |
| return -EINVAL; |
| } |
| |
| *out_addr = Map(buffer, hal_usage, 0); |
| if (*out_addr == MAP_FAILED) { |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int CameraBufferManagerImpl::LockYCbCr(buffer_handle_t buffer, |
| uint32_t hal_usage, |
| uint32_t x, |
| uint32_t y, |
| uint32_t width, |
| uint32_t height, |
| struct android_ycbcr *out_ycbcr) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| uint32_t num_planes = GetNumPlanes(buffer); |
| if (!num_planes) { |
| return -EINVAL; |
| } |
| if (num_planes < 2) { |
| LOG(HAL, Error) << "LockYCbCr called on single-planar buffer 0x" << std::hex |
| << handle->buffer_id; |
| return -EINVAL; |
| } |
| if ((hal_usage & handle->hal_usage_flags) != hal_usage) { |
| LOG(HAL, Error) << "Incompatible usage flags: " << hal_usage |
| << " (original usage flags: " << handle->hal_usage_flags << ")"; |
| return -EINVAL; |
| } |
| |
| //DCHECK_LE(num_planes, 3u); |
| std::vector<uint8_t *> addr(num_planes); |
| for (size_t i = 0; i < num_planes; ++i) { |
| void *a = Map(buffer, hal_usage, i); |
| if (a == MAP_FAILED) { |
| return -EINVAL; |
| } |
| addr[i] = reinterpret_cast<uint8_t *>(a); |
| } |
| out_ycbcr->y = addr[0]; |
| out_ycbcr->ystride = handle->strides[0]; |
| out_ycbcr->cstride = handle->strides[1]; |
| out_ycbcr->chroma_step = GetChromaStep(handle->drm_format); |
| //CHECK_GT(out_ycbcr->chroma_step, 0); |
| |
| if (num_planes == 2) { |
| switch (handle->drm_format) { |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_P010: |
| out_ycbcr->cb = addr[1]; |
| out_ycbcr->cr = addr[1] + (out_ycbcr->chroma_step / 2); |
| break; |
| |
| case DRM_FORMAT_NV21: |
| out_ycbcr->cb = addr[1] + (out_ycbcr->chroma_step / 2); |
| out_ycbcr->cr = addr[1]; |
| break; |
| |
| default: |
| LOG(HAL, Error) << "Unsupported semi-planar format: " |
| << FormatToString(handle->drm_format); |
| return -EINVAL; |
| } |
| } else { // num_planes == 3 |
| switch (handle->drm_format) { |
| case DRM_FORMAT_YUV420: |
| out_ycbcr->cb = addr[1]; |
| out_ycbcr->cr = addr[2]; |
| break; |
| |
| case DRM_FORMAT_YVU420: |
| out_ycbcr->cb = addr[2]; |
| out_ycbcr->cr = addr[1]; |
| break; |
| |
| default: |
| LOG(HAL, Error) << "Unsupported planar format: " |
| << FormatToString(handle->drm_format); |
| return -EINVAL; |
| } |
| } |
| return 0; |
| } |
| |
| int CameraBufferManagerImpl::Unlock(buffer_handle_t buffer) |
| { |
| for (size_t i = 0; i < GetNumPlanes(buffer); ++i) { |
| int ret = Unmap(buffer, i); |
| if (ret) { |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| uint32_t CameraBufferManagerImpl::ResolveDrmFormat(uint32_t hal_format, |
| uint32_t hal_usage) |
| { |
| uint32_t unused_gbm_flags; |
| return ResolveFormat(hal_format, hal_usage, &unused_gbm_flags); |
| } |
| |
| uint32_t CameraBufferManagerImpl::ResolveFormat(uint32_t hal_format, |
| uint32_t hal_usage, |
| uint32_t *gbm_flags) |
| { |
| uint32_t gbm_usage = GetGbmUseFlags(hal_format, hal_usage); |
| uint32_t drm_format = 0; |
| if (hal_usage & GRALLOC_USAGE_FORCE_I420) { |
| //CHECK_EQ(hal_format, HAL_PIXEL_FORMAT_YCbCr_420_888); |
| *gbm_flags = gbm_usage; |
| return DRM_FORMAT_YUV420; |
| } |
| |
| if (hal_format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED && |
| (hal_usage & GRALLOC_USAGE_HW_CAMERA_READ)) { |
| // Check which private format the graphics backend support. |
| if (allocator_->IsFormatSupported(DRM_FORMAT_MTISP_SXYZW10, gbm_usage)) { |
| *gbm_flags = gbm_usage; |
| return DRM_FORMAT_MTISP_SXYZW10; |
| } |
| // TODO(lnishan): Check other private formats when we have private formats |
| // from other platforms. |
| } |
| |
| if (kSupportedHalFormats.find(hal_format) == kSupportedHalFormats.end()) { |
| LOG(HAL, Error) << "Unsupported HAL pixel format"; |
| return 0; |
| } |
| |
| for (uint32_t format : kSupportedHalFormats[hal_format]) { |
| if (allocator_->IsFormatSupported(format, gbm_usage)) { |
| drm_format = format; |
| break; |
| } |
| } |
| |
| if (drm_format == 0 && hal_usage & GRALLOC_USAGE_HW_COMPOSER) { |
| gbm_usage &= ~GBM_BO_USE_SCANOUT; |
| for (uint32_t format : kSupportedHalFormats[hal_format]) { |
| if (allocator_->IsFormatSupported(format, gbm_usage)) { |
| drm_format = format; |
| break; |
| } |
| } |
| } |
| |
| if (drm_format == 0) { |
| LOG(HAL, Error) << "Cannot resolve the actual format of HAL pixel format " |
| << hal_format; |
| return 0; |
| } |
| |
| *gbm_flags = gbm_usage; |
| return drm_format; |
| } |
| |
| void *CameraBufferManagerImpl::Map(buffer_handle_t buffer, |
| uint32_t hal_usage, |
| uint32_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return MAP_FAILED; |
| } |
| |
| uint32_t num_planes = GetNumPlanes(buffer); |
| if (!num_planes) { |
| return MAP_FAILED; |
| } |
| if (!(plane < kMaxPlanes && plane < num_planes)) { |
| LOG(HAL, Error) << "Invalid plane: " << plane; |
| return MAP_FAILED; |
| } |
| |
| LOG(HAL, Info) << "buffer info:"; |
| LOG(HAL, Info) << "\tfd: " << handle->fds[plane]; |
| LOG(HAL, Info) << "\tbuffer_id: 0x" << std::hex << handle->buffer_id; |
| LOG(HAL, Info) << "\tformat: " << FormatToString(handle->drm_format); |
| LOG(HAL, Info) << "\twidth: " << handle->width; |
| LOG(HAL, Info) << "\theight: " << handle->height; |
| LOG(HAL, Info) << "\tstride: " << handle->strides[plane]; |
| LOG(HAL, Info) << "\toffset: " << handle->offsets[plane]; |
| |
| std::lock_guard l(lock_); |
| |
| auto context_it = buffers_.find(buffer); |
| if (context_it == buffers_.end()) { |
| LOG(HAL, Error) << "Buffer 0x" << std::hex << handle->buffer_id |
| << " is not registered"; |
| return MAP_FAILED; |
| } |
| |
| // Always map the whole buffer. |
| auto &bo = context_it->second.bo; |
| if (!bo->BeginCpuAccess(SyncType::kSyncReadWrite, plane)) { |
| LOG(HAL, Error) << "Failed to sync buffer for CPU access"; |
| return MAP_FAILED; |
| } |
| if (!bo->Map(plane)) { |
| LOG(HAL, Error) << "Failed to map buffer"; |
| return MAP_FAILED; |
| } |
| void *addr = bo->GetPlaneAddr(plane); |
| LOG(HAL, Info) << "Plane " << plane << " of DMA-buf 0x" << std::hex |
| << handle->buffer_id << " mapped to " << addr; |
| return addr; |
| } |
| |
| int CameraBufferManagerImpl::Unmap(buffer_handle_t buffer, uint32_t plane) |
| { |
| auto handle = camera_buffer_handle_t::FromBufferHandle(buffer); |
| if (!handle) { |
| return -EINVAL; |
| } |
| |
| std::lock_guard l(lock_); |
| |
| auto context_it = buffers_.find(buffer); |
| if (context_it == buffers_.end()) { |
| LOG(HAL, Error) << "Buffer 0x" << std::hex << handle->buffer_id |
| << " is not registered"; |
| return -EINVAL; |
| } |
| |
| auto &bo = context_it->second.bo; |
| bo->Unmap(plane); |
| if (!bo->EndCpuAccess(SyncType::kSyncReadWrite, plane)) { |
| LOG(HAL, Error) << "Failed to sync buffer after CPU access"; |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| } // namespace crosLibcamera |