Files
motion/src/video_v4l2.cpp
2021-03-24 22:13:52 -06:00

1264 lines
43 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-2021 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_H264;
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;
}
/* Get the count of how many controls and menu items the device supports */
static int v4l2_ctrls_count(ctx_v4l2cam *v4l2cam)
{
struct v4l2_queryctrl vid_ctrl;
struct v4l2_querymenu vid_menu;
int indx;
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;
}
return 0;
}
/* Print the device controls to the log */
static void v4l2_ctrls_log(ctx_v4l2cam *v4l2cam)
{
int indx;
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_v4l2cam *v4l2cam)
{
struct v4l2_queryctrl vid_ctrl;
struct v4l2_querymenu vid_menu;
int indx, indx_ctrl;
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(v4l2cam);
return;
}
/* Set the control array items to the device */
static void v4l2_ctrls_set(ctx_v4l2cam *v4l2cam)
{
struct ctx_v4l2cam_ctrl *devitem;
struct v4l2_control vid_ctrl;
int indx_dev, retcd;
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;
}
}
}
}
return;
}
static int v4l2_parms_set(ctx_v4l2cam *v4l2cam)
{
struct ctx_v4l2cam_ctrl *devitem;
struct ctx_params_item *usritem;
int indx_dev, indx_user;
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 int v4l2_set_input(ctx_v4l2cam *v4l2cam)
{
struct v4l2_input input;
memset(&input, 0, sizeof (struct v4l2_input));
if (v4l2cam->input == -1) {
input.index = 0;
} else {
input.index = v4l2cam->input;
}
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);
return -1;
}
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);
return -1;
}
v4l2cam->device_type = input.type;
v4l2cam->device_tuner = input.tuner;
return 0;
}
/* Set the video standard(norm) for the device to the user requested value*/
static void v4l2_set_norm(ctx_v4l2cam *v4l2cam)
{
struct v4l2_standard standard;
v4l2_std_id std_id;
if (xioctl(v4l2cam, VIDIOC_G_STD, &std_id) == -1) {
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
,_("Device does not support specifying PAL/NTSC norm"));
v4l2cam->norm = std_id = 0; // V4L2_STD_UNKNOWN = 0
}
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 (v4l2cam->norm) {
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_v4l2cam *v4l2cam)
{
struct v4l2_tuner tuner;
struct v4l2_frequency freq;
/* 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 = (v4l2cam->frequency / 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;
}
/* Set the pixel format on the device */
static int v4l2_set_pixfmt(ctx_v4l2cam *v4l2cam, unsigned int pixformat)
{
memset(&v4l2cam->dst_fmt, 0, sizeof(struct v4l2_format));
v4l2cam->dst_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2cam->dst_fmt.fmt.pix.width = v4l2cam->width;
v4l2cam->dst_fmt.fmt.pix.height = v4l2cam->height;
v4l2cam->dst_fmt.fmt.pix.pixelformat = pixformat;
v4l2cam->dst_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (xioctl(v4l2cam, VIDIOC_TRY_FMT, &v4l2cam->dst_fmt) != -1 &&
v4l2cam->dst_fmt.fmt.pix.pixelformat == pixformat) {
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);
if (v4l2cam->dst_fmt.fmt.pix.width != (unsigned int) v4l2cam->width ||
v4l2cam->dst_fmt.fmt.pix.height != (unsigned int) v4l2cam->height) {
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
,_("Adjusting resolution from %ix%i to %ix%i.")
,v4l2cam->width, v4l2cam->height
,v4l2cam->dst_fmt.fmt.pix.width
,v4l2cam->dst_fmt.fmt.pix.height);
v4l2cam->width = v4l2cam->dst_fmt.fmt.pix.width;
v4l2cam->height = v4l2cam->dst_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;
}
}
if (xioctl(v4l2cam, VIDIOC_S_FMT, &v4l2cam->dst_fmt) == -1) {
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
,_("Error setting pixel format.\nVIDIOC_S_FMT: "));
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);
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
,_("Bytesperlines %d sizeimage %d colorspace %08x")
,v4l2cam->dst_fmt.fmt.pix.bytesperline
,v4l2cam->dst_fmt.fmt.pix.sizeimage
,v4l2cam->dst_fmt.fmt.pix.colorspace);
return 0;
}
return -1;
}
/* If needed adjust sizes to be modulo 8 */
static void v4l2_validate_sizes(ctx_v4l2cam *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);
}
}
/* Find and select the pixel format for camera*/
static int v4l2_set_palette(ctx_v4l2cam *v4l2cam)
{
struct v4l2_fmtdesc fmtd;
int v4l2_pal, indx_palette, indx, retcd;
palette_item *palette_array;
palette_array =(palette_item*) malloc(sizeof(palette_item) * (V4L2_PALETTE_COUNT_MAX+1));
v4l2_palette_init(palette_array);
v4l2_validate_sizes(v4l2cam);
if (v4l2cam->palette == 21) {
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
,_("H264(21) format not supported via v4l2_device. Changing to default palette"));
v4l2cam->palette = 17;
}
/* First we try setting the config file value */
indx_palette = v4l2cam->palette;
if ((indx_palette >= 0) && (indx_palette <= V4L2_PALETTE_COUNT_MAX)) {
retcd = v4l2_set_pixfmt(v4l2cam, palette_array[indx_palette].v4l2id);
if (retcd >= 0) {
if (palette_array != NULL) {
free(palette_array);
}
palette_array = NULL;
return 0;
}
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
,_("Configuration palette index %d (%s) for %dx%d doesn't work.")
, indx_palette, palette_array[indx_palette].fourcc
,v4l2cam->width, v4l2cam->height);
}
memset(&fmtd, 0, sizeof(struct v4l2_fmtdesc));
fmtd.index = v4l2_pal = 0;
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
indx_palette = -1; /* -1 says not yet selected */
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, _("Supported palettes:"));
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);
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO
,_("%d - %s (compressed : %d) (%#x)")
,fmtd.index, fmtd.description, fmtd.flags, fmtd.pixelformat);
/* Adjust indx_palette if larger value found */
/* Prevent the selection of H264 since this module does not support it */
for (indx = 0; indx <= V4L2_PALETTE_COUNT_MAX; indx++) {
if ((palette_array[indx].v4l2id == fmtd.pixelformat) &&
(palette_array[indx].v4l2id != V4L2_PIX_FMT_H264)) {
indx_palette = indx;
}
}
memset(&fmtd, 0, sizeof(struct v4l2_fmtdesc));
fmtd.index = ++v4l2_pal;
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
if (indx_palette >= 0) {
retcd = v4l2_set_pixfmt(v4l2cam, palette_array[indx_palette].v4l2id);
if (retcd >= 0) {
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
,_("Selected palette %s")
,palette_array[indx_palette].fourcc);
if (palette_array != NULL) {
free(palette_array);
}
palette_array = NULL;
return 0;
}
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
,_("Palette selection failed for format %s")
, palette_array[indx_palette].fourcc);
}
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO
,_("Unable to find a compatible palette format."));
if (palette_array != NULL) {
free(palette_array);
}
palette_array = NULL;
return -1;
}
/* Set the memory mapping from device to Motion*/
static int v4l2_set_mmap(ctx_v4l2cam *v4l2cam)
{
enum v4l2_buf_type type;
int buffer_index;
/* Does the device support streaming? */
if (!(v4l2cam->cap.capabilities & V4L2_CAP_STREAMING)) {
return -1;
}
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);
return -1;
}
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);
return -1;
}
v4l2cam->buffers =(video_buff*) calloc(v4l2cam->buffer_count, sizeof(video_buff));
if (!v4l2cam->buffers) {
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, _("Out of memory."));
v4l2cam->buffers = NULL;
return -1;
}
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);
if (v4l2cam->buffers != NULL) {
free(v4l2cam->buffers);
}
v4l2cam->buffers = NULL;
return -1;
}
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);
if (v4l2cam->buffers != NULL) {
free(v4l2cam->buffers);
}
v4l2cam->buffers = NULL;
return -1;
}
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");
return -1;
}
}
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"));
return -1;
}
return 0;
}
/* Assign the resulting sizes to the camera context items */
static void v4l2_set_imgs(ctx_cam *cam)
{
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_buffer(ctx_v4l2cam *v4l2cam)
{
int retcd;
sigset_t set, old;
/* 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) {
if (xioctl(v4l2cam, VIDIOC_QBUF, &v4l2cam->buf) == -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;
if (xioctl(v4l2cam, VIDIOC_DQBUF, &v4l2cam->buf) == -1) {
/*
* Some drivers return EIO when there is no signal,
* driver might dequeue an (empty) buffer despite
* returning an error, or even stop capturing.
*/
if (errno == EIO) {
v4l2cam->pframe++;
if (v4l2cam->pframe >= (int)v4l2cam->req.count) {
v4l2cam->pframe = 0;
}
v4l2cam->buf.index = v4l2cam->pframe;
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO
,"VIDIOC_DQBUF: EIO "
"(vid_source->pframe %ud)", v4l2cam->pframe);
retcd = 1;
} else if (errno == EAGAIN) {
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_DQBUF: EAGAIN"
" (vid_source->pframe %ud)", v4l2cam->pframe);
retcd = 1;
} else {
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_DQBUF");
retcd = -1;
}
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
return retcd;
}
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 motion pixel format*/
static int v4l2_capture_convert(ctx_cam *cam, ctx_v4l2cam *v4l2cam, unsigned char *img_norm)
{
int shift;
video_buff *the_buffer = &v4l2cam->buffers[v4l2cam->buf.index];
shift = 0;
/*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_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:
shift += 2;
/*FALLTHROUGH*/
case V4L2_PIX_FMT_Y10:
shift += 2;
vid_y10torgb24(cam->imgs.common_buffer, the_buffer->ptr, v4l2cam->width, v4l2cam->height, shift);
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 int v4l2_device_init(ctx_cam *cam)
{
int indx;
cam->v4l2cam = (struct ctx_v4l2cam*)mymalloc(sizeof(struct 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 =(struct ctx_params*) mymalloc(sizeof(struct ctx_params));
memset(cam->v4l2cam->params, 0, sizeof(struct 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");
for (indx = 0; indx < cam->v4l2cam->params->params_count; indx++) {
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"input")) {
cam->v4l2cam->input = atoi(cam->v4l2cam->params->params_array[indx].param_value);
}
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"palette")) {
cam->v4l2cam->palette = atoi(cam->v4l2cam->params->params_array[indx].param_value);
}
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"norm")) {
cam->v4l2cam->norm = atoi(cam->v4l2cam->params->params_array[indx].param_value);
}
if (mystreq(cam->v4l2cam->params->params_array[indx].param_name,"frequency")) {
cam->v4l2cam->frequency = atol(cam->v4l2cam->params->params_array[indx].param_value);
}
}
cam->v4l2cam->height = cam->conf->height;
cam->v4l2cam->width = cam->conf->width;
cam->v4l2cam->fps =cam->conf->framerate;
return 0;
}
/* 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->v4l2cam);
if (retcd < 0 ) {
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO
,_("Error setting device controls"));
return;
}
v4l2_ctrls_set(cam->v4l2cam);
}
}
/* Open the device */
static int v4l2_device_open(ctx_cam *cam)
{
int fd_device;
cam->v4l2cam->fd_device = -1;
fd_device = -1;
cam->watchdog = 60;
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO
, _("Opening video device %s")
, cam->conf->v4l2_device.c_str());
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());
return -1;
}
if (xioctl(cam->v4l2cam, VIDIOC_QUERYCAP, &cam->v4l2cam->cap) < 0) {
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, _("Not a V4L2 device?"));
return -1;
}
if (!(cam->v4l2cam->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, _("Device does not support capturing."));
return -1;
}
return 0;
}
static void v4l2_log_types(ctx_v4l2cam *v4l2cam)
{
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");
}
return;
}
static void v4l2_log_formats(ctx_v4l2cam *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;
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;
}
if (palette_array != NULL) {
free(palette_array);
}
palette_array = NULL;
return;
}
static void v4l2_set_fps(ctx_v4l2cam *v4l2cam)
{
struct v4l2_streamparm setfps;
int retcd;
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);
return;
}
#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());
if (cam->v4l2cam == NULL) {
return;
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (cam->v4l2cam->fd_device != -1) {
xioctl(cam->v4l2cam, VIDIOC_STREAMOFF, &type);
close(cam->v4l2cam->fd_device);
cam->v4l2cam->fd_device = -1;
}
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);
}
if (cam->v4l2cam->buffers != NULL) {
free(cam->v4l2cam->buffers);
}
cam->v4l2cam->buffers = NULL;
}
if (cam->v4l2cam->devctrl_count != 0) {
for (indx = 0; indx < cam->v4l2cam->devctrl_count; indx++){
if (cam->v4l2cam->devctrl_array[indx].ctrl_iddesc != NULL) {
free(cam->v4l2cam->devctrl_array[indx].ctrl_iddesc);
}
cam->v4l2cam->devctrl_array[indx].ctrl_iddesc = NULL;
if (cam->v4l2cam->devctrl_array[indx].ctrl_name != NULL) {
free(cam->v4l2cam->devctrl_array[indx].ctrl_name);
}
cam->v4l2cam->devctrl_array[indx].ctrl_name = NULL;
}
if (cam->v4l2cam->devctrl_array != NULL) {
free(cam->v4l2cam->devctrl_array);
}
cam->v4l2cam->devctrl_array = NULL;
}
cam->v4l2cam->devctrl_count=0;
util_parms_free(cam->v4l2cam->params);
if (cam->v4l2cam != NULL) {
free(cam->v4l2cam);
}
cam->v4l2cam = NULL;
cam->running_cam = false;
#else
(void)cam;
#endif // HAVE_V4L2
}
int v4l2_start(ctx_cam *cam)
{
#ifdef HAVE_V4L2
int retcd;
retcd = v4l2_device_init(cam);
if (retcd == 0) retcd = v4l2_device_open(cam);
if (retcd == 0) v4l2_log_types(cam->v4l2cam);
if (retcd == 0) v4l2_log_formats(cam->v4l2cam);
if (retcd == 0) retcd = v4l2_set_input(cam->v4l2cam);
if (retcd == 0) v4l2_set_norm(cam->v4l2cam);
if (retcd == 0) v4l2_set_frequency(cam->v4l2cam);
if (retcd == 0) retcd = v4l2_set_palette(cam->v4l2cam);
if (retcd == 0) v4l2_set_fps(cam->v4l2cam);
if (retcd == 0) retcd = v4l2_ctrls_count(cam->v4l2cam);
if (retcd == 0) v4l2_ctrls_list(cam->v4l2cam);
if (retcd == 0) v4l2_ctrls_set(cam->v4l2cam);
if (retcd == 0) retcd = v4l2_set_mmap(cam->v4l2cam);
if (retcd == 0) v4l2_set_imgs(cam);
if (retcd < 0) {
v4l2_cleanup(cam);
return retcd;
}
return cam->v4l2cam->fd_device;
#else
(void)cam;
return -1;
#endif // HAVE_V4l2
}
int v4l2_next(ctx_cam *cam, ctx_image_data *img_data)
{
#ifdef HAVE_V4L2
int retcd;
v4l2_device_select(cam);
retcd = v4l2_capture_buffer(cam->v4l2cam);
if (retcd != 0) {
return retcd;
}
retcd = v4l2_capture_convert(cam, cam->v4l2cam, img_data->image_norm);
if (retcd != 0) {
return retcd;
}
/* Rotate the image as specified. */
rotate_map(cam, img_data);
return retcd;
#else
(void)cam;
(void)img_data;
return -1;
#endif // HAVE_V4L2
}