| /* |
| * Copyright Samsung Electronics Co.,LTD. |
| * Copyright (C) 2015 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 <linux/videodev2.h> |
| #include <linux/v4l2-controls.h> |
| |
| #include <exynos-hwjpeg.h> |
| #include "hwjpeg-internal.h" |
| |
| CHWJpegV4L2Compressor::CHWJpegV4L2Compressor(const char *path): CHWJpegCompressor(path) |
| { |
| memset(&m_v4l2Format, 0, sizeof(m_v4l2Format)); |
| memset(&m_v4l2SrcBuffer, 0, sizeof(m_v4l2SrcBuffer)); |
| memset(&m_v4l2DstBuffer, 0, sizeof(m_v4l2DstBuffer)); |
| memset(&m_v4l2SrcPlanes, 0, sizeof(m_v4l2SrcPlanes)); |
| memset(&m_v4l2DstPlanes, 0, sizeof(m_v4l2DstPlanes)); |
| memset(&m_v4l2Controls, 0, sizeof(m_v4l2Controls)); |
| |
| m_v4l2Format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| // default image format is initialized by 8x8 RGB24 in order for TryFormat() |
| // to seccess at anytime. |
| // 8x8 : the smallest image size to compress |
| // RGB24(or YUV444): The image format with the smallest memory footprint |
| // without any image size constraints. |
| m_v4l2Format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_RGB24; |
| m_v4l2Format.fmt.pix_mp.width = TO_IMAGE_SIZE(16, 0); |
| m_v4l2Format.fmt.pix_mp.height = TO_IMAGE_SIZE(16, 0); |
| m_v4l2SrcBuffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| m_v4l2DstBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| |
| m_v4l2SrcBuffer.m.planes = m_v4l2SrcPlanes; |
| m_v4l2DstBuffer.m.planes = m_v4l2DstPlanes; |
| |
| m_uiControlsToSet = 0; |
| m_uiHWDelay = 0; |
| |
| m_bEnableHWFC = false; |
| |
| v4l2_capability cap; |
| memset(&cap, 0, sizeof(cap)); |
| if (ioctl(GetDeviceFD(), VIDIOC_QUERYCAP, &cap) < 0) { |
| ALOGERR("Failed to query capability of /dev/video12"); |
| } else if (!!(cap.capabilities & V4L2_CAP_DEVICE_CAPS)) { |
| SetDeviceCapabilities(cap.device_caps); |
| } |
| |
| // Initialy declare that s_fmt is required. |
| SetFlag(HWJPEG_FLAG_PIX_FMT); |
| |
| ALOGI("CHWJpegV4L2Compressor Created: %p, FD %d", this, GetDeviceFD()); |
| } |
| |
| CHWJpegV4L2Compressor::~CHWJpegV4L2Compressor() |
| { |
| StopStreaming(); |
| |
| ALOGI("CHWJpegV4L2Compressor Destroyed: %p, FD %d", this, GetDeviceFD()); |
| } |
| |
| bool CHWJpegV4L2Compressor::SetChromaSampFactor( |
| unsigned int horizontal, unsigned int vertical) |
| { |
| __s32 value; |
| switch ((horizontal << 4) | vertical) { |
| case 0x00: value = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; break; |
| case 0x11: value = V4L2_JPEG_CHROMA_SUBSAMPLING_444; break; |
| case 0x21: value = V4L2_JPEG_CHROMA_SUBSAMPLING_422; break; |
| case 0x22: value = V4L2_JPEG_CHROMA_SUBSAMPLING_420; break; |
| case 0x41: value = V4L2_JPEG_CHROMA_SUBSAMPLING_411; break; |
| case 0x12: |
| default: |
| ALOGE("Unsupported chroma subsampling %ux%u", horizontal, vertical); |
| return false; |
| } |
| |
| m_v4l2Controls[HWJPEG_CTRL_CHROMFACTOR].id = V4L2_CID_JPEG_CHROMA_SUBSAMPLING; |
| m_v4l2Controls[HWJPEG_CTRL_CHROMFACTOR].value = value; |
| m_uiControlsToSet |= 1 << HWJPEG_CTRL_CHROMFACTOR; |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetQuality( |
| unsigned int quality_factor, unsigned int quality_factor2) |
| { |
| if (quality_factor > 100) { |
| ALOGE("Unsupported quality factor %u", quality_factor); |
| return false; |
| } |
| |
| if (quality_factor2 > 100) { |
| ALOGE("Unsupported quality factor %u for the secondary image", |
| quality_factor2); |
| return false; |
| } |
| |
| if (quality_factor > 0) { |
| m_v4l2Controls[HWJPEG_CTRL_QFACTOR].id = V4L2_CID_JPEG_COMPRESSION_QUALITY; |
| m_v4l2Controls[HWJPEG_CTRL_QFACTOR].value = static_cast<__s32>(quality_factor); |
| m_uiControlsToSet |= 1 << HWJPEG_CTRL_QFACTOR; |
| } |
| |
| if (quality_factor2 > 0) { |
| m_v4l2Controls[HWJPEG_CTRL_QFACTOR2].id = V4L2_CID_JPEG_SEC_COMP_QUALITY; |
| m_v4l2Controls[HWJPEG_CTRL_QFACTOR2].value = static_cast<__s32>(quality_factor2); |
| m_uiControlsToSet |= 1 << HWJPEG_CTRL_QFACTOR2; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetQuality(const unsigned char qtable[]) |
| { |
| v4l2_ext_controls ctrls; |
| v4l2_ext_control ctrl; |
| |
| memset(&ctrls, 0, sizeof(ctrls)); |
| memset(&ctrl, 0, sizeof(ctrl)); |
| |
| ctrls.ctrl_class = V4L2_CTRL_CLASS_JPEG; |
| ctrls.controls = &ctrl; |
| ctrls.count = 1; |
| |
| ctrl.id = V4L2_CID_JPEG_QTABLES2; |
| ctrl.size = 128; /* two quantization tables */ |
| ctrl.p_u8 = const_cast<unsigned char *>(qtable); |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_S_EXT_CTRLS, &ctrls) < 0) { |
| ALOGERR("Failed to configure %u controls", ctrls.count); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetImageFormat(unsigned int v4l2_fmt, |
| unsigned int width, unsigned int height, |
| unsigned int width2, unsigned int height2) |
| { |
| if ((m_v4l2Format.fmt.pix_mp.pixelformat == v4l2_fmt) && |
| (m_v4l2Format.fmt.pix_mp.width == TO_IMAGE_SIZE(width, width2)) && |
| (m_v4l2Format.fmt.pix_mp.height == TO_IMAGE_SIZE(height, height2))) |
| return true; |
| |
| m_v4l2Format.fmt.pix_mp.pixelformat = v4l2_fmt; |
| m_v4l2Format.fmt.pix_mp.width = TO_IMAGE_SIZE(width, width2); |
| m_v4l2Format.fmt.pix_mp.height = TO_IMAGE_SIZE(height, height2); |
| |
| SetFlag(HWJPEG_FLAG_PIX_FMT); |
| |
| return TryFormat(); |
| } |
| |
| bool CHWJpegV4L2Compressor::GetImageBufferSizes(size_t buf_sizes[], unsigned int *num_buffers) |
| { |
| if (buf_sizes) { |
| for (unsigned int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) |
| buf_sizes[i] = m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage; |
| } |
| |
| if (num_buffers) { |
| if (*num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("The size array length %u is smaller than the number of required buffers %u", |
| *num_buffers, m_v4l2Format.fmt.pix_mp.num_planes); |
| return false; |
| } |
| |
| *num_buffers = m_v4l2Format.fmt.pix_mp.num_planes; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetImageBuffer(char *buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("The number of buffers %u is smaller than the required %u", |
| num_buffers,m_v4l2Format.fmt.pix_mp.num_planes); |
| return false; |
| } |
| |
| for (unsigned int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) { |
| m_v4l2SrcPlanes[i].m.userptr = reinterpret_cast<unsigned long>(buffers[i]); |
| if (len_buffers[i] < m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage) { |
| ALOGE("The size of the buffer[%u] %zu is smaller than required %u", |
| i, len_buffers[i], m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage); |
| return false; |
| } |
| m_v4l2SrcPlanes[i].bytesused = m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage; |
| m_v4l2SrcPlanes[i].length = len_buffers[i]; |
| } |
| |
| m_v4l2SrcBuffer.memory = V4L2_MEMORY_USERPTR; |
| |
| SetFlag(HWJPEG_FLAG_SRC_BUFFER); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetImageBuffer(int buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("The number of buffers %u is smaller than the required %u", |
| num_buffers,m_v4l2Format.fmt.pix_mp.num_planes); |
| return false; |
| } |
| |
| for (unsigned int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) { |
| m_v4l2SrcPlanes[i].m.fd = buffers[i]; |
| if (len_buffers[i] < m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage) { |
| ALOGE("The size of the buffer[%u] %zu is smaller than required %u", |
| i, len_buffers[i], m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage); |
| return false; |
| } |
| m_v4l2SrcPlanes[i].bytesused = m_v4l2Format.fmt.pix_mp.plane_fmt[i].sizeimage; |
| m_v4l2SrcPlanes[i].length = len_buffers[i]; |
| } |
| |
| m_v4l2SrcBuffer.memory = V4L2_MEMORY_DMABUF; |
| |
| SetFlag(HWJPEG_FLAG_SRC_BUFFER); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetImageBuffer2(char *buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (!IsDeviceCapability(V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) { |
| ALOGE("Back-to-back compression is not suppored by H/W"); |
| return false; |
| } |
| |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("The number of buffers %u is smaller than the required %u (secondary)", |
| num_buffers,m_v4l2Format.fmt.pix_mp.num_planes); |
| return false; |
| } |
| |
| unsigned int ibuf = 0; |
| for (unsigned int i = m_v4l2Format.fmt.pix_mp.num_planes; |
| i < (m_v4l2Format.fmt.pix_mp.num_planes * 2); i++, ibuf++) { |
| m_v4l2SrcPlanes[i].m.userptr = reinterpret_cast<unsigned long>(buffers[ibuf]); |
| // size check is ignored for the secondary image buffers |
| m_v4l2SrcPlanes[i].bytesused = len_buffers[ibuf]; |
| m_v4l2SrcPlanes[i].length = len_buffers[ibuf]; |
| } |
| |
| // memory type is only configured by the primary image configuration |
| SetFlag(HWJPEG_FLAG_SRC_BUFFER2); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetImageBuffer2(int buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (!IsDeviceCapability(V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) { |
| ALOGE("Back-to-back compression is not suppored by H/W"); |
| return false; |
| } |
| |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("The number of buffers %u is smaller than the required %u (secondary)", |
| num_buffers,m_v4l2Format.fmt.pix_mp.num_planes); |
| return false; |
| } |
| |
| unsigned int ibuf = 0; |
| for (unsigned int i = m_v4l2Format.fmt.pix_mp.num_planes; |
| i < (m_v4l2Format.fmt.pix_mp.num_planes * 2); i++, ibuf++) { |
| m_v4l2SrcPlanes[i].m.fd = buffers[ibuf]; |
| // size check is ignored for the secondary image buffers |
| m_v4l2SrcPlanes[i].bytesused = len_buffers[ibuf]; |
| m_v4l2SrcPlanes[i].length = len_buffers[ibuf]; |
| } |
| |
| // memory type is only configured by the primary image configuration |
| |
| SetFlag(HWJPEG_FLAG_SRC_BUFFER2); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetJpegBuffer(char *buffer, size_t len_buffer) |
| { |
| m_v4l2DstPlanes[0].m.userptr = reinterpret_cast<unsigned long>(buffer); |
| m_v4l2DstPlanes[0].length = len_buffer; |
| m_v4l2DstBuffer.memory = V4L2_MEMORY_USERPTR; |
| SetFlag(HWJPEG_FLAG_DST_BUFFER); |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetJpegBuffer(int buffer, size_t len_buffer, int offset) |
| { |
| m_v4l2DstPlanes[0].m.fd = buffer; |
| m_v4l2DstPlanes[0].length = len_buffer; |
| m_v4l2DstPlanes[0].data_offset = offset; |
| m_v4l2DstBuffer.memory = V4L2_MEMORY_DMABUF; |
| SetFlag(HWJPEG_FLAG_DST_BUFFER); |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetJpegBuffer2(char *buffer, size_t len_buffer) |
| { |
| if (!IsDeviceCapability(V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) { |
| ALOGE("Back-to-back compression is not suppored by H/W"); |
| return false; |
| } |
| |
| m_v4l2DstPlanes[1].m.userptr = reinterpret_cast<unsigned long>(buffer); |
| m_v4l2DstPlanes[1].length = len_buffer; |
| SetFlag(HWJPEG_FLAG_DST_BUFFER2); |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetJpegBuffer2(int buffer, size_t len_buffer) |
| { |
| if (!IsDeviceCapability(V4L2_CAP_EXYNOS_JPEG_B2B_COMPRESSION)) { |
| ALOGE("Back-to-back compression is not suppored by H/W"); |
| return false; |
| } |
| |
| m_v4l2DstPlanes[1].m.fd = buffer; |
| m_v4l2DstPlanes[1].length = len_buffer; |
| SetFlag(HWJPEG_FLAG_DST_BUFFER2); |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::StopStreaming() |
| { |
| if (TestFlag(HWJPEG_FLAG_STREAMING)) { |
| if (!StreamOff()) |
| return false; |
| ClearFlag(HWJPEG_FLAG_STREAMING); |
| } |
| |
| // Stream off dequeues all queued buffers |
| ClearFlag(HWJPEG_FLAG_QBUF_OUT | HWJPEG_FLAG_QBUF_CAP); |
| |
| // It is OK to skip DQBUF because STREAMOFF dequeues all queued buffers |
| if (TestFlag(HWJPEG_FLAG_REQBUFS)) { |
| if (!ReqBufs(0)) |
| return false; |
| ClearFlag(HWJPEG_FLAG_REQBUFS); |
| } |
| |
| return true; |
| } |
| |
| ssize_t CHWJpegV4L2Compressor::Compress(size_t *secondary_stream_size, bool block_mode) |
| { |
| if (TestFlag(HWJPEG_FLAG_PIX_FMT)) { |
| if (!StopStreaming() || !SetFormat()) |
| return -1; |
| } |
| |
| if (!TestFlag(HWJPEG_FLAG_SRC_BUFFER)) { |
| ALOGE("Source image buffer is not specified"); |
| return -1; |
| } |
| |
| if (!TestFlag(HWJPEG_FLAG_DST_BUFFER)) { |
| ALOGE("Output JPEG stream buffer is not specified"); |
| return -1; |
| } |
| |
| m_v4l2SrcBuffer.length = m_v4l2Format.fmt.pix_mp.num_planes; |
| m_v4l2DstBuffer.length = 1; |
| if (IsB2BCompression()) { |
| if (!TestFlag(HWJPEG_FLAG_SRC_BUFFER2 | HWJPEG_FLAG_DST_BUFFER2)) { |
| ALOGE("Either of source or destination buffer of secondary image is not specified (%#x)", |
| GetFlags()); |
| return -1; |
| } |
| // The SMFC Driver expects the number of buffers to be doubled |
| // if back-to-back compression is enabled |
| m_v4l2SrcBuffer.length *= 2; |
| m_v4l2DstBuffer.length = 2; |
| } |
| |
| if (!!(GetAuxFlags() & EXYNOS_HWJPEG_AUXOPT_SRC_NOCACHECLEAN)) |
| m_v4l2SrcBuffer.flags |= V4L2_BUF_FLAG_NO_CACHE_CLEAN; |
| if (!!(GetAuxFlags() & EXYNOS_HWJPEG_AUXOPT_DST_NOCACHECLEAN)) |
| m_v4l2DstBuffer.flags |= V4L2_BUF_FLAG_NO_CACHE_CLEAN; |
| |
| if (!ReqBufs() || !StreamOn() || !UpdateControls() || !QBuf()) |
| return -1; |
| |
| return block_mode ? DQBuf(secondary_stream_size) : 0; |
| } |
| |
| bool CHWJpegV4L2Compressor::TryFormat() |
| { |
| if (ioctl(GetDeviceFD(), VIDIOC_TRY_FMT, &m_v4l2Format) < 0) { |
| ALOGERR("Failed to TRY_FMT for compression"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::SetFormat() |
| { |
| if (ioctl(GetDeviceFD(), VIDIOC_S_FMT, &m_v4l2Format) < 0) { |
| ALOGERR("Failed to S_FMT for image to compress"); |
| return false; |
| } |
| |
| v4l2_format v4l2JpegFormat; |
| memset(&v4l2JpegFormat, 0, sizeof(v4l2JpegFormat)); |
| |
| v4l2JpegFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| v4l2JpegFormat.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; |
| v4l2JpegFormat.fmt.pix_mp.width = m_v4l2Format.fmt.pix_mp.width; |
| v4l2JpegFormat.fmt.pix_mp.height = m_v4l2Format.fmt.pix_mp.height; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_S_FMT, &v4l2JpegFormat) < 0) { |
| ALOGERR("Failed to S_FMT for JPEG stream to capture"); |
| return false; |
| } |
| |
| ClearFlag(HWJPEG_FLAG_PIX_FMT); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::UpdateControls() |
| { |
| bool enable_hwfc = !!(GetAuxFlags() & EXYNOS_HWJPEG_AUXOPT_ENABLE_HWFC); |
| |
| if ((m_uiControlsToSet == 0) && (enable_hwfc == m_bEnableHWFC)) |
| return true; |
| |
| v4l2_ext_controls ctrls; |
| v4l2_ext_control ctrl[HWJPEG_CTRL_NUM]; |
| |
| memset(&ctrls, 0, sizeof(ctrls)); |
| memset(&ctrl, 0, sizeof(ctrl)); |
| |
| ctrls.ctrl_class = V4L2_CTRL_CLASS_JPEG; |
| ctrls.controls = ctrl; |
| unsigned int idx_ctrl = 0; |
| while (m_uiControlsToSet != 0) { |
| if (m_uiControlsToSet & (1 << idx_ctrl)) { |
| ctrl[ctrls.count].id = m_v4l2Controls[idx_ctrl].id; |
| ctrl[ctrls.count].value = m_v4l2Controls[idx_ctrl].value; |
| m_uiControlsToSet &= ~(1 << idx_ctrl); |
| ctrls.count++; |
| } |
| idx_ctrl++; |
| } |
| |
| if (m_bEnableHWFC != enable_hwfc) { |
| m_bEnableHWFC = enable_hwfc; |
| ctrl[ctrls.count].id = V4L2_CID_JPEG_HWFC_ENABLE; |
| ctrl[ctrls.count].value = m_bEnableHWFC ? 1 : 0; |
| ctrls.count++; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_S_EXT_CTRLS, &ctrls) < 0) { |
| ALOGERR("Failed to configure %u controls", ctrls.count); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::ReqBufs(unsigned int count) |
| { |
| // - count > 0 && REQBUFS is set: Just return true |
| // - count > 0 && REQBUFS is unset: REQBUFS(count) is required |
| // - count == 0 && REQBUFS is set: REQBUFS(0) is required |
| // - count == 0 && REQBUFS is unset: Just return true; |
| if ((count > 0) == TestFlag(HWJPEG_FLAG_REQBUFS)) |
| return true; |
| |
| v4l2_requestbuffers reqbufs; |
| |
| memset(&reqbufs, 0, sizeof(reqbufs)); |
| reqbufs.count = count; |
| reqbufs.memory = m_v4l2SrcBuffer.memory; |
| reqbufs.type = m_v4l2SrcBuffer.type; |
| if (ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs) < 0) { |
| ALOGERR("Failed to REQBUFS(%u) of the source image", count); |
| return false; |
| } |
| |
| memset(&reqbufs, 0, sizeof(reqbufs)); |
| reqbufs.count = count; |
| reqbufs.memory = m_v4l2DstBuffer.memory; |
| reqbufs.type = m_v4l2DstBuffer.type; |
| if (ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs) < 0) { |
| ALOGERR("Failed to REQBUFS(%u) of the JPEG stream", count); |
| // rolling back the reqbufs for the source image |
| reqbufs.memory = m_v4l2SrcBuffer.memory; |
| reqbufs.type = m_v4l2SrcBuffer.type; |
| reqbufs.count = 0; |
| ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs); // don't care if it fails |
| return false; |
| } |
| |
| if (count > 0) |
| SetFlag(HWJPEG_FLAG_REQBUFS); |
| else |
| ClearFlag(HWJPEG_FLAG_REQBUFS); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::StreamOn() |
| { |
| if (TestFlag(HWJPEG_FLAG_STREAMING)) |
| return true; |
| |
| if (!TestFlag(HWJPEG_FLAG_REQBUFS)) { |
| ALOGE("Trying to STREAMON before REQBUFS"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMON, &m_v4l2SrcBuffer.type) < 0) { |
| ALOGERR("Failed to STREAMON for the source image"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMON, &m_v4l2DstBuffer.type) < 0) { |
| ALOGERR("Failed to STREAMON for the JPEG stream"); |
| ioctl(GetDeviceFD(), VIDIOC_STREAMOFF, &m_v4l2SrcBuffer.type); |
| return false; |
| } |
| |
| SetFlag(HWJPEG_FLAG_STREAMING); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::StreamOff() |
| { |
| if (!TestFlag(HWJPEG_FLAG_STREAMING)) |
| return true; |
| |
| // error during stream off do not need further handling because of nothing to do |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMOFF, &m_v4l2SrcBuffer.type) < 0) |
| ALOGERR("Failed to STREAMOFF for the source image"); |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMOFF, &m_v4l2DstBuffer.type) < 0) |
| ALOGERR("Failed to STREAMOFF for the JPEG stream"); |
| |
| ClearFlag(HWJPEG_FLAG_STREAMING); |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::QBuf() |
| { |
| if (!TestFlag(HWJPEG_FLAG_REQBUFS)) { |
| ALOGE("QBuf is not permitted until REQBUFS is performed"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_QBUF, &m_v4l2SrcBuffer) < 0) { |
| ALOGERR("QBuf of the source buffers is failed (B2B %s)", |
| IsB2BCompression() ? "enabled" : "disabled"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_QBUF, &m_v4l2DstBuffer) < 0) { |
| ALOGERR("QBuf of the JPEG buffers is failed (B2B %s)", |
| IsB2BCompression() ? "enabled" : "disabled"); |
| // Reqbufs(0) is the only way to cancel the previous queued buffer |
| StopStreaming(); |
| return false; |
| } |
| |
| SetFlag(HWJPEG_FLAG_QBUF_OUT | HWJPEG_FLAG_QBUF_CAP); |
| |
| return true; |
| } |
| |
| ssize_t CHWJpegV4L2Compressor::DQBuf(size_t *secondary_stream_size) |
| { |
| bool failed = false; |
| v4l2_buffer buffer_src, buffer_dst; |
| v4l2_plane planes_src[6], planes_dst[2]; |
| |
| ALOG_ASSERT(TestFlag(HWJPEG_FLAG_QBUF_OUT) == TestFlag(HWJPEG_FLAG_QBUF_CAP)); |
| |
| memset(&buffer_src, 0, sizeof(buffer_src)); |
| memset(&buffer_dst, 0, sizeof(buffer_dst)); |
| memset(&planes_src, 0, sizeof(planes_src)); |
| memset(&planes_dst, 0, sizeof(planes_dst)); |
| |
| buffer_src.type = m_v4l2SrcBuffer.type; |
| buffer_src.memory = m_v4l2SrcBuffer.memory; |
| buffer_src.length = m_v4l2SrcBuffer.length; |
| buffer_src.m.planes = planes_src; |
| |
| buffer_dst.type = m_v4l2DstBuffer.type; |
| buffer_dst.memory = m_v4l2DstBuffer.memory; |
| buffer_dst.length = m_v4l2DstBuffer.length; |
| buffer_dst.m.planes = planes_dst; |
| |
| if (TestFlag(HWJPEG_FLAG_QBUF_OUT) && (ioctl(GetDeviceFD(), VIDIOC_DQBUF, &buffer_src) < 0)) { |
| ALOGERR("Failed to DQBUF of the image buffer"); |
| failed = true; |
| } |
| |
| if (TestFlag(HWJPEG_FLAG_QBUF_CAP) && (ioctl(GetDeviceFD(), VIDIOC_DQBUF, &buffer_dst) < 0)) { |
| ALOGERR("Failed to DQBUF of the JPEG stream buffer"); |
| failed = true; |
| } |
| |
| ClearFlag(HWJPEG_FLAG_QBUF_OUT | HWJPEG_FLAG_QBUF_CAP); |
| |
| if (failed) |
| return -1; |
| |
| if (!!((buffer_src.flags | buffer_dst.flags) & V4L2_BUF_FLAG_ERROR)) { |
| ALOGE("Error occurred during compression"); |
| return -1; |
| } |
| |
| // We don't need to check the length of secondary stream |
| // because it will be zero if the secondary image is not processed. |
| SetStreamSize(buffer_dst.m.planes[0].bytesused, buffer_dst.m.planes[1].bytesused); |
| |
| // The driver stores the delay in usec. of JPEG compression by H/W |
| // to v4l2_buffer.reserved2. |
| m_uiHWDelay = buffer_dst.reserved2; |
| |
| return GetStreamSize(secondary_stream_size); |
| } |
| |
| ssize_t CHWJpegV4L2Compressor::WaitForCompression(size_t *secondary_stream_size) |
| { |
| return DQBuf(secondary_stream_size); |
| } |
| |
| bool CHWJpegV4L2Compressor::GetImageBuffers(int buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (m_v4l2SrcBuffer.memory != V4L2_MEMORY_DMABUF) { |
| ALOGE("Current image buffer type is not dma-buf but attempted to retrieve dma-buf buffers"); |
| return false; |
| } |
| |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("Number of planes are %u but attemts to retrieve %u buffers", |
| m_v4l2Format.fmt.pix_mp.num_planes, num_buffers); |
| return false; |
| } |
| |
| for (unsigned int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) { |
| buffers[i] = m_v4l2SrcBuffer.m.planes[i].m.fd; |
| len_buffers[i] = m_v4l2SrcBuffer.m.planes[i].length; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::GetImageBuffers(char *buffers[], size_t len_buffers[], |
| unsigned int num_buffers) |
| { |
| if (m_v4l2SrcBuffer.memory != V4L2_MEMORY_USERPTR) { |
| ALOGE("Current image buffer type is not userptr but attempted to retrieve userptr buffers"); |
| return false; |
| } |
| |
| if (num_buffers < m_v4l2Format.fmt.pix_mp.num_planes) { |
| ALOGE("Number of planes are %u but attemts to retrieve %u buffers", |
| m_v4l2Format.fmt.pix_mp.num_planes, num_buffers); |
| return false; |
| } |
| |
| for (unsigned int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) { |
| buffers[i] = reinterpret_cast<char *>(m_v4l2SrcBuffer.m.planes[i].m.userptr); |
| len_buffers[i] = m_v4l2SrcBuffer.m.planes[i].length; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::GetJpegBuffer(int *buffer, size_t *len_buffer) |
| { |
| if (m_v4l2DstBuffer.memory != V4L2_MEMORY_DMABUF) { |
| ALOGE("Current jpeg buffer type is not dma-buf but attempted to retrieve dma-buf buffer"); |
| return false; |
| } |
| |
| *buffer = m_v4l2DstBuffer.m.planes[0].m.fd; |
| *len_buffer = m_v4l2DstBuffer.m.planes[0].length; |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Compressor::GetJpegBuffer(char **buffer, size_t *len_buffer) |
| { |
| if (m_v4l2DstBuffer.memory != V4L2_MEMORY_USERPTR) { |
| ALOGE("Current jpeg buffer type is not userptr but attempted to retrieve userptr buffer"); |
| return false; |
| } |
| |
| *buffer = reinterpret_cast<char *>(m_v4l2DstBuffer.m.planes[0].m.userptr); |
| *len_buffer = m_v4l2DstBuffer.m.planes[0].length; |
| |
| return true; |
| } |
| |
| void CHWJpegV4L2Compressor::Release() |
| { |
| StopStreaming(); |
| } |
| |
| void CHWJpegV4L2Compressor::DumpInfo(bool thumb, bool btb) |
| { |
| const char *str = thumb ? "thumb" : "main"; |
| bool src_dmabuf = (m_v4l2SrcBuffer.memory == V4L2_MEMORY_DMABUF) ? true : false; |
| bool dst_dmabuf = (m_v4l2DstBuffer.memory == V4L2_MEMORY_DMABUF) ? true : false; |
| |
| ALOGI("srcbuf fmt(%u)", m_v4l2Format.fmt.pix_mp.pixelformat); |
| |
| for (int i = 0; i < m_v4l2Format.fmt.pix_mp.num_planes; i++) { |
| ALOGI("%s srcbuf[%d]: %s(%lu) bytesused(%u) len(%u)", str, i, src_dmabuf ? "fd" : "userptr", |
| src_dmabuf ? (unsigned long)m_v4l2SrcPlanes[i].m.fd : m_v4l2SrcPlanes[i].m.userptr, |
| m_v4l2SrcPlanes[i].bytesused, m_v4l2SrcPlanes[i].length); |
| } |
| |
| ALOGI("%s dstbuf: %s(%lu) bytesused(%u) len(%u) offset(%u)", str, dst_dmabuf ? "fd" : "userptr", |
| dst_dmabuf ? (unsigned long)m_v4l2DstPlanes[0].m.fd : m_v4l2DstPlanes[0].m.userptr, m_v4l2DstPlanes[0].bytesused, |
| m_v4l2DstPlanes[0].length, dst_dmabuf? 0 : m_v4l2DstPlanes[0].data_offset); |
| |
| if (btb) { |
| for (int i = m_v4l2Format.fmt.pix_mp.num_planes; i < m_v4l2Format.fmt.pix_mp.num_planes * 2; i++) { |
| ALOGI("btb thumb srcbuf[%d]: %s(%lu) bytesused(%u) len(%u)", i - m_v4l2Format.fmt.pix_mp.num_planes, src_dmabuf ? "fd" : "userptr", |
| src_dmabuf ? (unsigned long)m_v4l2SrcPlanes[i].m.fd : m_v4l2SrcPlanes[i].m.userptr, |
| m_v4l2SrcPlanes[i].bytesused, m_v4l2SrcPlanes[i].length); |
| } |
| ALOGI("btb thumb dstbuf: %s(%lu) len(%u)", dst_dmabuf ? "fd" : "userptr", |
| dst_dmabuf ? m_v4l2DstPlanes[1].m.fd : m_v4l2DstPlanes[1].m.userptr, m_v4l2DstPlanes[1].length); |
| } |
| } |
| |
| /******************************************************************************/ |
| /********* D E C O M P R E S S I O N S U P P O R T **************************/ |
| /******************************************************************************/ |
| |
| CHWJpegV4L2Decompressor::CHWJpegV4L2Decompressor() : CHWJpegDecompressor("/dev/video12") |
| { |
| m_v4l2Format.type = 0; // inidication of uninitialized state |
| |
| memset(&m_v4l2DstBuffer, 0, sizeof(m_v4l2DstBuffer)); |
| m_v4l2DstBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| |
| if (Okay()) { |
| v4l2_capability cap; |
| memset(&cap, 0, sizeof(cap)); |
| if (ioctl(GetDeviceFD(), VIDIOC_QUERYCAP, &cap) < 0) { |
| ALOGERR("Failed to query capability of /dev/video12"); |
| } else if (!!(cap.capabilities & V4L2_CAP_DEVICE_CAPS)) { |
| SetDeviceCapabilities(cap.device_caps); |
| } |
| } |
| |
| m_uiHWDelay = 0; |
| } |
| |
| CHWJpegV4L2Decompressor::~CHWJpegV4L2Decompressor() |
| { |
| CancelCapture(); |
| } |
| |
| bool CHWJpegV4L2Decompressor::PrepareCapture() |
| { |
| if (m_v4l2DstBuffer.length < m_v4l2Format.fmt.pix.sizeimage) { |
| ALOGE("The size of the buffer %u is smaller than required %u", |
| m_v4l2DstBuffer.length, m_v4l2Format.fmt.pix.sizeimage); |
| return false; |
| } |
| |
| if (TestFlag(HWJPEG_FLAG_CAPTURE_READY)) |
| return true; |
| |
| v4l2_requestbuffers reqbufs; |
| |
| memset(&reqbufs, 0, sizeof(reqbufs)); |
| reqbufs.count = 1; |
| reqbufs.memory = m_v4l2DstBuffer.memory; |
| reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs) < 0) { |
| ALOGERR("Failed to REQBUFS for the decompressed image"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMON, &reqbufs.type) < 0) { |
| ALOGERR("Failed to STREAMON for the decompressed image"); |
| reqbufs.count = 0; |
| ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs); |
| return false; |
| } |
| |
| SetFlag(HWJPEG_FLAG_CAPTURE_READY); |
| |
| return true; |
| } |
| |
| void CHWJpegV4L2Decompressor::CancelCapture() |
| { |
| if (!TestFlag(HWJPEG_FLAG_CAPTURE_READY)) |
| return; |
| |
| v4l2_requestbuffers reqbufs; |
| |
| memset(&reqbufs, 0, sizeof(reqbufs)); |
| reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| reqbufs.memory = m_v4l2DstBuffer.memory; |
| |
| ioctl(GetDeviceFD(), VIDIOC_STREAMOFF, &reqbufs.type); |
| ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &reqbufs); |
| |
| ClearFlag(HWJPEG_FLAG_CAPTURE_READY); |
| } |
| |
| bool CHWJpegV4L2Decompressor::SetImageFormat(unsigned int v4l2_fmt, |
| unsigned int width, unsigned int height) |
| { |
| // Test if new format is the same as the current configured format |
| if (m_v4l2Format.type != 0) { |
| v4l2_pix_format *p = &m_v4l2Format.fmt.pix; |
| if ((p->pixelformat == v4l2_fmt) && |
| (p->width == width) && (p->height == height)) |
| return true; |
| } |
| |
| CancelCapture(); |
| |
| memset(&m_v4l2Format, 0, sizeof(m_v4l2Format)); |
| |
| m_v4l2Format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| m_v4l2Format.fmt.pix.pixelformat = v4l2_fmt; |
| m_v4l2Format.fmt.pix.width = width; |
| m_v4l2Format.fmt.pix.height = height; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_S_FMT, &m_v4l2Format) < 0) { |
| ALOGERR("Failed to S_FMT for decompressed image (%08X,%ux%u)", |
| v4l2_fmt, width, height); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Decompressor::SetImageBuffer(char *buffer, size_t len_buffer) |
| { |
| m_v4l2DstBuffer.m.userptr = reinterpret_cast<unsigned long>(buffer); |
| m_v4l2DstBuffer.bytesused = m_v4l2Format.fmt.pix.sizeimage; |
| m_v4l2DstBuffer.length = len_buffer; |
| m_v4l2DstBuffer.memory = V4L2_MEMORY_USERPTR; |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Decompressor::SetImageBuffer(int buffer, size_t len_buffer) |
| { |
| m_v4l2DstBuffer.m.fd = buffer; |
| m_v4l2DstBuffer.bytesused = m_v4l2Format.fmt.pix.sizeimage; |
| m_v4l2DstBuffer.length = len_buffer; |
| m_v4l2DstBuffer.memory = V4L2_MEMORY_DMABUF; |
| |
| return true; |
| } |
| |
| bool CHWJpegV4L2Decompressor::PrepareStream() |
| { |
| if (TestFlag(HWJPEG_FLAG_OUTPUT_READY)) |
| return true; |
| |
| /* |
| * S_FMT for output stream is unneccessary because the driver assumes that |
| * the current mode is decompression if the capture stream is uncompressed |
| * format |
| */ |
| |
| v4l2_requestbuffers rb; |
| memset(&rb, 0, sizeof(rb)); |
| |
| rb.count = 1; |
| rb.memory = V4L2_MEMORY_USERPTR; |
| rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| |
| // REQBUFS fails if no S_FMT is not performed |
| if (ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &rb) < 0) { |
| ALOGERR("Failed to REQBUFS for the JPEG stream."); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_STREAMON, &rb.type) < 0) { |
| ALOGERR("Failed to STREAMON for the JPEG stream."); |
| |
| rb.count = 0; |
| // don't care if reqbufs(0) fails. |
| ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &rb); |
| |
| return false; |
| } |
| |
| SetFlag(HWJPEG_FLAG_OUTPUT_READY); |
| |
| return true; |
| } |
| |
| void CHWJpegV4L2Decompressor::CancelStream() |
| { |
| if (!TestFlag(HWJPEG_FLAG_OUTPUT_READY)) |
| return; |
| |
| v4l2_requestbuffers rb; |
| memset(&rb, 0, sizeof(rb)); |
| rb.count = 0; |
| rb.memory = V4L2_MEMORY_USERPTR; |
| rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| |
| // ignore error during canceling |
| ioctl(GetDeviceFD(), VIDIOC_STREAMOFF, &rb.type); |
| ioctl(GetDeviceFD(), VIDIOC_REQBUFS, &rb); |
| |
| ClearFlag(HWJPEG_FLAG_OUTPUT_READY); |
| } |
| |
| bool CHWJpegV4L2Decompressor::QBufAndWait(const char *buffer, size_t len) |
| { |
| v4l2_buffer buf; |
| memset(&buf, 0, sizeof(buf)); |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| buf.memory = V4L2_MEMORY_USERPTR; |
| buf.bytesused = len; |
| buf.m.userptr = reinterpret_cast<unsigned long>(buffer); |
| buf.length = len; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_QBUF, &buf) < 0) { |
| ALOGERR("Failed to QBUF for the JPEG stream"); |
| return false; |
| } |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_QBUF, &m_v4l2DstBuffer) < 0) { |
| CancelStream(); |
| ALOGERR("Failed to QBUF for the decompressed image"); |
| return false; |
| } |
| |
| bool ret = true; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_DQBUF, &buf) < 0) { |
| ALOGERR("Failed to DQBUF of the stream buffer"); |
| ret = false; |
| } |
| |
| buf.type = m_v4l2DstBuffer.type; |
| buf.memory = m_v4l2DstBuffer.memory; |
| |
| if (ioctl(GetDeviceFD(), VIDIOC_DQBUF, &buf) < 0) { |
| ALOGERR("Failed to DQBUF of the image buffer"); |
| ret = false; |
| } |
| |
| m_uiHWDelay = buf.reserved2; |
| |
| return ret; |
| } |
| |
| bool CHWJpegV4L2Decompressor::Decompress(const char *buffer, size_t len) |
| { |
| if (m_v4l2Format.type == 0) { |
| ALOGE("Decompressed image format is not specified"); |
| return false; |
| } |
| |
| if (m_v4l2DstBuffer.length == 0) { |
| ALOGE("Decompressed image buffer is not specified"); |
| return false; |
| } |
| |
| // Do not change the order of PrepareCapture() and PrepareStream(). |
| // Otherwise, decompression will fail. |
| if (!PrepareCapture() || !PrepareStream()) |
| return false; |
| |
| if (!QBufAndWait(buffer, len)) |
| return false; |
| |
| return true; |
| } |