/* * Copyright (C) 2016 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. */ #include "v4l2_gralloc.h" #include #include #include #include #include #include "common.h" #include "stream_format.h" namespace v4l2_camera_hal { // Copy |height| lines from |src| to |dest|, // where |src| and |dest| may have different line lengths. void copyWithPadding(uint8_t* dest, const uint8_t* src, size_t dest_stride, size_t src_stride, size_t height) { size_t copy_stride = dest_stride; if (copy_stride > src_stride) { // Adding padding, not reducing. 0 out the extra memory. memset(dest, 0, src_stride * height); copy_stride = src_stride; } uint8_t* dest_line_start = dest; const uint8_t* src_line_start = src; for (size_t row = 0; row < height; ++row, dest_line_start += dest_stride, src_line_start += src_stride) { memcpy(dest_line_start, src_line_start, copy_stride); } } V4L2Gralloc* V4L2Gralloc::NewV4L2Gralloc() { // Initialize and check the gralloc module. const hw_module_t* module = nullptr; int res = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); if (res || !module) { HAL_LOGE("Couldn't get gralloc module."); return nullptr; } const gralloc_module_t* gralloc = reinterpret_cast(module); // This class only supports Gralloc v0, not Gralloc V1. if (gralloc->common.module_api_version > GRALLOC_MODULE_API_VERSION_0_3) { HAL_LOGE( "Invalid gralloc version %x. Only 0.3 (%x) " "and below are supported by this HAL.", gralloc->common.module_api_version, GRALLOC_MODULE_API_VERSION_0_3); return nullptr; } return new V4L2Gralloc(gralloc); } // Private. As checked by above factory, module will be non-null // and a supported version. V4L2Gralloc::V4L2Gralloc(const gralloc_module_t* module) : mModule(module) {} V4L2Gralloc::~V4L2Gralloc() { // Unlock buffers that are still locked. unlockAllBuffers(); } int V4L2Gralloc::lock(const camera3_stream_buffer_t* camera_buffer, uint32_t bytes_per_line, v4l2_buffer* device_buffer) { // Lock the camera buffer (varies depending on if the buffer is YUV or not). std::unique_ptr buffer_data( new BufferData{camera_buffer, nullptr, bytes_per_line}); buffer_handle_t buffer = *camera_buffer->buffer; void* data; camera3_stream_t* stream = camera_buffer->stream; int ret = 0; switch (StreamFormat::HalToV4L2PixelFormat(stream->format)) { // TODO(b/30119452): support more YCbCr formats. case V4L2_PIX_FMT_YUV420: android_ycbcr yuv_data; ret = mModule->lock_ycbcr(mModule, buffer, stream->usage, 0, 0, stream->width, stream->height, &yuv_data); if (ret) { HAL_LOGE("Failed to lock ycbcr buffer: %d", ret); return ret; } // Check if gralloc format matches v4l2 format // (same padding, not interleaved, contiguous). if (yuv_data.ystride == bytes_per_line && yuv_data.cstride == bytes_per_line / 2 && yuv_data.chroma_step == 1 && (reinterpret_cast(yuv_data.cb) == reinterpret_cast(yuv_data.y) + (stream->height * yuv_data.ystride)) && (reinterpret_cast(yuv_data.cr) == reinterpret_cast(yuv_data.cb) + (stream->height / 2 * yuv_data.cstride))) { // If so, great, point to the beginning. data = yuv_data.y; } else { // If not, allocate a contiguous buffer of appropriate size // (to be transformed back upon unlock). data = new uint8_t[device_buffer->length]; // Make a dynamically-allocated copy of yuv_data, // since it will be needed at transform time. buffer_data->transform_dest.reset(new android_ycbcr(yuv_data)); } break; case V4L2_PIX_FMT_JPEG: // Jpeg buffers are just contiguous blobs; lock length * 1. ret = mModule->lock(mModule, buffer, stream->usage, 0, 0, device_buffer->length, 1, &data); if (ret) { HAL_LOGE("Failed to lock jpeg buffer: %d", ret); return ret; } break; case V4L2_PIX_FMT_BGR32: // Fall-through. case V4L2_PIX_FMT_RGB32: // RGB formats have nice agreed upon representation. Unless using android // flex formats. ret = mModule->lock(mModule, buffer, stream->usage, 0, 0, stream->width, stream->height, &data); if (ret) { HAL_LOGE("Failed to lock RGB buffer: %d", ret); return ret; } break; default: return -EINVAL; } if (!data) { ALOGE("Gralloc lock returned null ptr"); return -ENODEV; } // Set up the device buffer. static_assert(sizeof(unsigned long) >= sizeof(void*), "void* must be able to fit in the v4l2_buffer m.userptr " "field (unsigned long) for this code to work"); device_buffer->m.userptr = reinterpret_cast(data); // Note the mapping of data:buffer info for when unlock is called. mBufferMap.emplace(data, buffer_data.release()); return 0; } int V4L2Gralloc::unlock(const v4l2_buffer* device_buffer) { // TODO(b/30000211): support multi-planar data (video_capture_mplane). if (device_buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { return -EINVAL; } void* data = reinterpret_cast(device_buffer->m.userptr); // Find and pop the matching entry in the map. auto map_entry = mBufferMap.find(data); if (map_entry == mBufferMap.end()) { HAL_LOGE("No matching buffer for data at %p", data); return -EINVAL; } std::unique_ptr buffer_data(map_entry->second); mBufferMap.erase(map_entry); const camera3_stream_buffer_t* camera_buffer = buffer_data->camera_buffer; const buffer_handle_t buffer = *camera_buffer->buffer; // Check for transform. if (buffer_data->transform_dest) { HAL_LOGV("Transforming V4L2 YUV to gralloc YUV."); // In this case data was allocated by this class, put it in a unique_ptr // to ensure it gets cleaned up no matter which way this function exits. std::unique_ptr data_cleanup(reinterpret_cast(data)); uint32_t bytes_per_line = buffer_data->v4l2_bytes_per_line; android_ycbcr* yuv_data = buffer_data->transform_dest.get(); // Should only occur in error situations. if (device_buffer->bytesused == 0) { return -EINVAL; } // Transform V4L2 to Gralloc, copying each plane to the correct place, // adjusting padding, and interleaving if necessary. uint32_t height = camera_buffer->stream->height; // Y data first. size_t y_len = bytes_per_line * height; if (yuv_data->ystride == bytes_per_line) { // Data should match exactly. memcpy(yuv_data->y, data, y_len); } else { HAL_LOGV("Changing padding on Y plane from %u to %u.", bytes_per_line, yuv_data->ystride); // Wrong padding from V4L2. copyWithPadding(reinterpret_cast(yuv_data->y), reinterpret_cast(data), yuv_data->ystride, bytes_per_line, height); } // C data. // TODO(b/30119452): These calculations assume YCbCr_420_888. size_t c_len = y_len / 4; uint32_t c_bytes_per_line = bytes_per_line / 2; // V4L2 is packed, meaning the data is stored as contiguous {y, cb, cr}. uint8_t* cb_device = reinterpret_cast(data) + y_len; uint8_t* cr_device = cb_device + c_len; size_t step = yuv_data->chroma_step; if (step == 1) { // Still planar. if (yuv_data->cstride == c_bytes_per_line) { // Data should match exactly. memcpy(yuv_data->cb, cb_device, c_len); memcpy(yuv_data->cr, cr_device, c_len); } else { HAL_LOGV("Changing padding on C plane from %u to %u.", c_bytes_per_line, yuv_data->cstride); // Wrong padding from V4L2. copyWithPadding(reinterpret_cast(yuv_data->cb), cb_device, yuv_data->cstride, c_bytes_per_line, height / 2); copyWithPadding(reinterpret_cast(yuv_data->cr), cr_device, yuv_data->cstride, c_bytes_per_line, height / 2); } } else { // Desire semiplanar (cb and cr interleaved). HAL_LOGV("Interleaving cb and cr. Padding going from %u to %u.", c_bytes_per_line, yuv_data->cstride); uint32_t c_height = height / 2; uint32_t c_width = camera_buffer->stream->width / 2; // Zero out destination uint8_t* cb_gralloc = reinterpret_cast(yuv_data->cb); uint8_t* cr_gralloc = reinterpret_cast(yuv_data->cr); memset(cb_gralloc, 0, c_width * c_height * step); // Interleaving means we need to copy the cb and cr bytes one by one. for (size_t line = 0; line < c_height; ++line, cb_gralloc += yuv_data->cstride, cr_gralloc += yuv_data->cstride, cb_device += c_bytes_per_line, cr_device += c_bytes_per_line) { for (size_t i = 0; i < c_width; ++i) { *(cb_gralloc + (i * step)) = *(cb_device + i); *(cr_gralloc + (i * step)) = *(cr_device + i); } } } } // Unlock. int res = mModule->unlock(mModule, buffer); if (res) { HAL_LOGE("Failed to unlock buffer at %p", buffer); return -ENODEV; } return 0; } int V4L2Gralloc::unlockAllBuffers() { HAL_LOG_ENTER(); bool failed = false; for (auto const& entry : mBufferMap) { int res = mModule->unlock(mModule, *entry.second->camera_buffer->buffer); if (res) { failed = true; } // When there is a transform to be made, the buffer returned by lock() // is dynamically allocated (to hold the pre-transform data). if (entry.second->transform_dest) { delete[] reinterpret_cast(entry.first); } // The BufferData entry is always dynamically allocated in lock(). delete entry.second; } mBufferMap.clear(); // If any unlock failed, return error. if (failed) { return -ENODEV; } return 0; } } // namespace default_camera_hal