653 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			653 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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;
 | |
| }
 |