📄 video2.c
字号:
/* * video2.c * * V4L2 interface with basically JPEG decompression support and even more ... * Copyright 2006 Krzysztof Blaszkowski (kb@sysmikro.com.pl) * 2007 Angel Carpintero (ack@telefonica.net) * 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_SBGGR8 ( sonix ) * V4L2_PIX_FMT_SN9C10X ( sonix ) * V4L2_PIX_FMT_MJPEG, ( tested ) * V4L2_PIX_FMT_JPEG, ( tested ) V4L2_PIX_FMT_RGB24, 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 ( i.ex 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. * * 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 :)*/#ifndef WITHOUT_V4L#ifdef MOTION_V4L2#include <math.h>#include <sys/utsname.h>#include <dirent.h>#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <malloc.h>#include <string.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/mman.h>#include "motion.h"#include "netcam.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#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, 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; char map; u32 fps; struct v4l2_capability cap; struct v4l2_format fmt; struct v4l2_requestbuffers req; struct v4l2_buffer buf; netcam_buff *buffers; s32 pframe; u32 ctrl_flags; struct v4l2_queryctrl *controls;} src_v4l2_t;static int xioctl(int fd, int request, void *arg){ int r; do r = ioctl(fd, request, arg); while (-1 == r && EINTR == errno); return r;}static int v4l2_get_capability(src_v4l2_t * s){ if (xioctl(s->fd, VIDIOC_QUERYCAP, &s->cap) < 0) { motion_log(LOG_ERR, 0, "Not a V4L2 device?"); return (-1); } motion_log(LOG_INFO, 0, "cap.driver: \"%s\"", s->cap.driver); motion_log(LOG_INFO, 0, "cap.card: \"%s\"", s->cap.card); motion_log(LOG_INFO, 0, "cap.bus_info: \"%s\"", s->cap.bus_info); motion_log(LOG_INFO, 0, "cap.capabilities=0x%08X", s->cap.capabilities); if (s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) motion_log(LOG_INFO, 0, "- VIDEO_CAPTURE"); if (s->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) motion_log(LOG_INFO, 0, "- VIDEO_OUTPUT"); if (s->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) motion_log(LOG_INFO, 0, "- VIDEO_OVERLAY"); if (s->cap.capabilities & V4L2_CAP_VBI_CAPTURE) motion_log(LOG_INFO, 0, "- VBI_CAPTURE"); if (s->cap.capabilities & V4L2_CAP_VBI_OUTPUT) motion_log(LOG_INFO, 0, "- VBI_OUTPUT"); if (s->cap.capabilities & V4L2_CAP_RDS_CAPTURE) motion_log(LOG_INFO, 0, "- RDS_CAPTURE"); if (s->cap.capabilities & V4L2_CAP_TUNER) motion_log(LOG_INFO, 0, "- TUNER"); if (s->cap.capabilities & V4L2_CAP_AUDIO) motion_log(LOG_INFO, 0, "- AUDIO"); if (s->cap.capabilities & V4L2_CAP_READWRITE) motion_log(LOG_INFO, 0, "- READWRITE"); if (s->cap.capabilities & V4L2_CAP_ASYNCIO) motion_log(LOG_INFO, 0, "- ASYNCIO"); if (s->cap.capabilities & V4L2_CAP_STREAMING) motion_log(LOG_INFO, 0, "- STREAMING"); if (s->cap.capabilities & V4L2_CAP_TIMEPERFRAME) motion_log(LOG_INFO, 0, "- TIMEPERFRAME"); if (!s->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { motion_log(LOG_ERR, 0, "Device does not support capturing."); return (-1); } return (0);}static int v4l2_select_input(src_v4l2_t * s, 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; if (in == 8) in = 0; /* Set the input. */ memset (&input, 0, sizeof (input)); input.index = in; if (xioctl(s->fd, VIDIOC_ENUMINPUT, &input) == -1) { motion_log(LOG_ERR, 0, "Unable to query input %d.", in); motion_log(LOG_ERR, 0, "VIDIOC_ENUMINPUT: %s", strerror(errno)); return (-1); } if (debug_level > 5) motion_log(LOG_INFO, 0, "%s: name = \"%s\", type 0x%08X, status %08x", __FUNCTION__, input.name, input.type, input.status); if ((input.type & V4L2_INPUT_TYPE_TUNER) && (debug_level > 5)) motion_log(LOG_INFO, 0, "- TUNER"); if ((input.type & V4L2_INPUT_TYPE_CAMERA) && (debug_level > 5)) motion_log(LOG_INFO, 0, "- CAMERA"); if (xioctl(s->fd, VIDIOC_S_INPUT, &in) == -1) { motion_log(LOG_ERR, 0, "Error selecting input %d", in); motion_log(LOG_ERR, 0, "VIDIOC_S_INPUT: %s", strerror(errno)); return (-1); } /* Set video standard usually webcams doesn't support the ioctl or return V4L2_STD_UNKNOWN */ if (xioctl(s->fd, VIDIOC_G_STD, &std_id) == -1) { if (debug_level > 5) motion_log(LOG_INFO, 0, "Device doesn't support VIDIOC_G_STD "); std_id = 0; // V4L2_STD_UNKNOWN = 0 } if (std_id) { memset(&standard, 0, sizeof(standard)); standard.index = 0; while (xioctl(s->fd, VIDIOC_ENUMSTD, &standard) == 0) { if ((standard.id & std_id) && (debug_level > 5)) { motion_log(LOG_INFO, 0, "- 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(s->fd, VIDIOC_S_STD, &std_id) == -1) { motion_log(LOG_ERR, 0, "Error selecting standard method %d", std_id); motion_log(LOG_ERR, 0, "VIDIOC_S_STD: %s", strerror(errno)); } } /* 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(s->fd, VIDIOC_G_TUNER, &tuner) == -1) { motion_log(LOG_ERR, 0, "VIDIOC_G_TUNER: %s", strerror(errno)); return (0); } /* 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(s->fd, VIDIOC_S_FREQUENCY, &freq) == -1) { motion_log(LOG_ERR, 0, "VIDIOC_S_FREQUENCY: %s", strerror(errno)); return (0); } } return (0);}static int v4l2_set_pix_format(struct context *cnt, src_v4l2_t * s, int *width, int *height){ struct v4l2_fmtdesc fmt; short int v4l2_pal; static const u32 supported_formats[] = { /* higher index means better chance to be used */ V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420, 0 }; short int index_format = -1; memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); fmt.index = v4l2_pal = 0; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; motion_log(LOG_INFO, 0, "Supported palettes:"); while (xioctl(s->fd, VIDIOC_ENUM_FMT, &fmt) != -1) { short int i; motion_log(LOG_INFO, 0, "%i: %c%c%c%c (%s)", v4l2_pal, fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24, fmt.description); for (i = 0; supported_formats[i]; i++) if (supported_formats[i] == fmt.pixelformat && i > index_format) { if (cnt->conf.v4l2_palette == i) { index_format = cnt->conf.v4l2_palette; motion_log(LOG_INFO, 0, "Selected palette %c%c%c%c", fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24); i=10; break; } index_format = i; } /* Chosen our selected palette, break from while */ if (index_format == cnt->conf.v4l2_palette && index_format >= 0) break; memset(&fmt, 0, sizeof(struct v4l2_fmtdesc)); fmt.index = ++v4l2_pal; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } if (index_format >= 0) { u32 pixformat = supported_formats[index_format]; memset(&s->fmt, 0, sizeof(struct v4l2_format)); s->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; s->fmt.fmt.pix.width = *width; s->fmt.fmt.pix.height = *height; s->fmt.fmt.pix.pixelformat = pixformat; s->fmt.fmt.pix.field = V4L2_FIELD_ANY; if (xioctl(s->fd, VIDIOC_TRY_FMT, &s->fmt) != -1 && s->fmt.fmt.pix.pixelformat == pixformat) { motion_log(LOG_INFO, 0, "index_format %d Test palette %c%c%c%c (%dx%d)", index_format, pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, *width, *height); if (s->fmt.fmt.pix.width != (unsigned int) *width || s->fmt.fmt.pix.height != (unsigned int) *height) { motion_log(LOG_INFO, 0, "Adjusting resolution from %ix%i to %ix%i.", *width, *height, s->fmt.fmt.pix.width, s->fmt.fmt.pix.height); *width = s->fmt.fmt.pix.width; *height = s->fmt.fmt.pix.height; } if (xioctl(s->fd, VIDIOC_S_FMT, &s->fmt) == -1) { motion_log(LOG_ERR, 0, "Error setting pixel format."); motion_log(LOG_ERR, 0, "VIDIOC_S_FMT: %s", strerror(errno)); return (-1); } motion_log(LOG_INFO, 0, "Using palette %c%c%c%c (%dx%d) bytesperlines %d sizeimage %d colorspace %08x", pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, *width, *height, s->fmt.fmt.pix.bytesperline, s->fmt.fmt.pix.sizeimage, s->fmt.fmt. pix.colorspace); /* TODO: Review when it has been tested */ if (pixformat == V4L2_PIX_FMT_MJPEG) { struct v4l2_jpegcompression v4l2_jpeg; if (xioctl(s->fd, VIDIOC_G_JPEGCOMP, &v4l2_jpeg) == -1) { motion_log(LOG_ERR, 0, "VIDIOC_G_JPEGCOMP not supported but it should"); } else { v4l2_jpeg.jpeg_markers |= V4L2_JPEG_MARKER_DHT; if (xioctl(s->fd, VIDIOC_S_JPEGCOMP, &v4l2_jpeg) == -1) motion_log(LOG_ERR, 0, "VIDIOC_S_JPEGCOMP %s", strerror(errno)); } } return 0; } motion_log(LOG_ERR, 0, "VIDIOC_TRY_FMT failed for format %c%c%c%c (%s).", pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, strerror(errno)); return -1; } motion_log(LOG_ERR, 0, "Unable to find a compatible palette format."); return (-1);}#if 0static void v4l2_set_fps(src_v4l2_t * s){ struct v4l2_streamparm* setfps; setfps=(struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm)); 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=s->fps; if (xioctl(s->fd, VIDIOC_S_PARM, setfps) == -1){ motion_log(LOG_ERR, 0, "v4l2_set_fps VIDIOC_S_PARM %s",strerror(errno)); }}#endifstatic int v4l2_set_mmap(src_v4l2_t * s){ enum v4l2_buf_type type; u32 b; /* Does the device support streaming? */ if (!s->cap.capabilities & V4L2_CAP_STREAMING) return (-1); memset(&s->req, 0, sizeof(struct v4l2_requestbuffers)); s->req.count = MMAP_BUFFERS; s->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; s->req.memory = V4L2_MEMORY_MMAP; if (xioctl(s->fd, VIDIOC_REQBUFS, &s->req) == -1) { motion_log(LOG_ERR, 1, "Error requesting buffers for memory map."); motion_log(LOG_ERR, 0, "VIDIOC_REQBUFS: %s", strerror(errno)); return (-1); } motion_log(LOG_DEBUG, 0, "mmap information:"); motion_log(LOG_DEBUG, 0, "frames=%d", s->req.count); if (s->req.count < MIN_MMAP_BUFFERS) { motion_log(LOG_ERR, 0, "Insufficient buffer memory."); return (-1); } s->buffers = calloc(s->req.count, sizeof(netcam_buff)); if (!s->buffers) { motion_log(LOG_ERR, 1, "%s: Out of memory.", __FUNCTION__); return (-1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -