view example/dummy.c @ 0:5f21a4dddc0c

Initial checkin
author KennethLavrsen
date Sun, 01 Apr 2007 05:22:43 +0000
parents
children 2fce9e157b8d
line wrap: on
line source

/*	dummy.c
 *
 *	Example program for using a videoloopback device in zero-copy mode.
 *	Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
 *	Copyright 2005 by Angel Carpintero (ack@telefonica.net)
 *	This software is distributed under the GNU public license version 2
 *	See also the file 'COPYING'.
 *
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include <dirent.h>
#include <sys/utsname.h>
#include <linux/videodev.h>

/* all seem reasonable, or not? */
#define MAXIOCTL 1024
#define MAXWIDTH 640
#define MAXHEIGHT 480
int width;
int height;
int fmt=0;
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;
}

char *v4l_create (int dev, int memsize)
{
	char *map;
	
	map=mmap(0, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0);
	if ((unsigned char *)-1 == (unsigned char *)map)
		return NULL;
	return map;	
}

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;
}

#define VIDIOCSINVALID	_IO('v',BASE_VIDIOCPRIVATE+1)

void sighandler(int signo)
{
	int size, ret;
	unsigned long int cmd;
	struct pollfd ufds;

	if (signo!=SIGIO)
		return;
	ufds.fd=v4ldev;
	ufds.events=POLLIN;
	ufds.revents=0;
	poll(&ufds, 1, 1000);
	if (!ufds.revents & POLLIN) {
		printf("Received signal but got negative on poll?!?!?!?\n");
		return;
	}
	size=read(v4ldev, ioctlbuf, MAXIOCTL);
	if (size >= sizeof(unsigned long int)) {
		memcpy(&cmd, ioctlbuf, sizeof(unsigned long int));
		if (cmd==0) {
			printf("Client closed device\n");
			return;
		}
		ret=v4l_ioctl(cmd, ioctlbuf+sizeof(unsigned long int));
		if (ret) {
			memset(ioctlbuf+sizeof(unsigned long int), MAXIOCTL-sizeof(unsigned long int), 0xff);
			printf("ioctl %lx unsuccesfull, lets issue VIDIOCSINVALID (%x)\n", cmd, VIDIOCSINVALID);
			ioctl(v4ldev, VIDIOCSINVALID);
		} else
			ioctl(v4ldev, cmd, ioctlbuf+sizeof(unsigned long int));
	}
	return;
}

int open_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;
	}

	if (strcmp(minor, "5") < 0) {
	
		vloopbacks=fopen("/proc/video/vloopback/vloopbacks", "r");
		if (!vloopbacks) {
			printf ("Failed to open '/proc/video/vloopback/vloopbacks");
			return -1;
		}
		/* Read vloopback version */
		fgets(buffer, 255, vloopbacks);
		printf("%s", buffer);
		/* 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);
					pipe_fd=open(pipepath, O_RDWR);
					if (pipe_fd>=0) {
						printf("Input: /dev/%s\n", input);
						printf("Output: /dev/%s\n", output);
						return pipe_fd;
					}
				}
			} 
		}

	}else{
		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 %s", pipepath);
        }
	
	return pipe_fd;
}

int main (int argc, char **argv)
{
	char palette[10]={'\0'};

	if (argc != 3) {
		printf("dummy.c\n");
		printf("A example for using a video4linux loopback in zero-copy mode\n");
		printf("Written by Jeroen Vreeken, 2000\n");
		printf("Updated to vloopback API v0.97\n\n");
		printf("Usage:\n\n");
		printf("dummy widthxheight rgb24|yuv420p\n\n");
		printf("example: dummy 352x288 yuv420p\n\n");
		exit(1);
	}
	
	sscanf(argv[1], "%dx%d", &width, &height);
	sscanf(argv[2], "%s", palette);

	if (!strcmp(palette,"rgb24")) fmt = VIDEO_PALETTE_RGB24;
	else if (!strcmp(palette,"yuv420p")) fmt = VIDEO_PALETTE_YUV420P;
	else fmt = VIDEO_PALETTE_RGB24;
	
	/* Default startup values, nothing special 
	width=352;
	height=288;
	*/

	v4ldev=open_vidpipe();
	if (v4ldev < 0) {
		printf ("Failed to open video loopback device\nError[%s]\n",strerror(errno));
		exit(1);
	}
	image_out=v4l_create(v4ldev, MAXWIDTH*MAXHEIGHT*3);
	if (!image_out) {
		exit(1);
		printf ("Failed to set device to zero-copy mode\nError[%s]\n",strerror(errno));
	}

	signal (SIGIO, sighandler);

	printf("\nListening.\n");
	while (1) {
		sleep(1000);
	}

	close (v4ldev);
	free(image_out);
	exit(0);
}