/* * vo_v4lw.c * * Copyright (C) Tilmann Bitterberg - Nov 2001 * * This 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, or (at your option) * any later version. * * This 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Updated 4/27/2006 - sfranzyshen <sfranzyshen at hotmail.com> * compiled against MPlayer-1.0pre7try2 *//* Quick v4lw loopback howto: - get the vloopback kernel module from <http://motion.technolust.cx/vloopback/index.html> or (alternative) my hacked version from <http://tibit.org/video/vloopback-0.90-tibit.tar.gz> - if using my version, you don't need to specify dev_offset. - untar and compile it tar xzf vloopback-VERSION.tar.gz && cd vloopback-VERSION make - load the vloopback module insmod vloopback.o dev_offset=2 pipes=1 - Make the devices accessible chmod og+rw /dev/v4l/video2 chmod og+rw /dev/v4l/video3 - eventually you have to recompile mplayer to let it see that it can talk v4l. ./configure --enable-v4lw to be sure - start mplayer mplayer -vo v4lw movie.avi - start xawtv xawtv -c /dev/v4l/video3 -geometry 800x600 - done easy, isn't it. NOTES: - avicap doesn't work yet. I got mp1e to work with a my hacked vloopback.o, you need to specify the exact size. - no scaling of video output is possible right now. - be sure to insmod vloopback with a dev_offset appropriate for your system. If you have a real grabber card (eg. a bttv which is at /dev/v4l/video0) you _HAVE_ to use at least a dev_offset=1 or you'll crash your kernel. - I am on devfs, if you're not you have to use /dev/video2 instead of /dev/v4l/video2 and may have to create the devices manually. Eg. for video2: mknod c 81 2 /dev/video2 - mplayer will tell you the v4lw output device from which you have to read. Performance: System: 2 * 1GHz Intel PIII (Coppermine) #SMP MPEG1 at 768x576 mplayer CPU Usage ca. 35%, xawtv 11% Divx at 512x384 mplayer CPU Usage ca. 20%, xawtv 10% */#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <fcntl.h>#include <string.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <signal.h>#include <sys/wait.h>#include <linux/videodev.h>#include <errno.h>#include <sys/poll.h>#include <dirent.h>#include <sys/utsname.h>#include "config.h"#include "video_out.h"#include "video_out_internal.h"#include "fastmemcpy.h"#include "sub.h"#include "aspect.h"#include "mp_msg.h"static uint32_t image_width, image_height;static int devout;static uint8_t *image;static unsigned int image_format=0;static int bpp = 24;static int fmt;#define MAXIOCTL 1024#define MAXWIDTH 1280#define MAXHEIGHT 1024int width;int height;char ioctlbuf[MAXIOCTL];int v4ldev;char *image_out;int get_frame(void){ int i; char colour = 0; memset(image_out, 0x128, width*height*3); for (i=10; i<width-10; i++) { image_out[10*width*3+i*3]=colour++; image_out[10*width*3+i*3+1]=0; image_out[10*width*3+i*3+2]=-colour; } for (i=10; i<width-10; i++) { image_out[(height-10)*width*3+i*3]=colour; image_out[(height-10)*width*3+i*3+1]=0; image_out[(height-10)*width*3+i*3+2]=-colour++; } /* */ usleep(500); /* BIG XXX */ return 0;}int v4l_ioctl(unsigned long int cmd, void *arg){ int i; switch (cmd) { case VIDIOCGCAP: { struct video_capability *vidcap=arg; sprintf(vidcap->name, "Jeroen's dummy v4l driver"); vidcap->type= VID_TYPE_CAPTURE; vidcap->channels=1; vidcap->audios=0; vidcap->maxwidth=MAXWIDTH; vidcap->maxheight=MAXHEIGHT; vidcap->minwidth=20; vidcap->minheight=20; return 0; } case VIDIOCGCHAN: { struct video_channel *vidchan= (struct video_channel *)arg; printf("VIDIOCGCHAN called\n"); if (vidchan->channel!=0) ;//return 1; vidchan->channel=0; vidchan->flags=0; vidchan->tuners=0; vidchan->norm=0; vidchan->type=VIDEO_TYPE_CAMERA; strcpy(vidchan->name, "Loopback"); return 0; } case VIDIOCSCHAN: { int *v=arg; if (v[0]!=0) return 1; return 0; } case VIDIOCGTUNER: { struct video_tuner *v = arg; if(v->tuner) { printf("VIDIOCGTUNER: Invalid Tuner, was %d\n", v->tuner); //return -EINVAL; } v->tuner=0; strcpy(v->name, "Format"); v->rangelow=0; v->rangehigh=0; v->flags=0; v->mode=VIDEO_MODE_AUTO; return 1; } case VIDIOCGPICT: { struct video_picture *vidpic=arg; vidpic->colour=0x8000; vidpic->hue=0x8000; vidpic->brightness=0x8000; vidpic->contrast=0x8000; vidpic->whiteness=0x8000; vidpic->depth=0x8000; vidpic->palette=fmt; return 0; } case VIDIOCSPICT: { struct video_picture *vidpic=arg; if (vidpic->palette!=fmt) return 1; return 0; } case VIDIOCGWIN: { struct video_window *vidwin=arg; vidwin->x=0; vidwin->y=0; vidwin->width=width; vidwin->height=height; vidwin->chromakey=0; vidwin->flags=0; vidwin->clipcount=0; return 0; } case VIDIOCSWIN: { struct video_window *vidwin=arg; if (vidwin->width > MAXWIDTH || vidwin->height > MAXHEIGHT ) return 1; if (vidwin->flags) return 1; width=vidwin->width; height=vidwin->height; printf("new size: %dx%d\n", width, height); return 0; } case VIDIOCGMBUF: { struct video_mbuf *vidmbuf=arg; vidmbuf->size=width*height*3; vidmbuf->frames=1; for (i=0; i<vidmbuf->frames; i++) vidmbuf->offsets[i]=i*vidmbuf->size; return 0; } case VIDIOCMCAPTURE: { struct video_mmap *vidmmap=arg; //return 0; if (vidmmap->height>MAXHEIGHT || vidmmap->width>MAXWIDTH || vidmmap->format!=fmt ) return 1; if (vidmmap->height!=height || vidmmap->width!=width) { height=vidmmap->height; width=vidmmap->width; printf("new size: %dx%d\n", width, height); } // check if 'vidmmap->frame' is valid // initiate capture for 'vidmmap->frame' frames return 0; } case VIDIOCSYNC: { //struct video_mmap *vidmmap=arg; // check if frames are ready. // wait until ready. get_frame(); return 0; } default: { printf("unknown ioctl: %ld\n", cmd & 0xff); return 1; } } return 0;}static vo_info_t info ={ "video4linux write output", "v4lw", "Tilmann Bitterberg <god at tibit.org>", "update 4/27/06 sfanzyshen <sfranzyshen at hotmail.com>"};LIBVO_EXTERN(v4lw)/* stolen from: * vloopback/example/dummy.c * returns 0 on successfull opening, !0 otherwise */int check_vidpipe(void){ int pipe_fd = -1; FILE *vloopbacks; char pipepath[255]; char buffer[255]; char *loop; char *input; char *istatus; char *output; char *ostatus; char *major; char *minor; struct utsname uts; if (uname(&uts) < 0) { printf("Unable to execute uname\nError[%s]\n",strerror(errno)); return -1; } major = strtok(uts.release, "."); minor = strtok(NULL, "."); if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { printf("Unable to decipher OS version\n"); return -1; } DIR *dir; struct dirent *dirp; const char prefix[]="/sys/class/video4linux/"; char *ptr, *io; int fd; int low=9999; int tfd; int tnum; if ((dir=opendir(prefix))== NULL) { printf( "Failed to open '%s'", prefix); return -1; } while ((dirp=readdir(dir)) != NULL) { if (!strncmp(dirp->d_name, "video", 5)) { strcpy(buffer, prefix); strcat(buffer, dirp->d_name); strcat(buffer, "/name"); if ((fd=open(buffer, O_RDONLY)) >= 0) { if ((read(fd, buffer, sizeof(buffer)-1))<0) { close(fd); continue; } ptr = strtok(buffer, " "); if (strcmp(ptr,"Video")) { close(fd); continue; } major = strtok(NULL, " "); minor = strtok(NULL, " "); io = strtok(NULL, " \n"); if (strcmp(major, "loopback") || strcmp(io, "input")) { close(fd); continue; } if ((ptr=strtok(buffer, " "))==NULL) { close(fd); continue; } tnum = atoi(minor); if (tnum < low) { strcpy(buffer, "/dev/"); strcat(buffer, dirp->d_name); if ((tfd=open(buffer, O_RDWR))>=0) { strcpy(pipepath, buffer); if (pipe_fd>=0) { close(pipe_fd); } pipe_fd = tfd; low = tnum; } } close(fd); } } } closedir(dir); if (pipe_fd >= 0) printf("Opened input of %i", pipe_fd); return 0;}/* copied from * vloopback-0.90/example/invert.c */int start_pipe (int dev, int width, int height){ struct video_capability vid_caps; struct video_window vid_win; struct video_picture vid_pic; printf("Started startpipe"); if (v4l_ioctl (VIDIOCGCAP, &vid_caps) == -1) { printf ("ioctl (VIDIOCGCAP)\nError[%s]\n",strerror(errno)); return (1); } if (v4l_ioctl (VIDIOCGPICT, &vid_pic)== -1) { printf ("ioctl VIDIOCGPICT\nError[%s]\n",strerror(errno)); return (1); } /* this is probably brain dead */ /* vid_pic.palette=VIDEO_PALETTE_RGB24; */ /* vid_pic.palette=VIDEO_PALETTE_YUV420P; */ vid_pic.palette = fmt; if (v4l_ioctl (VIDIOCSPICT, &vid_pic)== -1) { printf ("ioctl VIDIOCSPICT\nError[%s]\n",strerror(errno)); return (1); } if (v4l_ioctl (VIDIOCGWIN, &vid_win)== -1) { printf ("ioctl VIDIOCGWIN\nError[%s]\n",strerror(errno)); return (1); } vid_win.width = width; vid_win.height = height; if (v4l_ioctl (VIDIOCSWIN, &vid_win)== -1) { printf ("ioctl VIDIOCSWIN\nError[%s]\n",strerror(errno)); return (1); } return 0;}/* stolen from vo_odivx.c *//* orig size -- working */static int draw_slice(uint8_t *src[], int stride[], int w,int h,intx,int y){ uint8_t *s; uint8_t *d; int i; int dstride=image_width; int size = image_width*image_height; // copy Y d=image+dstride*y+x; s=src[0]; for(i=0;i<h;i++){ memcpy(d,s,w); s+=stride[0]; d+=dstride; } w/=2;h/=2;x/=2;y/=2; dstride/=2; // copy U d=image + size + dstride*y+x; s=src[1]; for(i=0;i<h;i++){ memcpy(d,s,w); s+=stride[1]; d+=dstride; } // copy V d=image + size + size/4 + dstride*y+x; s=src[2]; for(i=0;i<h;i++){ memcpy(d,s,w); s+=stride[2]; d+=dstride; } return 0;}static void draw_osd(void){}static void put_image(void){ int size = image_width*image_height*3; if (write(devout, image, size) != size) { perror("V4lW: Error writing image to pipe!"); }}static void flip_page(void){ put_image();}static int draw_frame(uint8_t *src[]){ /* untested */ printf("Started drawframe"); memcpy (image, src, image_width * image_height * 3); return 0;}static int query_format(uint32_t format){ switch(format){ case IMGFMT_YV12: case IMGFMT_I420: case IMGFMT_YUY2: case IMGFMT_RGB24: case IMGFMT_BGR24: return 1; } return 0;}static int config (uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format){ image_width = width; image_height = height; image_format = format; switch (image_format) { case IMGFMT_YV12: case IMGFMT_I420: fmt = VIDEO_PALETTE_YUV420P; break; case IMGFMT_RGB24: case IMGFMT_BGR24: fmt = VIDEO_PALETTE_RGB24; break; } if (check_vidpipe() != 0) { return 1; } if (start_pipe(devout, width, height) != 0) { printf("You got sqa nooooed!"); return 1; } printf("V4lW: width: %d; height: %d; format: %d\n", width, height, format); image = (uint8_t *) malloc(width*height*3); if (!image) { close (devout); return 1; } /* clear the buffer, grey */ memset(image,0x80,width*height*3); return 0;}static const vo_info_t*get_info(void){ return &info;}static voiduninit(void){ if (image) free(image); close (devout);}static void check_events(void){}static int preinit(const char *arg){ return 0;}static int control(uint32_t request, void *data, ...){ switch (request) { case VOCTRL_QUERY_FORMAT: return query_format(*((uint32_t*)data)); } return VO_NOTIMPL;}