mirror of
https://github.com/Motion-Project/motion.git
synced 2025-12-23 23:18:21 -05:00
1136 lines
38 KiB
C
1136 lines
38 KiB
C
/*
|
|
* video2.c
|
|
*
|
|
* V4L2 interface with basically JPEG decompression support and even more ...
|
|
* Copyright 2006 Krzysztof Blaszkowski (kb@sysmikro.com.pl)
|
|
* 2007 Angel Carpintero (motiondevelop@gmail.com)
|
|
|
|
* Supported features and TODO
|
|
- preferred palette is JPEG which seems to be very popular for many 640x480 usb cams
|
|
- other supported palettes (NOT TESTED)
|
|
V4L2_PIX_FMT_SN9C10X (sonix)
|
|
V4L2_PIX_FMT_SBGGR16,
|
|
V4L2_PIX_FMT_SBGGR8, (sonix)
|
|
V4L2_PIX_FMT_SPCA561,
|
|
V4L2_PIX_FMT_SGBRG8,
|
|
V4L2_PIX_FMT_SGRBG8,
|
|
V4L2_PIX_FMT_PAC207,
|
|
V4L2_PIX_FMT_PJPG,
|
|
V4L2_PIX_FMT_MJPEG, (tested)
|
|
V4L2_PIX_FMT_JPEG, (tested)
|
|
V4L2_PIX_FMT_RGB24,
|
|
V4L2_PIX_FMT_SPCA501,
|
|
V4L2_PIX_FMT_SPCA505,
|
|
V4L2_PIX_FMT_SPCA508,
|
|
V4L2_PIX_FMT_UYVY, (tested)
|
|
V4L2_PIX_FMT_YUV422P,
|
|
V4L2_PIX_FMT_YUV420, (tested)
|
|
V4L2_PIX_FMT_YUYV (tested)
|
|
|
|
* - setting tuner - NOT TESTED
|
|
* - access to V4L2 device controls is missing. Partially added but requires some improvements likely.
|
|
* - changing resolution at run-time may not work.
|
|
* - ucvideo svn r75 or above to work with MJPEG ( e.g. Logitech 5000 pro )
|
|
|
|
* This work is inspired by fswebcam and current design of motion.
|
|
* This interface has been tested with ZC0301 driver from kernel 2.6.17.3 and Labtec's usb camera (PAS202 sensor)
|
|
|
|
* I'm very pleased by achieved image quality and cpu usage comparing to junky v4l1 spca5xx driver with
|
|
* it nonsensical kernel messy jpeg decompressor.
|
|
* Default sensor settings used by ZC0301 driver are very reasonable choosen.
|
|
* apparently brigthness should be controlled automatically by motion still for light compensation.
|
|
* it can be done by adjusting ADC gain and also exposure time.
|
|
|
|
* Kernel 2.6.27
|
|
|
|
V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') YUYV per line
|
|
V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') YYUV per line
|
|
V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') YUVY per line
|
|
V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') 8 GBGB.. RGRG..
|
|
V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') 8 GRGR.. BGBG..
|
|
V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') 16 BGBG.. GRGR..
|
|
V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') compressed GBRG bayer
|
|
V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') Pixart 73xx JPEG
|
|
V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') compressed BGGR bayer
|
|
|
|
|
|
*
|
|
* This program 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 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program 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.
|
|
|
|
let's go :)
|
|
*/
|
|
|
|
#if !defined(WITHOUT_V4L) && defined(MOTION_V4L2)
|
|
|
|
#include "motion.h"
|
|
#include "video.h"
|
|
|
|
#ifdef MOTION_V4L2_OLD
|
|
// Seems that is needed for some system
|
|
#include <linux/time.h>
|
|
#include <linux/videodev2.h>
|
|
#endif
|
|
|
|
#define u8 unsigned char
|
|
#define u16 unsigned short
|
|
#define u32 unsigned int
|
|
#define s32 signed int
|
|
|
|
#define MMAP_BUFFERS 4
|
|
#define MIN_MMAP_BUFFERS 2
|
|
|
|
#ifndef V4L2_PIX_FMT_SBGGR8
|
|
/* see http://www.siliconimaging.com/RGB%20Bayer.htm */
|
|
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_MJPEG
|
|
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SN9C10X
|
|
#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SGBRG8
|
|
#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SGRBG8
|
|
#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SBGGR16
|
|
#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SPCA561
|
|
#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_PJPG
|
|
#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_PAC207
|
|
#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SPCA501
|
|
#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SPCA505
|
|
#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */
|
|
#endif
|
|
|
|
#ifndef V4L2_PIX_FMT_SPCA508
|
|
#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
|
|
#endif
|
|
|
|
#define ZC301_V4L2_CID_DAC_MAGN V4L2_CID_PRIVATE_BASE
|
|
#define ZC301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE+1)
|
|
|
|
static const u32 queried_ctrls[] = {
|
|
V4L2_CID_BRIGHTNESS,
|
|
V4L2_CID_CONTRAST,
|
|
V4L2_CID_SATURATION,
|
|
V4L2_CID_HUE,
|
|
/* first added in Linux kernel v2.6.26 */
|
|
#ifdef V4L2_CID_POWER_LINE_FREQUENCY
|
|
V4L2_CID_POWER_LINE_FREQUENCY,
|
|
#endif
|
|
|
|
V4L2_CID_RED_BALANCE,
|
|
V4L2_CID_BLUE_BALANCE,
|
|
V4L2_CID_GAMMA,
|
|
V4L2_CID_EXPOSURE,
|
|
V4L2_CID_AUTOGAIN,
|
|
V4L2_CID_GAIN,
|
|
|
|
ZC301_V4L2_CID_DAC_MAGN,
|
|
ZC301_V4L2_CID_GREEN_BALANCE,
|
|
0
|
|
};
|
|
|
|
typedef struct {
|
|
int fd;
|
|
u32 fps;
|
|
|
|
struct v4l2_capability cap;
|
|
struct v4l2_format src_fmt;
|
|
struct v4l2_format dst_fmt;
|
|
struct v4l2_requestbuffers req;
|
|
struct v4l2_buffer buf;
|
|
|
|
video_buff *buffers;
|
|
|
|
s32 pframe;
|
|
|
|
u32 ctrl_flags;
|
|
struct v4l2_queryctrl *controls;
|
|
volatile unsigned int *finish; /* End the thread */
|
|
|
|
} src_v4l2_t;
|
|
|
|
/**
|
|
* xioctl
|
|
*/
|
|
#ifdef __OpenBSD__
|
|
static int xioctl(src_v4l2_t *vid_source, unsigned long request, void *arg)
|
|
#else
|
|
static int xioctl(src_v4l2_t *vid_source, int request, void *arg)
|
|
#endif
|
|
{
|
|
int ret;
|
|
|
|
do
|
|
ret = ioctl(vid_source->fd, request, arg);
|
|
while (-1 == ret && EINTR == errno && !vid_source->finish);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* v4l2_get_capability
|
|
*/
|
|
static int v4l2_get_capability(src_v4l2_t * vid_source)
|
|
{
|
|
if (xioctl(vid_source, VIDIOC_QUERYCAP, &vid_source->cap) < 0) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Not a V4L2 device?");
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \n------------------------\n"
|
|
"cap.driver: \"%s\"\n"
|
|
"cap.card: \"%s\"\n"
|
|
"cap.bus_info: \"%s\"\n"
|
|
"cap.capabilities=0x%08X\n------------------------",
|
|
vid_source->cap.driver, vid_source->cap.card, vid_source->cap.bus_info,
|
|
vid_source->cap.capabilities);
|
|
|
|
if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_CAPTURE");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_OUTPUT");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_OVERLAY");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_VBI_CAPTURE)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VBI_CAPTURE");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_VBI_OUTPUT)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VBI_OUTPUT");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_RDS_CAPTURE)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - RDS_CAPTURE");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_TUNER)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - TUNER");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_AUDIO)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - AUDIO");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_READWRITE)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - READWRITE");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_ASYNCIO)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - ASYNCIO");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_STREAMING)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - STREAMING");
|
|
if (vid_source->cap.capabilities & V4L2_CAP_TIMEPERFRAME)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - TIMEPERFRAME");
|
|
|
|
if (!(vid_source->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Device does not support capturing.");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* v4l2_select_input
|
|
*/
|
|
static int v4l2_select_input(struct config *conf, struct video_dev *viddev,
|
|
src_v4l2_t * vid_source, int in, int norm,
|
|
unsigned long freq_, int tuner_number ATTRIBUTE_UNUSED)
|
|
{
|
|
struct v4l2_input input;
|
|
struct v4l2_standard standard;
|
|
v4l2_std_id std_id;
|
|
|
|
/* Set the input. */
|
|
memset(&input, 0, sizeof (input));
|
|
if (in == IN_DEFAULT)
|
|
input.index = IN_TV;
|
|
else input.index = in;
|
|
|
|
if (xioctl(vid_source, VIDIOC_ENUMINPUT, &input) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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, "%s: - TUNER");
|
|
|
|
if (input.type & V4L2_INPUT_TYPE_CAMERA)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - CAMERA");
|
|
|
|
if (xioctl(vid_source, VIDIOC_S_INPUT, &input.index) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting input %d"
|
|
" VIDIOC_S_INPUT", input.index);
|
|
return -1;
|
|
}
|
|
|
|
viddev->input = conf->input = in;
|
|
|
|
/*
|
|
* Set video standard usually webcams doesn't support the ioctl or
|
|
* return V4L2_STD_UNKNOWN
|
|
*/
|
|
if (xioctl(vid_source, VIDIOC_G_STD, &std_id) == -1) {
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Device doesn't support VIDIOC_G_STD");
|
|
norm = std_id = 0; // V4L2_STD_UNKNOWN = 0
|
|
}
|
|
|
|
if (std_id) {
|
|
memset(&standard, 0, sizeof(standard));
|
|
standard.index = 0;
|
|
|
|
while (xioctl(vid_source, VIDIOC_ENUMSTD, &standard) == 0) {
|
|
if (standard.id & std_id)
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - video standard %s",
|
|
standard.name);
|
|
|
|
standard.index++;
|
|
}
|
|
|
|
switch (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(vid_source, VIDIOC_S_STD, &std_id) == -1)
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting standard"
|
|
" method %d VIDIOC_S_STD", (int)std_id);
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set standard method %d",
|
|
(int)std_id);
|
|
}
|
|
|
|
viddev->norm = conf->norm = norm;
|
|
|
|
/* If this input is attached to a tuner, set the frequency. */
|
|
if (input.type & V4L2_INPUT_TYPE_TUNER) {
|
|
struct v4l2_tuner tuner;
|
|
struct v4l2_frequency freq;
|
|
|
|
/* Query the tuners capabilities. */
|
|
|
|
memset(&tuner, 0, sizeof(struct v4l2_tuner));
|
|
tuner.index = input.tuner;
|
|
|
|
if (xioctl(vid_source, VIDIOC_G_TUNER, &tuner) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: tuner %d VIDIOC_G_TUNER",
|
|
tuner.index);
|
|
return 0;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set tuner %d",
|
|
tuner.index);
|
|
|
|
/* Set the frequency. */
|
|
memset(&freq, 0, sizeof(struct v4l2_frequency));
|
|
freq.tuner = input.tuner;
|
|
freq.type = V4L2_TUNER_ANALOG_TV;
|
|
freq.frequency = (freq_ / 1000) * 16;
|
|
|
|
if (xioctl(vid_source, VIDIOC_S_FREQUENCY, &freq) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: freq %ul VIDIOC_S_FREQUENCY",
|
|
freq.frequency);
|
|
return 0;
|
|
}
|
|
|
|
viddev->freq = conf->frequency = freq_;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set Frequency to %ul",
|
|
freq.frequency);
|
|
} else {
|
|
viddev->freq = conf->frequency = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* *
|
|
* v4l2_do_set_pix_format
|
|
*
|
|
* This routine does the actual request to the driver
|
|
*
|
|
* Returns: 0 Ok
|
|
* -1 Problems setting palette or not supported
|
|
*
|
|
* Our algorithm for setting the picture format for the data which the
|
|
* driver returns to us will be as follows:
|
|
*
|
|
* First, we request that the format be set to whatever is in the config
|
|
* file (which is either the motion default, or a value chosen by the user).
|
|
* If that request is successful, we are finished.
|
|
*
|
|
* If the driver responds that our request is not accepted, we then enumerate
|
|
* the formats which the driver claims to be able to supply. From this list,
|
|
* we choose whichever format is "most efficient" for motion. The enumerated
|
|
* list is also printed to the motion log so that the user can consider
|
|
* choosing a different value for the config file.
|
|
*
|
|
* We then request the driver to set the format we have chosen. That request
|
|
* should never fail, so if it does we log the fact and give up.
|
|
*/
|
|
static int v4l2_do_set_pix_format(u32 pixformat, src_v4l2_t * vid_source,
|
|
int *width, int *height)
|
|
{
|
|
CLEAR(vid_source->dst_fmt);
|
|
vid_source->dst_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
vid_source->dst_fmt.fmt.pix.width = *width;
|
|
vid_source->dst_fmt.fmt.pix.height = *height;
|
|
vid_source->dst_fmt.fmt.pix.pixelformat = pixformat;
|
|
vid_source->dst_fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
|
|
|
if (xioctl(vid_source, VIDIOC_TRY_FMT, &vid_source->dst_fmt) != -1 &&
|
|
vid_source->dst_fmt.fmt.pix.pixelformat == pixformat) {
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Testing palette %c%c%c%c (%dx%d)",
|
|
pixformat >> 0, pixformat >> 8,
|
|
pixformat >> 16, pixformat >> 24, *width, *height);
|
|
|
|
if (vid_source->dst_fmt.fmt.pix.width != (unsigned int) *width ||
|
|
vid_source->dst_fmt.fmt.pix.height != (unsigned int) *height) {
|
|
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Adjusting resolution "
|
|
"from %ix%i to %ix%i.",
|
|
*width, *height, vid_source->dst_fmt.fmt.pix.width,
|
|
vid_source->dst_fmt.fmt.pix.height);
|
|
|
|
*width = vid_source->dst_fmt.fmt.pix.width;
|
|
*height = vid_source->dst_fmt.fmt.pix.height;
|
|
}
|
|
|
|
if (xioctl(vid_source, VIDIOC_S_FMT, &vid_source->dst_fmt) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error setting pixel "
|
|
"format.\nVIDIOC_S_FMT: ");
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using palette %c%c%c%c (%dx%d)"
|
|
" bytesperlines %d sizeimage %d colorspace %08x", pixformat >> 0,
|
|
pixformat >> 8, pixformat >> 16, pixformat >> 24, *width,
|
|
*height, vid_source->dst_fmt.fmt.pix.bytesperline,
|
|
vid_source->dst_fmt.fmt.pix.sizeimage,
|
|
vid_source->dst_fmt.fmt.pix.colorspace);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* v4l2_set_pix_format
|
|
*
|
|
* Returns: 0 Ok
|
|
* -1 Problems setting palette or not supported
|
|
*/
|
|
static int v4l2_set_pix_format(struct context *cnt, src_v4l2_t * vid_source,
|
|
int *width, int *height)
|
|
{
|
|
struct v4l2_fmtdesc fmtd;
|
|
int v4l2_pal;
|
|
|
|
/*
|
|
* Note that this array MUST exactly match the config file list.
|
|
* A higher index means better chance to be used
|
|
*/
|
|
static const u32 supported_formats[] = {
|
|
V4L2_PIX_FMT_SN9C10X,
|
|
V4L2_PIX_FMT_SBGGR16,
|
|
V4L2_PIX_FMT_SBGGR8,
|
|
V4L2_PIX_FMT_SPCA561,
|
|
V4L2_PIX_FMT_SGBRG8,
|
|
V4L2_PIX_FMT_SGRBG8,
|
|
V4L2_PIX_FMT_PAC207,
|
|
V4L2_PIX_FMT_PJPG,
|
|
V4L2_PIX_FMT_MJPEG,
|
|
V4L2_PIX_FMT_JPEG,
|
|
V4L2_PIX_FMT_RGB24,
|
|
V4L2_PIX_FMT_SPCA501,
|
|
V4L2_PIX_FMT_SPCA505,
|
|
V4L2_PIX_FMT_SPCA508,
|
|
V4L2_PIX_FMT_UYVY,
|
|
V4L2_PIX_FMT_YUYV,
|
|
V4L2_PIX_FMT_YUV422P,
|
|
V4L2_PIX_FMT_YUV420 /* most efficient for motion */
|
|
};
|
|
|
|
int array_size = sizeof(supported_formats) / sizeof(supported_formats[0]);
|
|
int index_format = -1; /* -1 says not yet chosen */
|
|
CLEAR(fmtd);
|
|
fmtd.index = v4l2_pal = 0;
|
|
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
/* First we try a shortcut of just setting the config file value */
|
|
if (cnt->conf.v4l2_palette >= 0) {
|
|
char name[5] = {supported_formats[cnt->conf.v4l2_palette] >> 0,
|
|
supported_formats[cnt->conf.v4l2_palette] >> 8,
|
|
supported_formats[cnt->conf.v4l2_palette] >> 16,
|
|
supported_formats[cnt->conf.v4l2_palette] >> 24, 0};
|
|
|
|
if (v4l2_do_set_pix_format(supported_formats[cnt->conf.v4l2_palette],
|
|
vid_source, width, height) >= 0)
|
|
return 0;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Config palette index %d (%s)"
|
|
" doesn't work.", cnt->conf.v4l2_palette, name);
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Supported palettes:");
|
|
|
|
while (xioctl(vid_source, VIDIOC_ENUM_FMT, &fmtd) != -1) {
|
|
|
|
int i;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: (%i) %c%c%c%c (%s)",
|
|
v4l2_pal, fmtd.pixelformat >> 0,
|
|
fmtd.pixelformat >> 8, fmtd.pixelformat >> 16,
|
|
fmtd.pixelformat >> 24, fmtd.description);
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: %d - %s (compressed : %d) (%#x)",
|
|
fmtd.index, fmtd.description, fmtd.flags, fmtd.pixelformat);
|
|
|
|
/* Adjust index_format if larger value found */
|
|
for (i = index_format + 1; i < array_size; i++)
|
|
if (supported_formats[i] == fmtd.pixelformat)
|
|
index_format = i;
|
|
|
|
CLEAR(fmtd);
|
|
fmtd.index = ++v4l2_pal;
|
|
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
}
|
|
|
|
if (index_format >= 0) {
|
|
char name[5] = {supported_formats[index_format] >> 0,
|
|
supported_formats[index_format] >> 8,
|
|
supported_formats[index_format] >> 16,
|
|
supported_formats[index_format] >> 24, 0};
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s Selected palette %s", name);
|
|
|
|
if (v4l2_do_set_pix_format(supported_formats[index_format],
|
|
vid_source, width, height) >= 0)
|
|
return 0;
|
|
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_TRY_FMT failed for "
|
|
"format %s", name);
|
|
}
|
|
|
|
MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Unable to find a compatible"
|
|
" palette format.");
|
|
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
static void v4l2_set_fps(src_v4l2_t * vid_source) {
|
|
struct v4l2_streamparm* setfps;
|
|
|
|
setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
|
|
memset(setfps, 0, sizeof(struct v4l2_streamparm));
|
|
setfpvid_source->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
setfpvid_source->parm.capture.timeperframe.numerator = 1;
|
|
setfpvid_source->parm.capture.timeperframe.denominator = vid_source->fps;
|
|
|
|
if (xioctl(vid_source, VIDIOC_S_PARM, setfps) == -1)
|
|
MOTION_LOG(ERR, 1, "%s: v4l2_set_fps VIDIOC_S_PARM");
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* v4l2_set_mmap
|
|
*/
|
|
static int v4l2_set_mmap(src_v4l2_t * vid_source)
|
|
{
|
|
enum v4l2_buf_type type;
|
|
u32 buffer_index;
|
|
|
|
/* Does the device support streaming? */
|
|
if (!(vid_source->cap.capabilities & V4L2_CAP_STREAMING))
|
|
return -1;
|
|
|
|
memset(&vid_source->req, 0, sizeof(struct v4l2_requestbuffers));
|
|
|
|
vid_source->req.count = MMAP_BUFFERS;
|
|
vid_source->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
vid_source->req.memory = V4L2_MEMORY_MMAP;
|
|
|
|
if (xioctl(vid_source, VIDIOC_REQBUFS, &vid_source->req) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error requesting buffers"
|
|
" %d for memory map. VIDIOC_REQBUFS",
|
|
vid_source->req.count);
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: mmap information: frames=%d",
|
|
vid_source->req.count);
|
|
|
|
if (vid_source->req.count < MIN_MMAP_BUFFERS) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Insufficient buffer memory"
|
|
" %d < MIN_MMAP_BUFFERS.", vid_source->req.count);
|
|
return -1;
|
|
}
|
|
|
|
vid_source->buffers = calloc(vid_source->req.count, sizeof(video_buff));
|
|
|
|
if (!vid_source->buffers) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Out of memory.");
|
|
return -1;
|
|
}
|
|
|
|
for (buffer_index = 0; buffer_index < vid_source->req.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(vid_source, VIDIOC_QUERYBUF, &buf) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error querying buffer"
|
|
" %i\nVIDIOC_QUERYBUF: ", buffer_index);
|
|
free(vid_source->buffers);
|
|
return -1;
|
|
}
|
|
|
|
vid_source->buffers[buffer_index].size = buf.length;
|
|
vid_source->buffers[buffer_index].ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, vid_source->fd, buf.m.offset);
|
|
|
|
if (vid_source->buffers[buffer_index].ptr == MAP_FAILED) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error mapping buffer %i mmap",
|
|
buffer_index);
|
|
free(vid_source->buffers);
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: %i length=%d Address (%x)",
|
|
buffer_index, buf.length, vid_source->buffers[buffer_index].ptr);
|
|
}
|
|
|
|
for (buffer_index = 0; buffer_index < vid_source->req.count; buffer_index++) {
|
|
memset(&vid_source->buf, 0, sizeof(struct v4l2_buffer));
|
|
|
|
vid_source->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
vid_source->buf.memory = V4L2_MEMORY_MMAP;
|
|
vid_source->buf.index = buffer_index;
|
|
|
|
if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
if (xioctl(vid_source, VIDIOC_STREAMON, &type) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error starting stream."
|
|
" VIDIOC_STREAMON");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* v4l2_scan_controls
|
|
*/
|
|
static int v4l2_scan_controls(src_v4l2_t * vid_source)
|
|
{
|
|
int count, i;
|
|
struct v4l2_queryctrl queryctrl;
|
|
|
|
memset(&queryctrl, 0, sizeof(struct v4l2_queryctrl));
|
|
|
|
for (i = 0, count = 0; queried_ctrls[i]; i++) {
|
|
queryctrl.id = queried_ctrls[i];
|
|
if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl))
|
|
continue;
|
|
|
|
count++;
|
|
vid_source->ctrl_flags |= 1 << i;
|
|
}
|
|
|
|
if (count) {
|
|
struct v4l2_queryctrl *ctrl = vid_source->controls
|
|
= calloc(count, sizeof(struct v4l2_queryctrl));
|
|
|
|
if (!ctrl) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Insufficient buffer memory.");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; queried_ctrls[i]; i++) {
|
|
if (vid_source->ctrl_flags & (1 << i)) {
|
|
struct v4l2_control control;
|
|
|
|
queryctrl.id = queried_ctrls[i];
|
|
if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl))
|
|
continue;
|
|
|
|
memcpy(ctrl, &queryctrl, sizeof(struct v4l2_queryctrl));
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: found control 0x%08x, \"%s\","
|
|
" range %d,%d %s",
|
|
ctrl->id, ctrl->name, ctrl->minimum, ctrl->maximum,
|
|
ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "!DISABLED!" : "");
|
|
|
|
memset(&control, 0, sizeof (control));
|
|
control.id = queried_ctrls[i];
|
|
xioctl(vid_source, VIDIOC_G_CTRL, &control);
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \t\"%s\", default %d, current %d",
|
|
ctrl->name, ctrl->default_value, control.value);
|
|
|
|
ctrl++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* v4l2_set_control
|
|
*/
|
|
static int v4l2_set_control(src_v4l2_t * vid_source, u32 cid, int value)
|
|
{
|
|
int i, count;
|
|
|
|
if (!vid_source->controls)
|
|
return -1;
|
|
|
|
for (i = 0, count = 0; queried_ctrls[i]; i++) {
|
|
if (vid_source->ctrl_flags & (1 << i)) {
|
|
if (cid == queried_ctrls[i]) {
|
|
struct v4l2_queryctrl *ctrl = vid_source->controls + count;
|
|
struct v4l2_control control;
|
|
int ret;
|
|
|
|
memset(&control, 0, sizeof (control));
|
|
control.id = queried_ctrls[i];
|
|
|
|
switch (ctrl->type) {
|
|
case V4L2_CTRL_TYPE_INTEGER:
|
|
value = control.value =
|
|
(value * (ctrl->maximum - ctrl->minimum) / 256) + ctrl->minimum;
|
|
ret = xioctl(vid_source, VIDIOC_S_CTRL, &control);
|
|
break;
|
|
|
|
case V4L2_CTRL_TYPE_BOOLEAN:
|
|
value = control.value = value ? 1 : 0;
|
|
ret = xioctl(vid_source, VIDIOC_S_CTRL, &control);
|
|
break;
|
|
|
|
case V4L2_CTRL_TYPE_MENU:
|
|
/* set as is, no adjustments */
|
|
control.value = value;
|
|
ret = xioctl(vid_source, VIDIOC_S_CTRL, &control);
|
|
break;
|
|
|
|
|
|
default:
|
|
MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: control type not supported yet");
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: setting control \"%s\" to %d"
|
|
" (ret %d %s) %s", ctrl->name, value, ret, ret ? strerror(errno) : "",
|
|
ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "Control is DISABLED!" : "");
|
|
|
|
return 0;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* v4l2_picture_controls
|
|
*/
|
|
static void v4l2_picture_controls(struct context *cnt, struct video_dev *viddev)
|
|
{
|
|
src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private;
|
|
|
|
if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) {
|
|
viddev->contrast = cnt->conf.contrast;
|
|
v4l2_set_control(vid_source, V4L2_CID_CONTRAST, viddev->contrast);
|
|
}
|
|
|
|
if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) {
|
|
viddev->saturation = cnt->conf.saturation;
|
|
v4l2_set_control(vid_source, V4L2_CID_SATURATION, viddev->saturation);
|
|
}
|
|
|
|
if (cnt->conf.hue && cnt->conf.hue != viddev->hue) {
|
|
viddev->hue = cnt->conf.hue;
|
|
v4l2_set_control(vid_source, V4L2_CID_HUE, viddev->hue);
|
|
}
|
|
|
|
#ifdef V4L2_CID_POWER_LINE_FREQUENCY
|
|
/* -1 is don't modify as 0 is an option to disable the power line filter */
|
|
if (cnt->conf.power_line_frequency != -1 && cnt->conf.power_line_frequency != viddev->power_line_frequency) {
|
|
viddev->power_line_frequency = cnt->conf.power_line_frequency;
|
|
v4l2_set_control(vid_source, V4L2_CID_POWER_LINE_FREQUENCY, viddev->power_line_frequency);
|
|
}
|
|
#endif
|
|
|
|
if (cnt->conf.autobright) {
|
|
if (vid_do_autobright(cnt, viddev)) {
|
|
if (v4l2_set_control(vid_source, V4L2_CID_BRIGHTNESS, viddev->brightness))
|
|
v4l2_set_control(vid_source, V4L2_CID_GAIN, viddev->brightness);
|
|
}
|
|
} else {
|
|
if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) {
|
|
viddev->brightness = cnt->conf.brightness;
|
|
if (v4l2_set_control(vid_source, V4L2_CID_BRIGHTNESS, viddev->brightness))
|
|
v4l2_set_control(vid_source, V4L2_CID_GAIN, viddev->brightness);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* public functions */
|
|
/**
|
|
* v4l2_start
|
|
*/
|
|
unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int width, int height,
|
|
int input, int norm, unsigned long freq, int tuner_number)
|
|
{
|
|
src_v4l2_t *vid_source;
|
|
|
|
/* Allocate memory for the state structure. */
|
|
if (!(vid_source = calloc(sizeof(src_v4l2_t), 1))) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Out of memory.");
|
|
goto err;
|
|
}
|
|
|
|
viddev->v4l2_private = vid_source;
|
|
vid_source->fd = viddev->fd;
|
|
vid_source->fps = cnt->conf.frame_limit;
|
|
vid_source->pframe = -1;
|
|
vid_source->finish = &cnt->finish;
|
|
struct config *conf = &cnt->conf;
|
|
|
|
if (v4l2_get_capability(vid_source))
|
|
goto err;
|
|
|
|
if (v4l2_select_input(conf, viddev, vid_source, input, norm, freq, tuner_number))
|
|
goto err;
|
|
|
|
if (v4l2_set_pix_format(cnt, vid_source, &width, &height))
|
|
goto err;
|
|
|
|
if (v4l2_scan_controls(vid_source))
|
|
goto err;
|
|
|
|
#if 0
|
|
v4l2_set_fps(vid_source);
|
|
#endif
|
|
if (v4l2_set_mmap(vid_source))
|
|
goto err;
|
|
|
|
viddev->size_map = 0;
|
|
viddev->v4l_buffers[0] = NULL;
|
|
viddev->v4l_maxbuffer = 1;
|
|
viddev->v4l_curbuffer = 0;
|
|
|
|
viddev->v4l_fmt = VIDEO_PALETTE_YUV420P;
|
|
viddev->v4l_bufsize = (width * height * 3) / 2;
|
|
|
|
|
|
/* Update width and height with supported values from camera driver */
|
|
viddev->width = width;
|
|
viddev->height = height;
|
|
|
|
return (void *) 1;
|
|
|
|
err:
|
|
free(vid_source);
|
|
|
|
viddev->v4l2_private = NULL;
|
|
viddev->v4l2 = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* v4l2_set_input
|
|
*/
|
|
void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map,
|
|
int width, int height, struct config *conf)
|
|
{
|
|
int input = conf->input;
|
|
int norm = conf->norm;
|
|
unsigned long freq = conf->frequency;
|
|
int tuner_number = conf->tuner_number;
|
|
|
|
if (input != viddev->input || width != viddev->width || height != viddev->height ||
|
|
freq != viddev->freq || tuner_number != viddev->tuner_number || norm != viddev->norm) {
|
|
|
|
unsigned int i;
|
|
struct timeval switchTime;
|
|
unsigned int skip = conf->roundrobin_skip;
|
|
|
|
if (conf->roundrobin_skip < 0)
|
|
skip = 1;
|
|
|
|
v4l2_select_input(conf, viddev, (src_v4l2_t *) viddev->v4l2_private,
|
|
input, norm, freq, tuner_number);
|
|
|
|
gettimeofday(&switchTime, NULL);
|
|
|
|
v4l2_picture_controls(cnt, viddev);
|
|
|
|
viddev->width = width;
|
|
viddev->height = height;
|
|
|
|
/*
|
|
viddev->input = input;
|
|
viddev->norm = norm;
|
|
viddev->width = width;
|
|
viddev->height = height;
|
|
viddev->freq = freq;
|
|
viddev->tuner_number = tuner_number;
|
|
*/
|
|
|
|
/* Skip all frames captured before switchtime, capture 1 after switchtime */
|
|
{
|
|
src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private;
|
|
unsigned int counter = 0;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: set_input_skip_frame "
|
|
"switch_time=%ld:%ld", switchTime.tv_sec, switchTime.tv_usec);
|
|
|
|
/* Avoid hang using the number of mmap buffers */
|
|
while(counter < vid_source->req.count) {
|
|
counter++;
|
|
if (v4l2_next(cnt, viddev, map, width, height))
|
|
break;
|
|
|
|
if (vid_source->buf.timestamp.tv_sec > switchTime.tv_sec ||
|
|
(vid_source->buf.timestamp.tv_sec == switchTime.tv_sec &&
|
|
vid_source->buf.timestamp.tv_usec > switchTime.tv_usec))
|
|
break;
|
|
|
|
MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: got frame before "
|
|
" switch timestamp=%ld:%ld",
|
|
vid_source->buf.timestamp.tv_sec,
|
|
vid_source->buf.timestamp.tv_usec);
|
|
}
|
|
}
|
|
|
|
/* skip a few frames if needed */
|
|
for (i = 1; i < skip; i++)
|
|
v4l2_next(cnt, viddev, map, width, height);
|
|
} else {
|
|
/* No round robin - we only adjust picture controls */
|
|
v4l2_picture_controls(cnt, viddev);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* v4l2_next
|
|
*/
|
|
int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map,
|
|
int width, int height)
|
|
{
|
|
sigset_t set, old;
|
|
src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private;
|
|
|
|
if (viddev->v4l_fmt != VIDEO_PALETTE_YUV420P)
|
|
return V4L_FATAL_ERROR;
|
|
|
|
/* 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);
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 1) vid_source->pframe %i",
|
|
vid_source->pframe);
|
|
|
|
if (vid_source->pframe >= 0) {
|
|
if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF");
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
memset(&vid_source->buf, 0, sizeof(struct v4l2_buffer));
|
|
|
|
vid_source->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
vid_source->buf.memory = V4L2_MEMORY_MMAP;
|
|
vid_source->buf.bytesused = 0;
|
|
|
|
if (xioctl(vid_source, VIDIOC_DQBUF, &vid_source->buf) == -1) {
|
|
int ret;
|
|
/*
|
|
* 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) {
|
|
vid_source->pframe++;
|
|
|
|
if ((u32)vid_source->pframe >= vid_source->req.count)
|
|
vid_source->pframe = 0;
|
|
|
|
vid_source->buf.index = vid_source->pframe;
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF: EIO "
|
|
"(vid_source->pframe %d)", vid_source->pframe);
|
|
ret = 1;
|
|
} else if (errno == EAGAIN) {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF: EAGAIN"
|
|
" (vid_source->pframe %d)", vid_source->pframe);
|
|
ret = 1;
|
|
} else {
|
|
MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF");
|
|
ret = -1;
|
|
}
|
|
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL);
|
|
return ret;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 2) vid_source->pframe %i",
|
|
vid_source->pframe);
|
|
|
|
vid_source->pframe = vid_source->buf.index;
|
|
vid_source->buffers[vid_source->buf.index].used = vid_source->buf.bytesused;
|
|
vid_source->buffers[vid_source->buf.index].content_length = vid_source->buf.bytesused;
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 3) vid_source->pframe %i "
|
|
"vid_source->buf.index %i", vid_source->pframe, vid_source->buf.index);
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: vid_source->buf.bytesused %i",
|
|
vid_source->buf.bytesused);
|
|
|
|
pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */
|
|
|
|
{
|
|
video_buff *the_buffer = &vid_source->buffers[vid_source->buf.index];
|
|
|
|
MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: the_buffer index %d Address (%x)",
|
|
vid_source->buf.index, the_buffer->ptr);
|
|
|
|
switch (vid_source->dst_fmt.fmt.pix.pixelformat) {
|
|
case V4L2_PIX_FMT_RGB24:
|
|
conv_rgb24toyuv420p(map, the_buffer->ptr, width, height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
conv_uyvyto420p(map, the_buffer->ptr, (unsigned)width, (unsigned)height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
conv_yuv422to420p(map, the_buffer->ptr, width, height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
memcpy(map, the_buffer->ptr, viddev->v4l_bufsize);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_PJPG:
|
|
case V4L2_PIX_FMT_JPEG:
|
|
case V4L2_PIX_FMT_MJPEG:
|
|
return mjpegtoyuv420p(map, the_buffer->ptr, width, height,
|
|
vid_source->buffers[vid_source->buf.index].content_length);
|
|
|
|
/* FIXME: quick hack to allow work all bayer formats */
|
|
case V4L2_PIX_FMT_SBGGR16:
|
|
case V4L2_PIX_FMT_SGBRG8:
|
|
case V4L2_PIX_FMT_SGRBG8:
|
|
/* case V4L2_PIX_FMT_SPCA561: */
|
|
case V4L2_PIX_FMT_SBGGR8: /* bayer */
|
|
bayer2rgb24(cnt->imgs.common_buffer, the_buffer->ptr, width, height);
|
|
conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height);
|
|
return 0;
|
|
|
|
case V4L2_PIX_FMT_SPCA561:
|
|
case V4L2_PIX_FMT_SN9C10X:
|
|
sonix_decompress(map, the_buffer->ptr, width, height);
|
|
bayer2rgb24(cnt->imgs.common_buffer, map, width, height);
|
|
conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* v4l2_close
|
|
*/
|
|
void v4l2_close(struct video_dev *viddev)
|
|
{
|
|
src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private;
|
|
enum v4l2_buf_type type;
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
xioctl(vid_source, VIDIOC_STREAMOFF, &type);
|
|
close(vid_source->fd);
|
|
vid_source->fd = -1;
|
|
}
|
|
|
|
/**
|
|
* v4l2_cleanup
|
|
*/
|
|
void v4l2_cleanup(struct video_dev *viddev)
|
|
{
|
|
src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private;
|
|
|
|
if (vid_source->buffers) {
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < vid_source->req.count; i++)
|
|
munmap(vid_source->buffers[i].ptr, vid_source->buffers[i].size);
|
|
|
|
free(vid_source->buffers);
|
|
vid_source->buffers = NULL;
|
|
}
|
|
|
|
free(vid_source->controls);
|
|
vid_source->controls = NULL;
|
|
|
|
free(vid_source);
|
|
viddev->v4l2_private = NULL;
|
|
}
|
|
#endif /* !WITHOUT_V4L && MOTION_V4L2 */
|