mirror of
https://github.com/Motion-Project/motion.git
synced 2026-05-10 07:46:43 -04:00
1386 lines
45 KiB
C++
1386 lines
45 KiB
C++
/*
|
|
* This file is part of MotionPlus.
|
|
*
|
|
* MotionPlus is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* MotionPlus is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with MotionPlus. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright 2020-2022 MotionMrDave@gmail.com
|
|
*/
|
|
|
|
#include "motionplus.hpp"
|
|
#include "conf.hpp"
|
|
#include "logger.hpp"
|
|
#include "util.hpp"
|
|
#include "rotate.hpp"
|
|
#include "video_common.hpp"
|
|
#include "video_v4l2.hpp"
|
|
#include <sys/mman.h>
|
|
|
|
|
|
#define MMAP_BUFFERS 4
|
|
#define MIN_MMAP_BUFFERS 2
|
|
#define V4L2_PALETTE_COUNT_MAX 21
|
|
|
|
#ifdef HAVE_V4L2
|
|
|
|
static void v4l2_palette_init(palette_item *palette_array)
|
|
{
|
|
int indx;
|
|
|
|
/* When adding here, update the max defined as V4L2_PALETTE_COUNT_MAX above */
|
|
palette_array[0].v4l2id = V4L2_PIX_FMT_SN9C10X;
|
|
palette_array[1].v4l2id = V4L2_PIX_FMT_SBGGR16;
|
|
palette_array[2].v4l2id = V4L2_PIX_FMT_SBGGR8;
|
|
palette_array[3].v4l2id = V4L2_PIX_FMT_SPCA561;
|
|
palette_array[4].v4l2id = V4L2_PIX_FMT_SGBRG8;
|
|
palette_array[5].v4l2id = V4L2_PIX_FMT_SGRBG8;
|
|
palette_array[6].v4l2id = V4L2_PIX_FMT_PAC207;
|
|
palette_array[7].v4l2id = V4L2_PIX_FMT_PJPG;
|
|
palette_array[8].v4l2id = V4L2_PIX_FMT_MJPEG;
|
|
palette_array[9].v4l2id = V4L2_PIX_FMT_JPEG;
|
|
palette_array[10].v4l2id = V4L2_PIX_FMT_RGB24;
|
|
palette_array[11].v4l2id = V4L2_PIX_FMT_SPCA501;
|
|
palette_array[12].v4l2id = V4L2_PIX_FMT_SPCA505;
|
|
palette_array[13].v4l2id = V4L2_PIX_FMT_SPCA508;
|
|
palette_array[14].v4l2id = V4L2_PIX_FMT_UYVY;
|
|
palette_array[15].v4l2id = V4L2_PIX_FMT_YUYV;
|
|
palette_array[16].v4l2id = V4L2_PIX_FMT_YUV422P;
|
|
palette_array[17].v4l2id = V4L2_PIX_FMT_YUV420; /* most efficient for motion */
|
|
palette_array[18].v4l2id = V4L2_PIX_FMT_Y10;
|
|
palette_array[19].v4l2id = V4L2_PIX_FMT_Y12;
|
|
palette_array[20].v4l2id = V4L2_PIX_FMT_GREY;
|
|
palette_array[21].v4l2id = V4L2_PIX_FMT_SRGGB8;
|
|
|
|
for (indx = 0; indx <= V4L2_PALETTE_COUNT_MAX; indx++) {
|
|
sprintf(palette_array[indx].fourcc ,"%c%c%c%c"
|
|
,palette_array[indx].v4l2id >> 0
|
|
,palette_array[indx].v4l2id >> 8
|
|
,palette_array[indx].v4l2id >> 16
|
|
,palette_array[indx].v4l2id >> 24);
|
|
}
|
|
|
|
}
|
|
|
|
/* Execute the request to the device */
|
|
static int xioctl(ctx_v4l2cam *v4l2cam, unsigned long request, void *arg)
|
|
{
|
|
int retcd;
|
|
|
|
if (v4l2cam->fd_device < 0) {
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
retcd = ioctl(v4l2cam->fd_device, request, arg);
|
|
} while (-1 == retcd && EINTR == errno && !v4l2cam->finish);
|
|
|
|
return retcd;
|
|
}
|
|
|
|
static void v4l2_device_close(ctx_cam *cam)
|
|
{
|
|
close(cam->v4l2cam->fd_device);
|
|
cam->v4l2cam->fd_device = -1;
|
|
}
|
|
|
|
/* Get the count of how many controls and menu items the device supports */
|
|
static void v4l2_ctrls_count(ctx_cam *cam)
|
|
{
|
|
int indx;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_queryctrl vid_ctrl;
|
|
struct v4l2_querymenu vid_menu;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
v4l2cam->devctrl_count = 0;
|
|
|
|
memset(&vid_ctrl, 0, sizeof(struct v4l2_queryctrl));
|
|
vid_ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
|
|
while (xioctl (v4l2cam, VIDIOC_QUERYCTRL, &vid_ctrl) == 0) {
|
|
if (vid_ctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
|
|
vid_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
continue;
|
|
}
|
|
v4l2cam->devctrl_count++;
|
|
if (vid_ctrl.type == V4L2_CTRL_TYPE_MENU) {
|
|
for (indx = vid_ctrl.minimum; indx <= vid_ctrl.maximum; indx++) {
|
|
memset(&vid_menu, 0, sizeof(struct v4l2_querymenu));
|
|
vid_menu.id = vid_ctrl.id;
|
|
vid_menu.index = indx;
|
|
if (xioctl(v4l2cam, VIDIOC_QUERYMENU, &vid_menu) == 0) {
|
|
v4l2cam->devctrl_count++;
|
|
}
|
|
}
|
|
}
|
|
vid_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
}
|
|
|
|
}
|
|
|
|
/* Print the device controls to the log */
|
|
static void v4l2_ctrls_log(ctx_cam *cam)
|
|
{
|
|
int indx;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
if (v4l2cam->devctrl_count != 0 ) {
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, _("---------Controls---------"));
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, _(" V4L2 ID Name and Range"));
|
|
for (indx = 0; indx < v4l2cam->devctrl_count; indx++) {
|
|
if (v4l2cam->devctrl_array[indx].ctrl_menuitem) {
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, " %s %s"
|
|
,v4l2cam->devctrl_array[indx].ctrl_iddesc
|
|
,v4l2cam->devctrl_array[indx].ctrl_name);
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "%s %s, %d to %d"
|
|
,v4l2cam->devctrl_array[indx].ctrl_iddesc
|
|
,v4l2cam->devctrl_array[indx].ctrl_name
|
|
,v4l2cam->devctrl_array[indx].ctrl_minimum
|
|
,v4l2cam->devctrl_array[indx].ctrl_maximum);
|
|
}
|
|
}
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "--------------------------");
|
|
}
|
|
|
|
}
|
|
|
|
/* Get names of the controls and menu items the device supports */
|
|
static void v4l2_ctrls_list(ctx_cam *cam)
|
|
{
|
|
int indx, indx_ctrl;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_queryctrl vid_ctrl;
|
|
struct v4l2_querymenu vid_menu;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
v4l2cam->devctrl_array = NULL;
|
|
if (v4l2cam->devctrl_count == 0) {
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, _("No Controls found for device"));
|
|
return;
|
|
}
|
|
|
|
v4l2cam->devctrl_array =(ctx_v4l2cam_ctrl*) malloc(v4l2cam->devctrl_count * sizeof(ctx_v4l2cam_ctrl));
|
|
|
|
memset(&vid_ctrl, 0, sizeof(struct v4l2_queryctrl));
|
|
vid_ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
indx_ctrl = 0;
|
|
while (xioctl (v4l2cam, VIDIOC_QUERYCTRL, &vid_ctrl) == 0) {
|
|
if (vid_ctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) {
|
|
vid_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
continue;
|
|
}
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_id = vid_ctrl.id;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_type = vid_ctrl.type;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_default = vid_ctrl.default_value;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_currval = vid_ctrl.default_value;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_newval = vid_ctrl.default_value;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_menuitem = false;
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_name =(char*) malloc(32);
|
|
sprintf(v4l2cam->devctrl_array[indx_ctrl].ctrl_name,"%s",vid_ctrl.name);
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_iddesc =(char*) malloc(15);
|
|
sprintf(v4l2cam->devctrl_array[indx_ctrl].ctrl_iddesc,"ID%08d",vid_ctrl.id);
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_minimum = vid_ctrl.minimum;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_maximum = vid_ctrl.maximum;
|
|
|
|
if (vid_ctrl.type == V4L2_CTRL_TYPE_MENU) {
|
|
for (indx = vid_ctrl.minimum; indx <= vid_ctrl.maximum; indx++) {
|
|
memset(&vid_menu, 0, sizeof(struct v4l2_querymenu));
|
|
vid_menu.id = vid_ctrl.id;
|
|
vid_menu.index = indx;
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_QUERYMENU, &vid_menu) == 0) {
|
|
indx_ctrl++;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_id = vid_ctrl.id;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_type = 0;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_menuitem = true;
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_name =(char*) malloc(32);
|
|
sprintf(v4l2cam->devctrl_array[indx_ctrl].ctrl_name,"%s",vid_menu.name);
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_iddesc =(char*) malloc(40);
|
|
sprintf(v4l2cam->devctrl_array[indx_ctrl].ctrl_iddesc,"menu item: Value %d",indx);
|
|
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_minimum = 0;
|
|
v4l2cam->devctrl_array[indx_ctrl].ctrl_maximum = 0;
|
|
}
|
|
}
|
|
}
|
|
indx_ctrl++;
|
|
vid_ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
|
|
}
|
|
|
|
v4l2_ctrls_log(cam);
|
|
|
|
}
|
|
|
|
/* Set the control array items to the device */
|
|
static void v4l2_ctrls_set(ctx_cam *cam)
|
|
{
|
|
int indx_dev, retcd;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
ctx_v4l2cam_ctrl *devitem;
|
|
struct v4l2_control vid_ctrl;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
for (indx_dev = 0; indx_dev < v4l2cam->devctrl_count; indx_dev++) {
|
|
devitem=&v4l2cam->devctrl_array[indx_dev];
|
|
if (!devitem->ctrl_menuitem) {
|
|
if (devitem->ctrl_currval != devitem->ctrl_newval) {
|
|
memset(&vid_ctrl, 0, sizeof (struct v4l2_control));
|
|
vid_ctrl.id = devitem->ctrl_id;
|
|
vid_ctrl.value = devitem->ctrl_newval;
|
|
retcd = xioctl(v4l2cam, VIDIOC_S_CTRL, &vid_ctrl);
|
|
if (retcd < 0) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("setting control %s \"%s\" to %d failed with return code %d")
|
|
,devitem->ctrl_iddesc, devitem->ctrl_name
|
|
,devitem->ctrl_newval,retcd);
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO
|
|
,_("Set control \"%s\" to value %d")
|
|
,devitem->ctrl_name, devitem->ctrl_newval);
|
|
devitem->ctrl_currval = devitem->ctrl_newval;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int v4l2_parms_set(ctx_cam *cam)
|
|
{
|
|
int indx_dev, indx_user;
|
|
ctx_params_item *usritem;
|
|
ctx_v4l2cam_ctrl *devitem;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
if (v4l2cam->devctrl_count == 0) {
|
|
v4l2cam->params->update_params = false;
|
|
return 0;
|
|
}
|
|
|
|
for (indx_dev = 0; indx_dev < v4l2cam->devctrl_count; indx_dev++) {
|
|
|
|
devitem=&v4l2cam->devctrl_array[indx_dev];
|
|
devitem->ctrl_newval = devitem->ctrl_default;
|
|
|
|
for (indx_user = 0; indx_user < v4l2cam->params->params_count; indx_user++){
|
|
|
|
usritem=&v4l2cam->params->params_array[indx_user];
|
|
|
|
if ((mystrceq(devitem->ctrl_iddesc,usritem->param_name)) ||
|
|
(mystrceq(devitem->ctrl_name ,usritem->param_name))) {
|
|
switch (devitem->ctrl_type) {
|
|
case V4L2_CTRL_TYPE_MENU:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_CTRL_TYPE_INTEGER:
|
|
if (atoi(usritem->param_value) < devitem->ctrl_minimum) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("%s control option value %s is below minimum. Skipping...")
|
|
,devitem->ctrl_name, usritem->param_value, devitem->ctrl_minimum);
|
|
} else if (atoi(usritem->param_value) > devitem->ctrl_maximum) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("%s control option value %s is above maximum. Skipping...")
|
|
,devitem->ctrl_name, usritem->param_value, devitem->ctrl_maximum);
|
|
} else {
|
|
devitem->ctrl_newval = atoi(usritem->param_value);
|
|
}
|
|
break;
|
|
case V4L2_CTRL_TYPE_BOOLEAN:
|
|
devitem->ctrl_newval = usritem->param_value ? 1 : 0;
|
|
break;
|
|
default:
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("control type not supported yet"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Set the device to the input number requested by user */
|
|
static void v4l2_set_input(ctx_cam *cam)
|
|
{
|
|
int indx, spec;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_input input;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
spec = -1;
|
|
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
|
|
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"input")) {
|
|
spec = atoi(cam->v4l2cam->params->params_array[indx].param_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(&input, 0, sizeof (struct v4l2_input));
|
|
if (spec == -1) {
|
|
input.index = 0;
|
|
} else {
|
|
input.index = spec;
|
|
}
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_ENUMINPUT, &input) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Unable to query input %d."
|
|
" VIDIOC_ENUMINPUT, if you use a WEBCAM change input value in conf by -1")
|
|
,input.index);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_("Name = \"%s\", type 0x%08X, status %08x")
|
|
,input.name, input.type, input.status);
|
|
|
|
if (input.type & V4L2_INPUT_TYPE_TUNER) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Name = \"%s\",- TUNER"), input.name);
|
|
}
|
|
|
|
if (input.type & V4L2_INPUT_TYPE_CAMERA) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO,_("Name = \"%s\"- CAMERA"), input.name);
|
|
}
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_S_INPUT, &input.index) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
, _("Error selecting input %d VIDIOC_S_INPUT"), input.index);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
v4l2cam->device_type = input.type;
|
|
v4l2cam->device_tuner = input.tuner;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Set the video standard(norm) for the device to the user requested value*/
|
|
static void v4l2_set_norm(ctx_cam *cam)
|
|
{
|
|
int indx, spec;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_standard standard;
|
|
v4l2_std_id std_id;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
spec = 1;
|
|
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
|
|
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"norm")) {
|
|
spec = atoi(cam->v4l2cam->params->params_array[indx].param_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_G_STD, &std_id) == -1) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_("Device does not support specifying PAL/NTSC norm"));
|
|
return;
|
|
}
|
|
|
|
if (std_id) {
|
|
memset(&standard, 0, sizeof(struct v4l2_standard));
|
|
standard.index = 0;
|
|
|
|
while (xioctl(v4l2cam, VIDIOC_ENUMSTD, &standard) == 0) {
|
|
if (standard.id & std_id) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("- video standard %s"), standard.name);
|
|
}
|
|
standard.index++;
|
|
}
|
|
|
|
switch (spec) {
|
|
case 1:
|
|
std_id = V4L2_STD_NTSC;
|
|
break;
|
|
case 2:
|
|
std_id = V4L2_STD_SECAM;
|
|
break;
|
|
default:
|
|
std_id = V4L2_STD_PAL;
|
|
}
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_S_STD, &std_id) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error selecting standard method %d VIDIOC_S_STD")
|
|
,(int)std_id);
|
|
}
|
|
|
|
if (std_id == V4L2_STD_NTSC) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Video standard set to NTSC"));
|
|
} else if (std_id == V4L2_STD_SECAM) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Video standard set to SECAM"));
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Video standard set to PAL"));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Set the frequency on the device to the user requested value */
|
|
static void v4l2_set_frequency(ctx_cam *cam)
|
|
{
|
|
int indx;
|
|
long spec;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_tuner tuner;
|
|
struct v4l2_frequency freq;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
spec = 0;
|
|
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
|
|
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"frequency")) {
|
|
spec = atol(cam->v4l2cam->params->params_array[indx].param_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If this input is attached to a tuner, set the frequency. */
|
|
if (v4l2cam->device_type & V4L2_INPUT_TYPE_TUNER) {
|
|
/* Query the tuners capabilities. */
|
|
memset(&tuner, 0, sizeof(struct v4l2_tuner));
|
|
tuner.index = v4l2cam->device_tuner;
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_G_TUNER, &tuner) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("tuner %d VIDIOC_G_TUNER"), tuner.index);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Set tuner %d"), tuner.index);
|
|
|
|
/* Set the frequency. */
|
|
memset(&freq, 0, sizeof(struct v4l2_frequency));
|
|
freq.tuner = v4l2cam->device_tuner;
|
|
freq.type = V4L2_TUNER_ANALOG_TV;
|
|
freq.frequency = (spec / 1000) * 16;
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_S_FREQUENCY, &freq) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("freq %ul VIDIOC_S_FREQUENCY"), freq.frequency);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Set Frequency to %ul"), freq.frequency);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int v4l2_pixfmt_try(ctx_cam *cam, uint pixformat)
|
|
{
|
|
int retcd;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_format *fmt = &v4l2cam->fmt;
|
|
|
|
memset(fmt, 0, sizeof(struct v4l2_format));
|
|
|
|
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
fmt->fmt.pix.width = v4l2cam->width;
|
|
fmt->fmt.pix.height = v4l2cam->height;
|
|
fmt->fmt.pix.pixelformat = pixformat;
|
|
fmt->fmt.pix.field = V4L2_FIELD_ANY;
|
|
|
|
retcd = xioctl(v4l2cam, VIDIOC_TRY_FMT, fmt);
|
|
if ((retcd == -1) || (fmt->fmt.pix.pixelformat != pixformat)) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Unable to use palette %c%c%c%c (%dx%d)")
|
|
,pixformat >> 0, pixformat >> 8
|
|
,pixformat >> 16, pixformat >> 24
|
|
,v4l2cam->width, v4l2cam->height);
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Testing palette %c%c%c%c (%dx%d)")
|
|
,pixformat >> 0, pixformat >> 8
|
|
,pixformat >> 16, pixformat >> 24
|
|
,v4l2cam->width, v4l2cam->height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int v4l2_pixfmt_stride(ctx_cam *cam)
|
|
{
|
|
int wd, bpl, wps;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_format *fmt = &v4l2cam->fmt;
|
|
|
|
v4l2cam->width = (int)fmt->fmt.pix.width;
|
|
v4l2cam->height = (int)fmt->fmt.pix.height;
|
|
|
|
bpl = (int)fmt->fmt.pix.bytesperline;
|
|
wd = v4l2cam->width;
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
, _("Checking image size %dx%d with stride %d")
|
|
, v4l2cam->width, v4l2cam->height, bpl);
|
|
|
|
if (bpl == 0) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
, _("No stride value provided from device."));
|
|
return 0;
|
|
}
|
|
|
|
if (wd > bpl) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
, _("Width(%d) must be less than stride(%d)"), wd, bpl);
|
|
return -1;
|
|
}
|
|
|
|
/* For perfect multiples of width and stride, no adjustment needed */
|
|
if ((wd == bpl) || ((bpl % wd) == 0)) {
|
|
return 0;
|
|
}
|
|
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
, _("The image width(%d) is not multiple of the stride(%d)")
|
|
, wd, bpl);
|
|
|
|
/* Width per stride */
|
|
wps = bpl / wd;
|
|
if (wps < 1) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
, _("Impossible condition: Width(%d), Stride(%d), Per stride(%d)")
|
|
, wd, bpl, wps);
|
|
}
|
|
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
, _("Image width will be padded %d bytes"), ((bpl % wd)/wps));
|
|
|
|
v4l2cam->width = wd + ((bpl % wd)/wps);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int v4l2_pixfmt_adjust(ctx_cam *cam)
|
|
{
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_format *fmt = &v4l2cam->fmt;
|
|
|
|
if (fmt->fmt.pix.width != (uint)v4l2cam->width ||
|
|
fmt->fmt.pix.height != (uint)v4l2cam->height) {
|
|
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("Adjusting resolution from %ix%i to %ix%i.")
|
|
,v4l2cam->width, v4l2cam->height
|
|
,fmt->fmt.pix.width
|
|
,fmt->fmt.pix.height);
|
|
|
|
v4l2cam->width = fmt->fmt.pix.width;
|
|
v4l2cam->height = fmt->fmt.pix.height;
|
|
|
|
if ((v4l2cam->width % 8) || (v4l2cam->height % 8)) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("Adjusted resolution not modulo 8."));
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("Specify different palette or width/height in config file."));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set the pixel format on the device */
|
|
static int v4l2_pixfmt_set(ctx_cam *cam, unsigned int pixformat)
|
|
{
|
|
int retcd;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_format *fmt = &v4l2cam->fmt;
|
|
|
|
retcd = v4l2_pixfmt_try(cam, pixformat);
|
|
if (retcd != 0) {
|
|
return -1;
|
|
}
|
|
retcd = v4l2_pixfmt_stride(cam);
|
|
if (retcd != 0) {
|
|
return -1;
|
|
}
|
|
retcd = v4l2_pixfmt_adjust(cam);
|
|
if (retcd != 0) {
|
|
return -1;
|
|
}
|
|
retcd = xioctl(v4l2cam, VIDIOC_S_FMT, fmt);
|
|
if (retcd == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error setting pixel format."));
|
|
return -1;
|
|
}
|
|
|
|
v4l2cam->pixfmt_src = pixformat;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Using palette %c%c%c%c (%dx%d)")
|
|
,pixformat >> 0 , pixformat >> 8
|
|
,pixformat >> 16, pixformat >> 24
|
|
,v4l2cam->width, v4l2cam->height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void v4l2_params_check(ctx_cam *cam)
|
|
{
|
|
int indx, spec;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
if (v4l2cam->width % 8) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("config image width (%d) is not modulo 8"), v4l2cam->width);
|
|
v4l2cam->width = v4l2cam->width - (v4l2cam->width % 8) + 8;
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
, _("Adjusting to width (%d)"), v4l2cam->width);
|
|
}
|
|
|
|
if (v4l2cam->height % 8) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("config image height (%d) is not modulo 8"), v4l2cam->height);
|
|
v4l2cam->height = v4l2cam->height - (v4l2cam->height % 8) + 8;
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("Adjusting to height (%d)"), v4l2cam->height);
|
|
}
|
|
|
|
spec = 17;
|
|
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
|
|
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"palette")) {
|
|
spec = atoi(cam->v4l2cam->params->params_array[indx].param_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((spec < 0) || (spec > V4L2_PALETTE_COUNT_MAX)) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("Invalid palette. Changing to default"));
|
|
util_parms_add(cam->v4l2cam->params,"palette","17");
|
|
}
|
|
|
|
}
|
|
|
|
/*List camera palettes and return index of one that Motionplus supports*/
|
|
static int v4l2_pixfmt_list(ctx_cam *cam, palette_item *palette_array)
|
|
{
|
|
int v4l2_pal, indx_palette, indx;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_fmtdesc fmtd;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Supported palettes:"));
|
|
|
|
v4l2_pal = 0;
|
|
memset(&fmtd, 0, sizeof(struct v4l2_fmtdesc));
|
|
fmtd.index = v4l2_pal;
|
|
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
indx_palette = -1; /* -1 says not yet selected */
|
|
|
|
while (xioctl(v4l2cam, VIDIOC_ENUM_FMT, &fmtd) != -1) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
, "(%i) %c%c%c%c (%s)", v4l2_pal
|
|
, fmtd.pixelformat >> 0, fmtd.pixelformat >> 8
|
|
, fmtd.pixelformat >> 16, fmtd.pixelformat >> 24
|
|
, fmtd.description);
|
|
for (indx = 0; indx <= V4L2_PALETTE_COUNT_MAX; indx++) {
|
|
if (palette_array[indx].v4l2id == fmtd.pixelformat) {
|
|
indx_palette = indx;
|
|
}
|
|
}
|
|
|
|
v4l2_pal++;
|
|
memset(&fmtd, 0, sizeof(struct v4l2_fmtdesc));
|
|
fmtd.index = v4l2_pal;
|
|
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
}
|
|
|
|
return indx_palette;
|
|
}
|
|
|
|
/* Find and select the pixel format for camera*/
|
|
static void v4l2_palette_set(ctx_cam *cam)
|
|
{
|
|
int indxp, indx, retcd;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
palette_item *palette_array;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
palette_array =(palette_item*) malloc(sizeof(palette_item) * (V4L2_PALETTE_COUNT_MAX+1));
|
|
v4l2_palette_init(palette_array);
|
|
|
|
v4l2_params_check(cam);
|
|
|
|
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
|
|
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"palette")) {
|
|
indxp = atoi(cam->v4l2cam->params->params_array[indx].param_value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
retcd = v4l2_pixfmt_set(cam, palette_array[indxp].v4l2id);
|
|
if (retcd == 0) {
|
|
myfree(&palette_array);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Configuration palette index %d (%s) for %dx%d doesn't work.")
|
|
, indxp, palette_array[indxp].fourcc
|
|
,v4l2cam->width, v4l2cam->height);
|
|
|
|
indxp = v4l2_pixfmt_list(cam, palette_array);
|
|
if (indxp < 0) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("Unable to find a compatible palette format."));
|
|
myfree(&palette_array);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
retcd = v4l2_pixfmt_set(cam, palette_array[indxp].v4l2id);
|
|
if (retcd < 0) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
, _("Palette selection failed for format %s")
|
|
, palette_array[indxp].fourcc);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Selected palette %s")
|
|
,palette_array[indxp].fourcc);
|
|
|
|
myfree(&palette_array);
|
|
|
|
}
|
|
|
|
/* Set the memory mapping from device to Motion*/
|
|
static void v4l2_set_mmap(ctx_cam *cam)
|
|
{
|
|
enum v4l2_buf_type type;
|
|
int buffer_index;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
if (!(v4l2cam->cap.capabilities & V4L2_CAP_STREAMING)) {
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
memset(&v4l2cam->req, 0, sizeof(struct v4l2_requestbuffers));
|
|
|
|
v4l2cam->req.count = MMAP_BUFFERS;
|
|
v4l2cam->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
v4l2cam->req.memory = V4L2_MEMORY_MMAP;
|
|
if (xioctl(v4l2cam, VIDIOC_REQBUFS, &v4l2cam->req) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error requesting buffers %d for memory map. VIDIOC_REQBUFS")
|
|
,v4l2cam->req.count);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
v4l2cam->buffer_count = v4l2cam->req.count;
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_("mmap information: frames=%d"), v4l2cam->buffer_count);
|
|
|
|
if (v4l2cam->buffer_count < MIN_MMAP_BUFFERS) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Insufficient buffer memory %d < MIN_MMAP_BUFFERS.")
|
|
,v4l2cam->buffer_count);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
v4l2cam->buffers =(video_buff*) calloc(v4l2cam->buffer_count, sizeof(video_buff));
|
|
if (v4l2cam->buffers == NULL) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, _("Out of memory."));
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
for (buffer_index = 0; buffer_index < v4l2cam->buffer_count; buffer_index++) {
|
|
struct v4l2_buffer buf;
|
|
|
|
memset(&buf, 0, sizeof(struct v4l2_buffer));
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
buf.index = buffer_index;
|
|
if (xioctl(v4l2cam, VIDIOC_QUERYBUF, &buf) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error querying buffer %i\nVIDIOC_QUERYBUF: ")
|
|
,buffer_index);
|
|
myfree(&v4l2cam->buffers);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
v4l2cam->buffers[buffer_index].size = buf.length;
|
|
v4l2cam->buffers[buffer_index].ptr =(unsigned char*) mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, v4l2cam->fd_device, buf.m.offset);
|
|
|
|
if (v4l2cam->buffers[buffer_index].ptr == MAP_FAILED) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error mapping buffer %i mmap"), buffer_index);
|
|
myfree(&v4l2cam->buffers);
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_("%i length=%d Address (%x)")
|
|
,buffer_index, buf.length, v4l2cam->buffers[buffer_index].ptr);
|
|
}
|
|
|
|
for (buffer_index = 0; buffer_index < v4l2cam->buffer_count; buffer_index++) {
|
|
memset(&v4l2cam->buf, 0, sizeof(struct v4l2_buffer));
|
|
|
|
v4l2cam->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
v4l2cam->buf.memory = V4L2_MEMORY_MMAP;
|
|
v4l2cam->buf.index = buffer_index;
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_QBUF, &v4l2cam->buf) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_QBUF");
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
}
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
if (xioctl(v4l2cam, VIDIOC_STREAMON, &type) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
|
|
,_("Error starting stream. VIDIOC_STREAMON"));
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
/* Assign the resulting sizes to the camera context items */
|
|
static void v4l2_set_imgs(ctx_cam *cam)
|
|
{
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
cam->imgs.width = cam->v4l2cam->width;
|
|
cam->imgs.height = cam->v4l2cam->height;
|
|
cam->imgs.motionsize = cam->imgs.width * cam->imgs.height;
|
|
cam->imgs.size_norm = (cam->imgs.motionsize * 3) / 2;
|
|
cam->conf->width = cam->v4l2cam->width;
|
|
cam->conf->height = cam->v4l2cam->height;
|
|
}
|
|
|
|
/* Capture the image into the buffer */
|
|
static int v4l2_capture(ctx_cam *cam)
|
|
{
|
|
int retcd;
|
|
sigset_t set, old;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
/* Block signals during IOCTL */
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGCHLD);
|
|
sigaddset(&set, SIGALRM);
|
|
sigaddset(&set, SIGUSR1);
|
|
sigaddset(&set, SIGTERM);
|
|
sigaddset(&set, SIGHUP);
|
|
pthread_sigmask(SIG_BLOCK, &set, &old);
|
|
|
|
if (v4l2cam->pframe >= 0) {
|
|
retcd = xioctl(v4l2cam, VIDIOC_QBUF, &v4l2cam->buf);
|
|
if (retcd == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_QBUF");
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
memset(&v4l2cam->buf, 0, sizeof(struct v4l2_buffer));
|
|
|
|
v4l2cam->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
v4l2cam->buf.memory = V4L2_MEMORY_MMAP;
|
|
v4l2cam->buf.bytesused = 0;
|
|
|
|
retcd = xioctl(v4l2cam, VIDIOC_DQBUF, &v4l2cam->buf);
|
|
if (retcd == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_DQBUF");
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
|
|
return -1;
|
|
}
|
|
|
|
v4l2cam->pframe = v4l2cam->buf.index;
|
|
v4l2cam->buffers[v4l2cam->buf.index].used = v4l2cam->buf.bytesused;
|
|
v4l2cam->buffers[v4l2cam->buf.index].content_length = v4l2cam->buf.bytesused;
|
|
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Convert captured image to the standard pixel format*/
|
|
static int v4l2_convert(ctx_cam *cam, unsigned char *img_norm)
|
|
{
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
video_buff *the_buffer = &v4l2cam->buffers[v4l2cam->buf.index];
|
|
|
|
/*The FALLTHROUGH is a special comment required by compiler. */
|
|
switch (v4l2cam->pixfmt_src) {
|
|
case V4L2_PIX_FMT_RGB24:
|
|
vid_rgb24toyuv420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
vid_uyvyto420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
|
vid_yuv422to420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
vid_yuv422pto420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
memcpy(img_norm, the_buffer->ptr, the_buffer->content_length);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_PJPG:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_JPEG:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_MJPEG:
|
|
return vid_mjpegtoyuv420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height
|
|
,the_buffer->content_length);
|
|
|
|
case V4L2_PIX_FMT_SBGGR16:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_SGBRG8:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_SGRBG8:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_SBGGR8: /* bayer */
|
|
vid_bayer2rgb24(cam->imgs.common_buffer, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
vid_rgb24toyuv420p(img_norm, cam->imgs.common_buffer, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_SRGGB8: /*New Pi Camera format*/
|
|
vid_bayer2rgb24(cam->imgs.common_buffer, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
vid_rgb24toyuv420p(img_norm, cam->imgs.common_buffer, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_SPCA561:
|
|
/*FALLTHROUGH*/
|
|
case V4L2_PIX_FMT_SN9C10X:
|
|
vid_sonix_decompress(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
vid_bayer2rgb24(cam->imgs.common_buffer, img_norm, v4l2cam->width, v4l2cam->height);
|
|
vid_rgb24toyuv420p(img_norm, cam->imgs.common_buffer, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_Y12:
|
|
vid_y10torgb24(cam->imgs.common_buffer, the_buffer->ptr, v4l2cam->width, v4l2cam->height, 2);
|
|
vid_rgb24toyuv420p(img_norm, cam->imgs.common_buffer, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
case V4L2_PIX_FMT_Y10:
|
|
vid_y10torgb24(cam->imgs.common_buffer, the_buffer->ptr, v4l2cam->width, v4l2cam->height, 4);
|
|
vid_rgb24toyuv420p(img_norm, cam->imgs.common_buffer, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
case V4L2_PIX_FMT_GREY:
|
|
vid_greytoyuv420p(img_norm, the_buffer->ptr, v4l2cam->width, v4l2cam->height);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
static void v4l2_device_init(ctx_cam *cam)
|
|
{
|
|
cam->v4l2cam = (ctx_v4l2cam*)mymalloc(sizeof(ctx_v4l2cam));
|
|
cam->v4l2cam->devctrl_array = NULL;
|
|
cam->v4l2cam->devctrl_count = 0;
|
|
cam->v4l2cam->buffer_count= 0;
|
|
cam->v4l2cam->pframe = -1;
|
|
cam->v4l2cam->finish = cam->finish_cam;
|
|
cam->v4l2cam->buffers = NULL;
|
|
|
|
cam->v4l2cam->params =(ctx_params*) mymalloc(sizeof(ctx_params));
|
|
memset(cam->v4l2cam->params, 0, sizeof(ctx_params));
|
|
cam->v4l2cam->params->params_array = NULL;
|
|
cam->v4l2cam->params->params_count = 0;
|
|
cam->v4l2cam->params->update_params = true; /*Set trigger to update the params */
|
|
|
|
util_parms_parse(cam->v4l2cam->params, cam->conf->v4l2_params);
|
|
util_parms_add_default(cam->v4l2cam->params, "input", "-1");
|
|
util_parms_add_default(cam->v4l2cam->params, "palette", "17");
|
|
util_parms_add_default(cam->v4l2cam->params, "norm", "0");
|
|
util_parms_add_default(cam->v4l2cam->params, "frequency", "0");
|
|
|
|
cam->v4l2cam->height = cam->conf->height;
|
|
cam->v4l2cam->width = cam->conf->width;
|
|
cam->v4l2cam->fps =cam->conf->framerate;
|
|
|
|
}
|
|
|
|
/* Update and set user params if needed */
|
|
static void v4l2_device_select(ctx_cam *cam)
|
|
{
|
|
int retcd;
|
|
|
|
if (cam->v4l2cam->params->update_params == true) {
|
|
|
|
util_parms_parse(cam->v4l2cam->params, cam->conf->v4l2_params);
|
|
|
|
retcd = v4l2_parms_set(cam);
|
|
if (retcd < 0 ) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
|
|
,_("Error setting device controls"));
|
|
return;
|
|
}
|
|
|
|
v4l2_ctrls_set(cam);
|
|
}
|
|
}
|
|
|
|
/* Open the device */
|
|
static void v4l2_device_open(ctx_cam *cam)
|
|
{
|
|
int fd_device;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
, _("Opening video device %s")
|
|
, cam->conf->v4l2_device.c_str());
|
|
|
|
cam->watchdog = 60;
|
|
fd_device = open(cam->conf->v4l2_device.c_str(), O_RDWR|O_CLOEXEC);
|
|
if (fd_device > 0) {
|
|
cam->v4l2cam->fd_device = fd_device;
|
|
} else {
|
|
MOTION_LOG(ALR, TYPE_VIDEO, SHOW_ERRNO
|
|
, _("Failed to open video device %s")
|
|
, cam->conf->v4l2_device.c_str());
|
|
cam->v4l2cam->fd_device = -1;
|
|
return;
|
|
}
|
|
|
|
if (xioctl(cam->v4l2cam, VIDIOC_QUERYCAP, &cam->v4l2cam->cap) < 0) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, _("Not a V4L2 device?"));
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
if (!(cam->v4l2cam->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, _("Device does not support capturing."));
|
|
v4l2_device_close(cam);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static void v4l2_log_types(ctx_cam *cam)
|
|
{
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "------------------------");
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "cap.driver: \"%s\"",v4l2cam->cap.driver);
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "cap.card: \"%s\"",v4l2cam->cap.card);
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "cap.bus_info: \"%s\"",v4l2cam->cap.bus_info);
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "cap.capabilities=0x%08X",v4l2cam->cap.capabilities);
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "------------------------");
|
|
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- VIDEO_CAPTURE");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- VIDEO_OUTPUT");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- VIDEO_OVERLAY");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_VBI_CAPTURE) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- VBI_CAPTURE");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_VBI_OUTPUT) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- VBI_OUTPUT");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_RDS_CAPTURE) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- RDS_CAPTURE");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_TUNER) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- TUNER");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_AUDIO) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- AUDIO");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_READWRITE) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- READWRITE");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_ASYNCIO) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- ASYNCIO");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_STREAMING) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- STREAMING");
|
|
}
|
|
if (v4l2cam->cap.capabilities & V4L2_CAP_TIMEPERFRAME) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "- TIMEPERFRAME");
|
|
}
|
|
|
|
}
|
|
|
|
static void v4l2_log_formats(ctx_cam *cam)
|
|
{
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
palette_item *palette_array;
|
|
struct v4l2_fmtdesc dev_format;
|
|
struct v4l2_frmsizeenum dev_sizes;
|
|
struct v4l2_frmivalenum dev_frameint;
|
|
int indx_format, indx_sizes, indx_frameint;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
palette_array = (palette_item *)malloc(sizeof(palette_item) * (V4L2_PALETTE_COUNT_MAX+1));
|
|
|
|
v4l2_palette_init(palette_array);
|
|
|
|
memset(&dev_format, 0, sizeof(struct v4l2_fmtdesc));
|
|
dev_format.index = indx_format = 0;
|
|
dev_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
while (xioctl(v4l2cam, VIDIOC_ENUM_FMT, &dev_format) != -1) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_("Supported palette %s (%c%c%c%c)")
|
|
,dev_format.description
|
|
,dev_format.pixelformat >> 0
|
|
,dev_format.pixelformat >> 8
|
|
,dev_format.pixelformat >> 16
|
|
,dev_format.pixelformat >> 24);
|
|
|
|
memset(&dev_sizes, 0, sizeof(struct v4l2_frmsizeenum));
|
|
dev_sizes.index = indx_sizes = 0;
|
|
dev_sizes.pixel_format = dev_format.pixelformat;
|
|
while (xioctl(v4l2cam, VIDIOC_ENUM_FRAMESIZES, &dev_sizes) != -1) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_(" Width: %d, Height %d")
|
|
,dev_sizes.discrete.width
|
|
,dev_sizes.discrete.height);
|
|
|
|
memset(&dev_frameint, 0, sizeof(struct v4l2_frmivalenum));
|
|
dev_frameint.index = indx_frameint = 0;
|
|
dev_frameint.pixel_format = dev_format.pixelformat;
|
|
dev_frameint.width = dev_sizes.discrete.width;
|
|
dev_frameint.height = dev_sizes.discrete.height;
|
|
while (xioctl(v4l2cam, VIDIOC_ENUM_FRAMEINTERVALS, &dev_frameint) != -1) {
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
|
|
,_(" Framerate %d/%d")
|
|
,dev_frameint.discrete.numerator
|
|
,dev_frameint.discrete.denominator);
|
|
memset(&dev_frameint, 0, sizeof(struct v4l2_frmivalenum));
|
|
dev_frameint.index = ++indx_frameint;
|
|
dev_frameint.pixel_format = dev_format.pixelformat;
|
|
dev_frameint.width = dev_sizes.discrete.width;
|
|
dev_frameint.height = dev_sizes.discrete.height;
|
|
}
|
|
memset(&dev_sizes, 0, sizeof(struct v4l2_frmsizeenum));
|
|
dev_sizes.index = ++indx_sizes;
|
|
dev_sizes.pixel_format = dev_format.pixelformat;
|
|
}
|
|
memset(&dev_format, 0, sizeof(struct v4l2_fmtdesc));
|
|
dev_format.index = ++indx_format;
|
|
dev_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
}
|
|
|
|
myfree(&palette_array);
|
|
|
|
}
|
|
|
|
static void v4l2_set_fps(ctx_cam *cam)
|
|
{
|
|
int retcd;
|
|
ctx_v4l2cam *v4l2cam = cam->v4l2cam;
|
|
struct v4l2_streamparm setfps;
|
|
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
return;
|
|
}
|
|
|
|
memset(&setfps, 0, sizeof(struct v4l2_streamparm));
|
|
|
|
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
setfps.parm.capture.timeperframe.numerator = 1;
|
|
setfps.parm.capture.timeperframe.denominator = v4l2cam->fps;
|
|
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO
|
|
, _("Trying to set fps to %d")
|
|
, setfps.parm.capture.timeperframe.denominator);
|
|
|
|
retcd = xioctl(v4l2cam, VIDIOC_S_PARM, &setfps);
|
|
if (retcd != 0) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
|
|
,_("Error setting fps. Return code %d"), retcd);
|
|
}
|
|
|
|
MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO
|
|
, _("Device set fps to %d")
|
|
, setfps.parm.capture.timeperframe.denominator);
|
|
|
|
}
|
|
|
|
#endif /* HAVE_V4L2 */
|
|
|
|
void v4l2_cleanup(ctx_cam *cam)
|
|
{
|
|
#ifdef HAVE_V4L2
|
|
|
|
enum v4l2_buf_type type;
|
|
int indx;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
|
|
,_("Closing video device %s"), cam->conf->v4l2_device.c_str());
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
if (cam->v4l2cam->fd_device != -1) {
|
|
xioctl(cam->v4l2cam, VIDIOC_STREAMOFF, &type);
|
|
v4l2_device_close(cam);
|
|
}
|
|
|
|
if (cam->v4l2cam->buffers != NULL) {
|
|
for (indx = 0; indx < (int)cam->v4l2cam->req.count; indx++){
|
|
munmap(cam->v4l2cam->buffers[indx].ptr, cam->v4l2cam->buffers[indx].size);
|
|
}
|
|
myfree(&cam->v4l2cam->buffers);
|
|
}
|
|
|
|
if (cam->v4l2cam->devctrl_count != 0) {
|
|
for (indx = 0; indx < cam->v4l2cam->devctrl_count; indx++){
|
|
myfree(&cam->v4l2cam->devctrl_array[indx].ctrl_iddesc);
|
|
myfree(&cam->v4l2cam->devctrl_array[indx].ctrl_name);
|
|
}
|
|
myfree(&cam->v4l2cam->devctrl_array);
|
|
}
|
|
cam->v4l2cam->devctrl_count=0;
|
|
|
|
util_parms_free(cam->v4l2cam->params);
|
|
myfree(&cam->v4l2cam->params);
|
|
|
|
myfree(&cam->v4l2cam);
|
|
#endif // HAVE_V4L2
|
|
|
|
cam->running_cam = false;
|
|
cam->camera_status = STATUS_CLOSED;
|
|
|
|
}
|
|
|
|
void v4l2_start(ctx_cam *cam)
|
|
{
|
|
#ifdef HAVE_V4L2
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO,_("Opening V4L2 device"));
|
|
|
|
v4l2_device_init(cam);
|
|
v4l2_device_open(cam);
|
|
v4l2_log_types(cam);
|
|
v4l2_log_formats(cam);
|
|
v4l2_set_input(cam);
|
|
v4l2_set_norm(cam);
|
|
v4l2_set_frequency(cam);
|
|
v4l2_palette_set(cam);
|
|
v4l2_set_fps(cam);
|
|
v4l2_ctrls_count(cam);
|
|
v4l2_ctrls_list(cam);
|
|
v4l2_ctrls_set(cam);
|
|
v4l2_set_mmap(cam);
|
|
v4l2_set_imgs(cam);
|
|
if (cam->v4l2cam->fd_device == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO,_("V4L2 device failed to open"));
|
|
v4l2_cleanup(cam);
|
|
return;
|
|
}
|
|
cam->camera_status = STATUS_OPENED;
|
|
|
|
#else
|
|
cam->camera_status = STATUS_CLOSED;
|
|
#endif // HAVE_V4l2
|
|
}
|
|
|
|
int v4l2_next(ctx_cam *cam, ctx_image_data *img_data)
|
|
{
|
|
#ifdef HAVE_V4L2
|
|
int retcd;
|
|
|
|
if (cam->v4l2cam == NULL) {
|
|
return CAPTURE_FAILURE;
|
|
}
|
|
|
|
v4l2_device_select(cam);
|
|
|
|
retcd = v4l2_capture(cam);
|
|
if (retcd != 0) {
|
|
return CAPTURE_FAILURE;
|
|
}
|
|
|
|
retcd = v4l2_convert(cam, img_data->image_norm);
|
|
if (retcd != 0) {
|
|
return CAPTURE_FAILURE;
|
|
}
|
|
|
|
rotate_map(cam, img_data);
|
|
|
|
return CAPTURE_SUCCESS;
|
|
#else
|
|
(void)cam;
|
|
(void)img_data;
|
|
return CAPTURE_FAILURE;
|
|
#endif // HAVE_V4L2
|
|
}
|
|
|
|
|