Files
motion/src/video_v4l2.cpp
2022-11-26 20:37:07 -07:00

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
}