diff options
author | HyunKyung Kim <hk310.kim@samsung.com> | 2019-08-12 15:27:36 +0900 |
---|---|---|
committer | HyunKyung Kim <hk310.kim@samsung.com> | 2019-09-18 19:34:02 +0900 |
commit | 6935a9b0ac01b978519d8d704dee3994440ff023 (patch) | |
tree | b221bd0957f7ca7d936d598394dfc7dbf44ecdf7 /libscaler/libscaler-v4l2.cpp | |
parent | ac1c0535583ce3eeb8f2b59431ebe510619c0416 (diff) |
Add initial source code
Change-Id: I285a55c44c9fd5d3a8abaa781ef9dc8d9d39f53a
Signed-off-by: HyunKyung Kim <hk310.kim@samsung.com>
Diffstat (limited to 'libscaler/libscaler-v4l2.cpp')
-rw-r--r-- | libscaler/libscaler-v4l2.cpp | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/libscaler/libscaler-v4l2.cpp b/libscaler/libscaler-v4l2.cpp new file mode 100644 index 0000000..8b9dd98 --- /dev/null +++ b/libscaler/libscaler-v4l2.cpp @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright@ Samsung Electronics Co. LTD + * + * 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. + */ + +/*! + * \file libscaler-v4l2.cpp + * \brief source file for Scaler HAL + * \author Cho KyongHo <pullip.cho@samsung.com> + * \date 2014/05/12 + * + * <b>Revision History: </b> + * - 2014.05.12 : Cho KyongHo (pullip.cho@samsung.com) \n + * Create + */ + +#include <cstring> +#include <cstdlib> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "libscaler-v4l2.h" +#include "libscaler-swscaler.h" + + +#define V4L2_CID_EXYNOS_BASE (V4L2_CTRL_CLASS_USER | 0x2000) +#define V4L2_CID_CSC_EQ_MODE (V4L2_CID_EXYNOS_BASE + 100) +#define V4L2_CID_CSC_EQ (V4L2_CID_EXYNOS_BASE + 101) +#define V4L2_CID_CSC_RANGE (V4L2_CID_EXYNOS_BASE + 102) +#define V4L2_CID_CONTENT_PROTECTION (V4L2_CID_EXYNOS_BASE + 201) + +void CScalerV4L2::Initialize(int instance) +{ + snprintf(m_cszNode, SC_MAX_NODENAME, SC_DEV_NODE "%d", SC_NODE(instance)); + + m_fdScaler = open(m_cszNode, O_RDWR); + if (m_fdScaler < 0) { + SC_LOGERR("Failed to open '%s'", m_cszNode); + return; + } + + m_fdValidate = -m_fdScaler; +} + +CScalerV4L2::CScalerV4L2(int instance, int allow_drm) +{ + m_fdScaler = -1; + m_iInstance = instance; + m_nRotDegree = 0; + m_fStatus = 0; + m_filter = 0; + + memset(&m_frmSrc, 0, sizeof(m_frmSrc)); + memset(&m_frmDst, 0, sizeof(m_frmDst)); + + m_frmSrc.fdAcquireFence = -1; + m_frmDst.fdAcquireFence = -1; + + m_frmSrc.name = "output"; + m_frmDst.name = "capture"; + + m_frmSrc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + m_frmDst.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + m_frameRate = 0; + + Initialize(instance); + + if(Valid()) { + if (allow_drm) + SetFlag(m_fStatus, SCF_ALLOW_DRM); + SC_LOGD("Successfully opened '%s'; returned fd %d; drmmode %s", + m_cszNode, m_fdScaler, allow_drm ? "enabled" : "disabled"); + } +} + +CScalerV4L2::~CScalerV4L2() +{ + if (m_fdScaler >= 0) + close(m_fdScaler); + + m_fdScaler = -1; +} + +bool CScalerV4L2::Stop() +{ + if (!ResetDevice(m_frmSrc)) { + SC_LOGE("Failed to stop Scaler for the output frame"); + return false; + } + + if (!ResetDevice(m_frmDst)) { + SC_LOGE("Failed to stop Scaler for the cature frame"); + return false; + } + + return true; +} + +bool CScalerV4L2::Run() +{ + if (LibScaler::UnderOne16thScaling( + m_frmSrc.crop.width, m_frmSrc.crop.height, + m_frmDst.crop.width, m_frmDst.crop.height, + m_nRotDegree)) + return RunSWScaling(); + + if (!DevSetCtrl()) + return false; + + if (!DevSetFormat()) + return false; + + if (!ReqBufs()) + return false; + + if (!StreamOn()) + return false; + + if (!QBuf()) { + Stop(); + return false; + } + + return DQBuf(); +} + +bool CScalerV4L2::SetCtrl() +{ + struct v4l2_control ctrl; + + if (TestFlag(m_fStatus, SCF_DRM_FRESH)) { + if (!Stop()) + return false; + + ctrl.id = V4L2_CID_CONTENT_PROTECTION; + ctrl.value = TestFlag(m_fStatus, SCF_DRM); + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed configure V4L2_CID_CONTENT_PROTECTION to %d", TestFlag(m_fStatus, SCF_DRM)); + return false; + } + + ClearFlag(m_fStatus, SCF_DRM_FRESH); + } else { + SC_LOGD("Skipping DRM configuration"); + } + + if (TestFlag(m_fStatus, SCF_ROTATION_FRESH)) { + if (!Stop()) + return false; + + ctrl.id = V4L2_CID_ROTATE; + ctrl.value = m_nRotDegree; + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed V4L2_CID_ROTATE with degree %d", m_nRotDegree); + return false; + } + + ctrl.id = V4L2_CID_VFLIP; + ctrl.value = TestFlag(m_fStatus, SCF_HFLIP); + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed V4L2_CID_VFLIP - %d", TestFlag(m_fStatus, SCF_VFLIP)); + return false; + } + + ctrl.id = V4L2_CID_HFLIP; + ctrl.value = TestFlag(m_fStatus, SCF_VFLIP); + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed V4L2_CID_HFLIP - %d", TestFlag(m_fStatus, SCF_HFLIP)); + return false; + } + + SC_LOGD("Successfully set CID_ROTATE(%d), CID_VFLIP(%d) and CID_HFLIP(%d)", + m_nRotDegree, TestFlag(m_fStatus, SCF_VFLIP), TestFlag(m_fStatus, SCF_HFLIP)); + ClearFlag(m_fStatus, SCF_ROTATION_FRESH); + } else { + SC_LOGD("Skipping rotation and flip setting due to no change"); + } + + if (m_filter > 0) { + if (!Stop()) + return false; + + ctrl.id = LIBSC_V4L2_CID_DNOISE_FT; + ctrl.value = m_filter; + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed LIBSC_V4L2_CID_DNOISE_FT to %d", m_filter); + return false; + } + } + + if (TestFlag(m_fStatus, SCF_CSC_FRESH)) { + if (!Stop()) + return false; + + ctrl.id = V4L2_CID_CSC_RANGE; + ctrl.value = TestFlag(m_fStatus, SCF_CSC_WIDE) ? 1 : 0; + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed V4L2_CID_CSC_RANGE to %d", TestFlag(m_fStatus, SCF_CSC_WIDE)); + return false; + } + + ctrl.id = V4L2_CID_CSC_EQ; + ctrl.value = m_colorspace; + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGERR("Failed V4L2_CID_CSC_EQ to %d", m_colorspace); + } + ClearFlag(m_fStatus, SCF_CSC_FRESH); + } + + /* This is optional, so we don't return failure. */ + if (TestFlag(m_fStatus, SCF_FRAMERATE)) { + if (!Stop()) + return false; + + ctrl.id = SC_CID_FRAMERATE; + ctrl.value = m_frameRate; + if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) { + SC_LOGD("Failed SC_CID_FRAMERATE to %d", m_frameRate); + } + ClearFlag(m_fStatus, SCF_FRAMERATE); + } + + return true; +} + +bool CScalerV4L2::DevSetCtrl() +{ + return SetCtrl(); +} + +bool CScalerV4L2::ResetDevice(FrameInfo &frm) +{ + DQBuf(frm); + + if (TestFlag(frm.flags, SCFF_STREAMING)) { + if (ioctl(m_fdScaler, VIDIOC_STREAMOFF, &frm.type) < 0) { + SC_LOGERR("Failed STREAMOFF for the %s", frm.name); + } + ClearFlag(frm.flags, SCFF_STREAMING); + } + + SC_LOGD("VIDIC_STREAMOFF is successful for the %s", frm.name); + + if (TestFlag(frm.flags, SCFF_REQBUFS)) { + v4l2_requestbuffers reqbufs; + memset(&reqbufs, 0, sizeof(reqbufs)); + reqbufs.type = frm.type; + reqbufs.memory = frm.memory; + if (ioctl(m_fdScaler, VIDIOC_REQBUFS, &reqbufs) < 0 ) { + SC_LOGERR("Failed to REQBUFS(0) for the %s", frm.name); + } + + ClearFlag(frm.flags, SCFF_REQBUFS); + } + + SC_LOGD("VIDIC_REQBUFS(0) is successful for the %s", frm.name); + + return true; +} + +bool CScalerV4L2::DevSetFormat(FrameInfo &frm) +{ + + if (!TestFlag(frm.flags, SCFF_BUF_FRESH)) { + SC_LOGD("Skipping S_FMT for the %s since it is already done", frm.name); + return true; + } + + if (!ResetDevice(frm)) { + SC_LOGE("Failed to VIDIOC_S_FMT for the %s", frm.name); + return false; + } + + v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = frm.type; + fmt.fmt.pix_mp.pixelformat = frm.color_format; + fmt.fmt.pix_mp.width = frm.width; + fmt.fmt.pix_mp.height = frm.height; + + if (TestFlag(frm.flags, SCFF_PREMULTIPLIED)) { +#ifdef SCALER_USE_PREMUL_FMT + fmt.fmt.pix_mp.flags = V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; +#else + fmt.fmt.pix_mp.reserved[1] = SC_V4L2_FMT_PREMULTI_FLAG; +#endif + } + + if (ioctl(m_fdScaler, VIDIOC_S_FMT, &fmt) < 0) { + SC_LOGERR("Failed S_FMT(fmt: %d, w:%d, h:%d) for the %s", + fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, + frm.name); + return false; + } + + // returned fmt.fmt.pix_mp.num_planes and fmt.fmt.pix_mp.plane_fmt[i].sizeimage + frm.out_num_planes = fmt.fmt.pix_mp.num_planes; + + for (int i = 0; i < frm.out_num_planes; i++) + frm.out_plane_size[i] = fmt.fmt.pix_mp.plane_fmt[i].sizeimage; + + v4l2_crop crop; + crop.type = frm.type; + crop.c = frm.crop; + + if (ioctl(m_fdScaler, VIDIOC_S_CROP, &crop) < 0) { + SC_LOGERR("Failed S_CROP(fmt: %d, l:%d, t:%d, w:%d, h:%d) for the %s", + crop.type, crop.c.left, crop.c.top, crop.c.width, crop.c.height, + frm.name); + return false; + } + + if (frm.out_num_planes > SC_MAX_PLANES) { + SC_LOGE("Number of planes exceeds %d of %s", frm.out_num_planes, frm.name); + return false; + } + + ClearFlag(frm.flags, SCFF_BUF_FRESH); + + SC_LOGD("Successfully S_FMT and S_CROP for the %s", frm.name); + + return true; +} + +bool CScalerV4L2::DevSetFormat() +{ + if (!DevSetFormat(m_frmSrc)) + return false; + + return DevSetFormat(m_frmDst); +} + +bool CScalerV4L2::QBuf(FrameInfo &frm, int *pfdReleaseFence) +{ + v4l2_buffer buffer; + v4l2_plane planes[SC_MAX_PLANES]; + + if (!TestFlag(frm.flags, SCFF_REQBUFS)) { + SC_LOGE("Trying to QBUF without REQBUFS for %s is not allowed", + frm.name); + return false; + } + + if (!DQBuf(frm)) + return false; + + memset(&buffer, 0, sizeof(buffer)); + memset(&planes, 0, sizeof(planes)); + + buffer.type = frm.type; + buffer.memory = frm.memory; + buffer.index = 0; + buffer.length = frm.out_num_planes; + + if (pfdReleaseFence) { + buffer.flags = V4L2_BUF_FLAG_USE_SYNC; + buffer.reserved = frm.fdAcquireFence; + } + + buffer.m.planes = planes; + for (unsigned long i = 0; i < buffer.length; i++) { + planes[i].length = frm.out_plane_size[i]; + if (V4L2_TYPE_IS_OUTPUT(buffer.type)) + planes[i].bytesused = planes[i].length; + if (buffer.memory == V4L2_MEMORY_DMABUF) + planes[i].m.fd = static_cast<__s32>(reinterpret_cast<long>(frm.addr[i])); + else + planes[i].m.userptr = reinterpret_cast<unsigned long>(frm.addr[i]); + } + + + if (ioctl(m_fdScaler, VIDIOC_QBUF, &buffer) < 0) { + SC_LOGERR("Failed to QBUF for the %s", frm.name); + return false; + } + + SetFlag(frm.flags, SCFF_QBUF); + + if (pfdReleaseFence) { + if (frm.fdAcquireFence >= 0) + close(frm.fdAcquireFence); + frm.fdAcquireFence = -1; + + *pfdReleaseFence = static_cast<int>(buffer.reserved); + } + + SC_LOGD("Successfully QBUF for the %s", frm.name); + + return true; +} + +bool CScalerV4L2::ReqBufs(FrameInfo &frm) +{ + v4l2_requestbuffers reqbufs; + + if (TestFlag(frm.flags, SCFF_REQBUFS)) { + SC_LOGD("Skipping REQBUFS for the %s since it is already done", frm.name); + return true; + } + + memset(&reqbufs, 0, sizeof(reqbufs)); + + reqbufs.type = frm.type; + reqbufs.memory = frm.memory; + reqbufs.count = 1; + + if (ioctl(m_fdScaler, VIDIOC_REQBUFS, &reqbufs) < 0) { + SC_LOGERR("Failed to REQBUFS for the %s", frm.name); + return false; + } + + SetFlag(frm.flags, SCFF_REQBUFS); + + SC_LOGD("Successfully REQBUFS for the %s", frm.name); + + return true; +} + +bool CScalerV4L2::SetRotate(int rot, int flip_h, int flip_v) +{ + if ((rot % 90) != 0) { + SC_LOGE("Rotation of %d degree is not supported", rot); + return false; + } + + SetRotDegree(rot); + + if (flip_h) + SetFlag(m_fStatus, SCF_VFLIP); + else + ClearFlag(m_fStatus, SCF_VFLIP); + + if (flip_v) + SetFlag(m_fStatus, SCF_HFLIP); + else + ClearFlag(m_fStatus, SCF_HFLIP); + + SetFlag(m_fStatus, SCF_ROTATION_FRESH); + + return true; +} + +bool CScalerV4L2::StreamOn(FrameInfo &frm) +{ + if (!TestFlag(frm.flags, SCFF_REQBUFS)) { + SC_LOGE("Trying to STREAMON without REQBUFS for %s is not allowed", + frm.name); + return false; + } + + if (!TestFlag(frm.flags, SCFF_STREAMING)) { + if (ioctl(m_fdScaler, VIDIOC_STREAMON, &frm.type) < 0 ) { + SC_LOGERR("Failed StreamOn for the %s", frm.name); + return false; + } + + SetFlag(frm.flags, SCFF_STREAMING); + + SC_LOGD("Successfully VIDIOC_STREAMON for the %s", frm.name); + } + + return true; +} + +bool CScalerV4L2::DQBuf(FrameInfo &frm) +{ + if (!TestFlag(frm.flags, SCFF_QBUF)) + return true; + + v4l2_buffer buffer; + v4l2_plane plane[SC_NUM_OF_PLANES]; + + memset(&buffer, 0, sizeof(buffer)); + + buffer.type = frm.type; + buffer.memory = frm.memory; + + if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) { + memset(plane, 0, sizeof(plane)); + + buffer.length = frm.out_num_planes; + buffer.m.planes = plane; + } + + ClearFlag(frm.flags, SCFF_QBUF); + + if (ioctl(m_fdScaler, VIDIOC_DQBUF, &buffer) < 0 ) { + SC_LOGERR("Failed to DQBuf the %s", frm.name); + return false; + } + + if (buffer.flags & V4L2_BUF_FLAG_ERROR) { + SC_LOGE("Error occurred while processing streaming data"); + return false; + } + + SC_LOGD("Successfully VIDIOC_DQBUF for the %s", frm.name); + + return true; +} + +static bool GetBuffer(CScalerV4L2::FrameInfo &frm, char *addr[]) +{ + for (int i = 0; i < frm.out_num_planes; i++) { + if (frm.memory == V4L2_MEMORY_DMABUF) { + addr[i] = reinterpret_cast<char *>(mmap(NULL, frm.out_plane_size[i], + PROT_READ | PROT_WRITE, MAP_SHARED, + static_cast<int>(reinterpret_cast<long>(frm.addr[i])), 0)); + if (addr[i] == MAP_FAILED) { + SC_LOGE("Failed to map FD %ld", reinterpret_cast<long>(frm.addr[i])); + while (i-- > 0) + munmap(addr[i], frm.out_plane_size[i]); + return false; + } + } else { + addr[i] = reinterpret_cast<char *>(frm.addr[i]); + } + } + + return true; +} + +static void PutBuffer(CScalerV4L2::FrameInfo &frm, char *addr[]) +{ + for (int i = 0; i < frm.out_num_planes; i++) { + if (frm.memory == V4L2_MEMORY_DMABUF) { + munmap(addr[i], frm.out_plane_size[i]); + } + } +} + +bool CScalerV4L2::RunSWScaling() +{ + if (m_frmSrc.color_format != m_frmDst.color_format) { + SC_LOGE("Source and target image format must be the same"); + return false; + } + + if (m_nRotDegree != 0) { + SC_LOGE("Rotation is not allowed for S/W Scaling"); + return false; + } + + SC_LOGI("Running S/W Scaler: %dx%d -> %dx%d", + m_frmSrc.crop.width, m_frmSrc.crop.height, + m_frmDst.crop.width, m_frmDst.crop.height); + + CScalerSW *swsc; + char *src[3], *dst[3]; + + switch (m_frmSrc.color_format) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + m_frmSrc.out_num_planes = 1; + m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height * 2; + m_frmDst.out_num_planes = 1; + m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height * 2; + + if (!GetBuffer(m_frmSrc, src)) + return false; + + if (!GetBuffer(m_frmDst, dst)) { + PutBuffer(m_frmSrc, src); + return false; + } + + swsc = new CScalerSW_YUYV(src[0], dst[0]); + break; + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + m_frmSrc.out_num_planes = 2; + m_frmDst.out_num_planes = 2; + m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height; + m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height; + m_frmSrc.out_plane_size[1] = m_frmSrc.out_plane_size[0] / 2; + m_frmDst.out_plane_size[1] = m_frmDst.out_plane_size[0] / 2; + + if (!GetBuffer(m_frmSrc, src)) + return false; + + if (!GetBuffer(m_frmDst, dst)) { + PutBuffer(m_frmSrc, src); + return false; + } + + swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + m_frmSrc.out_num_planes = 1; + m_frmDst.out_num_planes = 1; + m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height; + m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height; + m_frmSrc.out_plane_size[0] += m_frmSrc.out_plane_size[0] / 2; + m_frmDst.out_plane_size[0] += m_frmDst.out_plane_size[0] / 2; + + if (!GetBuffer(m_frmSrc, src)) + return false; + + if (!GetBuffer(m_frmDst, dst)) { + PutBuffer(m_frmSrc, src); + return false; + } + + src[1] = src[0] + m_frmSrc.width * m_frmSrc.height; + dst[1] = dst[0] + m_frmDst.width * m_frmDst.height; + + swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]); + break; + case V4L2_PIX_FMT_UYVY: // TODO: UYVY is not implemented yet. + default: + SC_LOGE("Format %x is not supported", m_frmSrc.color_format); + return false; + } + + if (swsc == NULL) { + SC_LOGE("Failed to allocate SW Scaler"); + PutBuffer(m_frmSrc, src); + PutBuffer(m_frmDst, dst); + return false; + } + + swsc->SetSrcRect(m_frmSrc.crop.left, m_frmSrc.crop.top, + m_frmSrc.crop.width, m_frmSrc.crop.height, m_frmSrc.width); + + swsc->SetDstRect(m_frmDst.crop.left, m_frmDst.crop.top, + m_frmDst.crop.width, m_frmDst.crop.height, m_frmDst.width); + + bool ret = swsc->Scale(); + + delete swsc; + + PutBuffer(m_frmSrc, src); + PutBuffer(m_frmDst, dst); + + return ret; +} |