📄 input-v4l.c
字号:
/* * Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org> * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <config.h>#include <sys/types.h>#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <errno.h>#include <pthread.h>#include <sys/mman.h>#include <sys/ioctl.h>#ifdef HAVE_ASM_TYPES_H#include <asm/types.h>#endif#ifdef HAVE_LINUX_COMPILER_H#include <linux/compiler.h>#endif#include <linux/videodev.h>#ifdef HAVE_PWC_IOCTL_H#include <pwc-ioctl.h>#endif#include <event.h>#include <log.h>#include <frame.h>#include <stream.h>#include <inputs.h>#include <conf_parse.h>#define INPUTTYPE_WEBCAM 1#define INPUTTYPE_NTSC 2#define INPUTTYPE_PAL 3struct v4l_input { struct stream *output; struct frame_exchanger *ex; char device[256]; int width; int height; int fincr; int fbase; int inputport; int inputtype;#ifdef HAVE_PWC_IOCTL_H struct pwc_whitebalance pwc_whitebalance;#endif int fps; int fd; struct video_mbuf vm; unsigned char *mmap_buf; int cur_frame; pthread_t thread; int running;};static void copy_yuv420p_to_uyvy( unsigned char *dest, unsigned char *src, int width, int height ){ unsigned char *p, *y, *u, *v; int r, c; p = dest; y = src; u = y + width * height; v = u + ( width / 2 ) * ( height / 2 ); for( r = 0; r < height; ++r ) for( c = 0; c < width; c += 2 ) { *(p++) = u[( r / 2 ) * ( width / 2 ) + ( c / 2 )]; *(p++) = y[r * width + c]; *(p++) = v[( r / 2 ) * ( width / 2 ) + ( c / 2 )]; *(p++) = y[r * width + c + 1]; }}static void *capture_loop( void *d ){ struct v4l_input *conf = (struct v4l_input *)d; struct video_mmap mm; struct frame *f; struct timeval now, start; int frames = 0, cur_buf, i; start.tv_sec = 0; mm.frame = 0; mm.width = conf->width; mm.height = conf->height; mm.format = VIDEO_PALETTE_YUV420P; if( ioctl( conf->fd, VIDIOCMCAPTURE, &mm ) < 0 ) { spook_log( SL_ERR, "v4l: aborting on error in VIDIOCMCAPTURE: %s", strerror( errno ) ); exit( 1 ); } cur_buf = 1; for(;;) { mm.frame = cur_buf; mm.width = conf->width; mm.height = conf->height; mm.format = VIDEO_PALETTE_YUV420P; if( ioctl( conf->fd, VIDIOCMCAPTURE, &mm ) < 0 ) { spook_log( SL_ERR, "v4l: aborting on error in VIDIOCMCAPTURE: %s", strerror( errno ) ); exit( 1 ); } cur_buf = cur_buf ^ 1; if( ioctl( conf->fd, VIDIOCSYNC, &cur_buf ) < 0 ) { spook_log( SL_ERR, "v4l: aborting on error in VIDIOCSYNC: %s", strerror( errno ) ); exit( 1 ); } if( conf->fincr == 0 ) { if( start.tv_sec == 0 ) { gettimeofday( &start, NULL ); continue; } gettimeofday( &now, NULL ); ++frames; if( now.tv_sec >= start.tv_sec + 8 && now.tv_usec >= start.tv_usec ) { i = ( now.tv_sec - start.tv_sec ) * 1000000 + now.tv_usec - start.tv_usec; i = ( (double)1000000 * (double)frames / (double)i + 0.005 ) * (double)100; if( i % 100 == 0 ) { conf->fincr = 1; conf->fbase = i / 100; } else if( i % 10 == 0 ) { conf->fincr = 10; conf->fbase = i / 10; } else { conf->fincr = 100; conf->fbase = i; } spook_log( SL_INFO, "guessed frame rate at %.2f", (double)conf->fbase/(double)conf->fincr ); spook_log( SL_DEBUG, "fincr = %d, fbase = %d", conf->fbase, conf->fincr ); frames = 0; } continue; } if( ! conf->running ) { frames = 0; f = NULL; continue; } ++frames; if( ! ( f = get_next_frame( conf->ex, 0 ) ) ) { spook_log( SL_WARN, "v4l: dropping frame" ); continue; } f->length = conf->width * conf->height * 2; f->format = FORMAT_RAW_UYVY; f->width = conf->width; f->height = conf->height; f->key = 1; frames = 0; copy_yuv420p_to_uyvy( f->d, conf->mmap_buf + conf->vm.offsets[cur_buf], f->width, f->height ); deliver_frame( conf->ex, f ); } return NULL;}static void get_back_frame( struct frame *f, void *d ){ struct v4l_input *conf = (struct v4l_input *)d; exchange_frame( conf->ex, new_frame() ); deliver_frame_to_stream( f, conf->output );}static int v4l_setup( struct v4l_input *conf ){ struct video_capability vc; struct video_channel chan; struct video_picture pict; struct video_window win; int i, pwc, real_width, real_height;#ifdef HAVE_PWC_IOCTL_H struct pwc_probe pwc_probe;#ifdef VIDIOCPWCGREALSIZE struct pwc_imagesize pwc_imagesize;#endif#endif conf->cur_frame = -1; if( ( conf->fd = open( conf->device, O_RDWR ) ) < 0 ) { spook_log( SL_ERR, "v4l: unable to open %s: %s", conf->device, strerror( errno ) ); return -1; } if( ioctl( conf->fd, VIDIOCGCAP, &vc ) < 0 ) { spook_log( SL_ERR, "v4l: error determining device capabilities" ); return -1; } spook_log( SL_INFO, "v4l: using capture device \"%s\"", vc.name ); if( conf->width > vc.maxwidth ) spook_log( SL_ERR, "v4l: device supports a maximum frame width of %d; %d is too large", vc.maxwidth, conf->width ); if( conf->height > vc.maxheight ) spook_log( SL_ERR, "v4l: device supports a maximum frame height of %d; %d is too large", vc.maxheight, conf->height ); if( conf->width > vc.maxwidth || conf->height > vc.maxheight ) return -1;#ifdef HAVE_PWC_IOCTL_H /* This is apparently how we're supposed to probe for PWC devices... */ if( sscanf( vc.name, "Philips %d webcam", &i ) == 1 || ( ioctl( conf->fd, VIDIOCPWCPROBE, &pwc_probe ) == 0 && ! strcmp( vc.name, pwc_probe.name ) ) ) { spook_log( SL_INFO, "v4l: activating support for Philips webcams" ); pwc = 1; } else#endif pwc = 0; if( conf->inputport < 0 || conf->inputport >= vc.channels ) { spook_log( SL_ERR, "v4l: input port is invalid! Valid input ports:" ); for( i = 0; i < vc.channels; ++i ) { chan.channel = i; if( ioctl( conf->fd, VIDIOCGCHAN, &chan ) < 0 ) { spook_log( SL_ERR, "v4l: error getting info on input port" ); return -1; } spook_log( SL_INFO, "v4l: input port %d: %s", chan.channel, chan.name ); } return -1; } chan.channel = conf->inputport; switch( conf->inputtype ) { case INPUTTYPE_NTSC: chan.norm = VIDEO_MODE_NTSC; break; case INPUTTYPE_PAL: chan.norm = VIDEO_MODE_PAL; break; default: chan.norm = VIDEO_MODE_AUTO; break; } if( ioctl( conf->fd, VIDIOCSCHAN, &chan ) < 0 ) { spook_log( SL_ERR, "v4l: error selecting input port" ); return -1; } if( ioctl( conf->fd, VIDIOCGPICT, &pict ) < 0 ) { spook_log( SL_ERR, "v4l: error querying palette parameters" ); return -1; } pict.palette = VIDEO_PALETTE_YUV420P; pict.depth = 24; if( ioctl( conf->fd, VIDIOCSPICT, &pict ) < 0 ) { spook_log( SL_ERR, "v4l: error setting palette parameters" ); return -1; } win.x = win.y = 0; win.width = conf->width; win.height = conf->height; win.chromakey = 0; win.flags = 0; win.clips = NULL; win.clipcount = 0; if( conf->inputtype == INPUTTYPE_WEBCAM && conf->fps > 0 ) {#ifdef HAVE_PWC_IOCTL_H if( pwc ) win.flags |= conf->fps << PWC_FPS_SHIFT; else#endif { spook_log( SL_ERR, "v4l: don't know how to set frame rate; try using \"FrameRate auto\"" ); return -1; } } if( ioctl( conf->fd, VIDIOCSWIN, &win ) < 0 ) { if( conf->inputtype == INPUTTYPE_WEBCAM && conf->fps > 0 ) spook_log( SL_ERR, "v4l: unable to set requested frame size/frame rate" ); else spook_log( SL_ERR, "v4l: unable to set requested frame size" ); return -1; } memset( &win, 0, sizeof( win ) ); if( ioctl( conf->fd, VIDIOCGWIN, &win ) < 0 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -