[MPlayer-dev-eng] New vo_v4lw video output module

Tilmann Bitterberg tilmann at bitterberg.de
Wed Nov 28 01:42:43 CET 2001


Hi,

I hacked together a new libvo module called vo_v4lw.c. It
provides write support for the video4linux api. This means that
mplayer can act as a video4linux device providing (uncompressed)
YUV/RGB data to other v4l applications like xawtv, mp1e etc.

This is a generic way to use other recompressors over a well
known API.

The main work is done by a linux kernel module called vloopback
which provides a v4l loopback device. vloopback provides at
least 2 v4l devices, one is the input pipe and the other one the
output. Read more on <http://motion.technolust.cx/vloopback/index.html>.

I did not wrote vloopback but hacked on it too and you probably
want my version which is at <http://tibit.org/video/>

A (very) quick Howto follows (more on the mentioned site):
- patch mplayer
- get vloopback from http://tibit.org/video/vloopback-0.90-tibit.tar.gz
- compile and load module vloopback
- compile mplayer (./configure will detect v4lw)
- run mplayer (it will tell you the output device)
  ./mplayer -vo v4lw movie.avi
- run xawtv
  xawtv -c /dev/v4l/video1 -geometry 800x600

Limitations:
- No scaling possible, so you have to tell grabbing applications
  the *exact* size.
- RGB is not tested (draw_frame too).
- No sound.
- Only tested with Linux 2.4.X and devfs

If you find that code useful you're welcome to include it into
your amazing project.

Tilmann
please CC: me, since I'm not yet on the list.
-- 
If something can go wrong, it w
fortune: segmentation fault.  core dumped
-------------- next part --------------
diff -Nur MPlayer-0.50/configure MPlayer-0.50-tibit/configure
--- MPlayer-0.50/configure	Tue Nov 20 18:06:03 2001
+++ MPlayer-0.50-tibit/configure	Tue Nov 20 16:22:12 2001
@@ -144,6 +144,7 @@
         --enable-sdl            build with SDL render support [autodetect]
         --enable-aa             build with AAlib render support [autodetect]
         --enable-ggi            build with GGI render support [autodetect]
+	--enable-v4l            build with video4linux write support [autodetect]
         --enable-mga            build with mga_vid support [autodetect, if /dev/mga_vid
                                 is available]
 	--enable-xmga           build with mga_vid X Window support [autodetect,
@@ -511,6 +512,7 @@
 _xmga=autodetect
 _dga=no
 _dga2=no
+_v4l=no
 _svga=no
 _fbdev=no
 [ "$system_name" = Linux ] && _fbdev=yes
@@ -589,6 +591,11 @@
 fi
 
 
+if [ -d /proc/video/vloopback ]; then
+ _v4l=yes
+fi
+
+
 if [ -c /dev/mga_vid ]; then
  _mga=yes
  _syncfb=yes
@@ -1176,6 +1183,9 @@
   --enable-ggi)
         _ggi=yes
         ;;
+  --enable-v4l)
+        _v4l=yes
+        ;;
   --enable-mga)
         _mga=yes
         ;;
@@ -1289,6 +1299,9 @@
   --disable-ggi)
         _ggi=no
         ;;
+  --disable-v4l)
+        _v4l=no
+        ;;
   --disable-mga)
         _mga=no
         ;;
@@ -1550,6 +1563,7 @@
 echo "Screen size ... ${_x}x${_y}"
 echo "Checking for X11 libs ... $_x11libdir"
 echo "Checking for X11 headers ... $_x11incdir"
+echo "Checking v4l loopback ... $_v4l"
 echo "Checking mga_vid device ... $_mga"
 echo "Checking for xmga ... $_xmga" 
 echo "Checking for SDL ... $_sdl"
@@ -2059,6 +2073,12 @@
 fi
 
 # ---
+if [ $_v4l = yes ]; then
+ _v4l='#define HAVE_V4L'
+ _vosrc=$_vosrc' vo_v4l.c'
+else
+ _v4l='#undef HAVE_V4L'
+fi
 
 if [ $_mga = yes ]; then
  _mga='#define HAVE_MGA'
@@ -2408,6 +2428,7 @@
 $_ggi
 $_3dfx
 $_tdfxfb
+$_v4l
 $_mga
 $_xmga
 $_syncfb
diff -Nur MPlayer-0.50/libvo/video_out.c MPlayer-0.50-tibit/libvo/video_out.c
--- MPlayer-0.50/libvo/video_out.c	Tue Nov 20 18:06:03 2001
+++ MPlayer-0.50-tibit/libvo/video_out.c	Tue Nov 20 16:23:43 2001
@@ -70,6 +70,7 @@
 extern vo_functions_t video_out_ggi;
 extern vo_functions_t video_out_aa;
 extern vo_functions_t video_out_mpegpes;
+extern vo_functions_t video_out_v4l;
 
 vo_functions_t* video_out_drivers[] =
 {
@@ -116,6 +117,9 @@
 #endif
 #ifdef HAVE_AA
 	&video_out_aa,
+#endif
+#ifdef HAVE_V4L
+	&video_out_v4l,
 #endif
 
 #ifdef HAVE_PNG
diff -Nur MPlayer-0.50/libvo/vo_v4l.c MPlayer-0.50-tibit/libvo/vo_v4l.c
--- MPlayer-0.50/libvo/vo_v4l.c	Thu Jan  1 00:00:00 1970
+++ MPlayer-0.50-tibit/libvo/vo_v4l.c	Sat Nov 24 19:00:54 2001
@@ -0,0 +1,399 @@
+/* 
+ *  vo_v4l.c
+ *
+ *	Copyright (C) Aaron Holtzman - June 2000
+ *
+ *  This file is part of mpeg2dec, a free MPEG-2 video stream decoder.
+ *	
+ *  mpeg2dec 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.
+ *   
+ *  mpeg2dec 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. 
+ *
+ */
+
+/*
+   Quick v4l loopback howto:
+
+   - get the vloopback kernel module from 
+     <http://motion.technolust.cx/vloopback/index.html>
+
+   - 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-v4l
+     to be sure
+   
+   - start mplayer
+   mplayer -vo v4l movie.avi
+
+   - start xawtv
+   xawtv -c /dev/v4l/video5 -geometry 800x600
+
+   - done
+     easy, isn't it.
+
+   NOTES:
+   - avicap and mp1e don't work yet, they want mmap'ed buffers 
+     and I can't provide them (right now). Thats not fully correct, 
+     I got mp1e to work with a hacked vloopback.o
+   - 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 v4l 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 "config.h"
+#include "video_out.h"
+#include "video_out_internal.h"
+
+LIBVO_EXTERN(v4l)
+
+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;
+
+static vo_info_t vo_info = 
+{
+	"v4l video output",
+	"v4l",
+	"Tilmann Bitterberg <god at tibit.org>",
+	"experimental"
+};
+
+/* stolen from:
+ * vloopback/example/dummy.c
+ * returns 0 on successfull opening, !0 otherwise 
+ */
+int check_vidpipe(void)
+{
+	FILE *vloopbacks;
+	char pipepath[255];
+	char buffer[255];
+	char *loop;        /* internal loop nr */
+	char *input;       /* our output dev */
+	char *istatus;
+	char *output;      /* user should read from */
+	char *ostatus;
+
+	vloopbacks=fopen("/proc/video/vloopback/vloopbacks", "r");
+	if (!vloopbacks) {
+		perror ("V4L: Failed to open '/proc/video/vloopback/vloopbacks'");
+		printf ("V4L: You need to load the vlooback kernel module\n");
+		return -1;
+	}
+	/* Read vloopback version */
+	fgets(buffer, 255, vloopbacks);
+	/* Read explaination line */
+	fgets(buffer, 255, vloopbacks);
+	while (fgets(buffer, 255, vloopbacks)) {
+		if (strlen(buffer)>1) {
+			buffer[strlen(buffer)-1]=0;
+			loop    = strtok(buffer, "\t");
+			input   = strtok(NULL, "\t");
+			istatus = strtok(NULL, "\t");
+			output  = strtok(NULL, "\t");
+			ostatus = strtok(NULL, "\t");
+			if (istatus[0] == '-') {
+				sprintf(pipepath, "/dev/%s", input);
+				devout = open (pipepath, O_RDWR);
+				if (devout >= 0) {
+					printf("V4L: I am writing to: /dev/%s\n", input);
+					printf("V4L: You should read from: /dev/%s\n", output);
+					/* Ok, found one */
+					return 0;
+				} else {
+					printf ("V4L: Error opening /dev/%s\n", input);
+					perror ("V4L: Failed to open output video device");
+					/* scan ahead */
+				}
+			}
+		} 
+	}
+	printf("V4L: Unable to find a available vloopback device\n");
+	printf("V4L: Maybe insmod with a greater pipes= value helps\n");
+	return -1;
+}
+/* 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;
+
+	if (ioctl (dev, VIDIOCGCAP, &vid_caps) == -1) {
+		perror ("ioctl (VIDIOCGCAP)");
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGPICT, &vid_pic)== -1) {
+		perror ("ioctl VIDIOCGPICT");
+		return (1);
+	}
+	/* this is probably brain dead */
+	/* vid_pic.palette=VIDEO_PALETTE_RGB24; */
+	/* vid_pic.palette=VIDEO_PALETTE_YUV420P; */
+	vid_pic.palette = fmt;
+
+	if (ioctl (dev, VIDIOCSPICT, &vid_pic)== -1) {
+		perror ("ioctl VIDIOCSPICT");
+		return (1);
+	}
+	if (ioctl (dev, VIDIOCGWIN, &vid_win)== -1) {
+		perror ("ioctl VIDIOCGWIN");
+		return (1);
+	}
+	vid_win.width = width;
+	vid_win.height = height;
+	if (ioctl (dev, VIDIOCSWIN, &vid_win)== -1) {
+		perror ("ioctl VIDIOCSWIN");
+		return (1);
+	}
+	return 0;
+}
+
+/* stolen from
+   vo_odivx.c
+   */
+/* orig size -- working  */
+static uint32_t draw_slice(uint8_t *src[], int stride[], int w,int h,int x,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;
+}
+/*
+   printf("%d, %d, %d, %d, %d, %d, %d\n", stride[0], stride[1], stride[2], w, h, x, y);
+   720, 360, 360, 720, 16, 0, 0
+   720, 360, 360, 720, 16, 0, 16
+   ...
+   720, 360, 360, 720, 16, 0, 544
+   720, 360, 360, 720, 16, 0, 560
+*/
+#if 0
+static uint32_t draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y)
+{
+	uint8_t *s;
+	uint8_t *d;
+	int i;
+	int j;
+	int dstride = image_width;
+	int size = image_width*image_height;
+
+	stride[0]<<=1;
+	stride[1]<<=1;
+	stride[2]<<=1;
+
+	// copy Y
+	d=image+dstride/2*y+x;
+	s=src[0];
+	for(i=0;i<h;i+=2){
+		for (j=0; j<w; j+=2)
+			d[j>>1]=s[j];
+		s+=stride[0];
+		d+=dstride;
+	}
+
+	w/=2;h/=2;x/=2;y/=2; dstride/=2;
+
+	// copy U
+	d=image + size + dstride/2*y+x;
+	s=src[1];
+	for(i=0;i<h;i+=2){
+		for (j=0; j<w; j+=2)
+			d[j>>1]=s[j];
+		s+=stride[1];
+		d+=dstride;
+	}
+
+	// copy V
+	d=image + size + size/4 + dstride/2*y+x;
+	s=src[2];
+	for(i=0;i<h;i+=2){
+		for (j=0; j<w; j+=2)
+			d[j>>1]=s[j];
+		s+=stride[2];
+		d+=dstride;
+	}
+
+	return 0;
+}
+#endif
+static void draw_osd(void)
+{
+}
+
+static void put_image(void)
+{
+	int size = image_width*image_height*3;
+	if (write(devout, image, size) != size) {
+		perror("V4L: Error writing image to pipe!");
+	}
+}
+
+static void flip_page(void)
+{
+	put_image();
+}
+
+static uint32_t draw_frame(uint8_t *src[])
+{
+	memcpy (image, src, image_width * image_height * 3);
+	return 0;
+}
+
+static uint32_t query_format(uint32_t format)
+{
+	switch(format){
+		case IMGFMT_YV12:
+		case IMGFMT_YUY2:
+		case IMGFMT_RGB24:
+		case IMGFMT_BGR24:
+			return 1;
+	}
+	return 0;
+}
+
+static uint32_t init (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) {
+		return (1);
+	}
+
+	/* printf("V4L: 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 &vo_info;
+}
+
+static void
+uninit(void)
+{
+	if (image)
+		free(image);
+	close (devout);
+}
+
+
+static void check_events(void)
+{
+}


More information about the MPlayer-dev-eng mailing list