video test app

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



Hi William/Fabrizio,

attached is the test app... I apparently removed it so I decided to just
rewrite it (GStreamer's source contained most of the functions, so I
just had to cut 'n paste it...).

Anyway, the image is flickering, don't know why, probably something
wrong with my SDL handling, but it doesn't really matter since it's only
a test anyway. I'm too lazy to fix it. ;-).

Hope it's helpful,

Ronald

-- 
Ronald Bultje <rbultje@xxxxxxxxxxxxxxxxxxx>
/* video4linux2 sample code
 * Copyright (C) 2002 Ronald Bultje <rbultje@xxxxxxxxxxxxxxxxxxx>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * FAQ for the innocent:
 *  1) compile: gcc [-DHAVE_SDL $(sdl-config --cflags --libs)] -o test test.c
 *  2) run: ./test
 *  3) quit: ctrl-C
 *  4) help: ./test -h
 */

#include <stdio.h>
#include <sys/time.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>

#ifdef HAVE_SDL
#include <SDL.h>
#endif

/* On some systems MAP_FAILED seems to be missing */
#ifndef MAP_FAILED
#define MAP_FAILED ( (caddr_t) -1 )
#endif

/* we use global variables because we don't care */
char *device = "/dev/video";
int video_fd = -1;
int stop = 0;

#ifdef HAVE_SDL
SDL_Surface *screen;
SDL_Rect dimensions;
SDL_Overlay *yuv_overlay;
#endif

/**
 * enumeration stuff
 **/

static int
enumerate_inputs (void)
{
	int n;
	struct v4l2_input input;

	printf("Supported inputs:\n");

	/* and now, the inputs */
	for (n=0;;n++) {
		input.index = n;
		if (ioctl(video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
			if (errno == EINVAL)
				break; /* end of enumeration */
			else {
				fprintf(stderr,
					"Failed to get no. %d in input enumeration for %s: %s\n",
					n, device, strerror(errno));
				return 0;
			}
		}

		printf(" input %d: %s (type=%s) - status=0x%x\n",
			n, input.name,
			input.type==V4L2_INPUT_TYPE_TUNER?"tuner":"camera",
			input.status);
		if (input.type == V4L2_INPUT_TYPE_TUNER)
			printf(" Associated tuner: %d\n",
				input.tuner);
		if (input.audioset)
			printf(" Associated audioset: 0x%x\n",
				input.audioset);
	}

	printf("\n");

	return 1;
}

static int
enumerate_outputs (void)
{
	int n;
	struct v4l2_output output;

	printf("Supported outputs:\n");

	/* outputs */
	for (n=0;;n++) {
		output.index = n;
		if (ioctl(video_fd, VIDIOC_ENUMOUTPUT, &output) < 0) {
			if (errno == EINVAL)
				break; /* end of enumeration */
			else {
				fprintf(stderr,
					"Failed to get no. %d in output enumeration for %s: %s\n",
					n, device, strerror(errno));
				return 0;
			}
		}

		printf(" output %d: %s (type=%s)\n",
			n, output.name,
			output.type==V4L2_OUTPUT_TYPE_MODULATOR?"modulator":"analog");
		if (output.type == V4L2_OUTPUT_TYPE_MODULATOR)
			printf("  Associated modulator: %d\n",
				output.modulator);
		if (output.audioset)
			printf("  Associated audioset: 0x%x\n",
				output.audioset);
	}

	printf("\n");

	return 1;
}

static int
enumerate_norms (void)
{
	int n;
	struct v4l2_standard standard;

	printf("Supported norms:\n");

	/* norms... */
	for (n=0;;n++) {
		standard.index = n;
		if (ioctl(video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
			if (errno == EINVAL)
				break; /* end of enumeration */
			else {
				fprintf(stderr,
					"Failed to get no. %d in norm enumeration for %s: %s\n",
					n, device, strerror(errno));
				return 0;
			}
		}

		printf(" norm %d: %s (0x%llx) - %lf fps\n",
			n, standard.name, standard.id,
			(standard.frameperiod.denominator!=0)?((double)standard.frameperiod.numerator / standard.frameperiod.denominator):0.);
	}

	printf("\n");

	return 1;
}

static int
enumerate_controls (void)
{
	int n;
	struct v4l2_queryctrl control;
	struct v4l2_querymenu menu;

	printf("Supported controls:\n");

	/* and lastly, controls+menus (if appropriate) */
	for (n=V4L2_CID_BASE;;n++) {
		/* also go over the privates */
		if (n == V4L2_CID_LASTP1)
			n = V4L2_CID_PRIVATE_BASE;

		control.id = n;
		if (ioctl(video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
			if (errno == EINVAL) {
				if (n < V4L2_CID_PRIVATE_BASE)
					continue;
				else
					break;
			} else {
				fprintf(stderr,
					"Failed to get no. %d in control enumeration for %s: %s\n",
					n, device, strerror(errno));
				return 0;
			}
		}
		if (control.flags & V4L2_CTRL_FLAG_DISABLED)
			continue;

		printf(" control %d: %s (type=%d)\n",
			n, control.name, control.type);

		if (control.type == V4L2_CTRL_TYPE_MENU) {
			int i;

			printf("Menu items for this control:\n");

			menu.id = n;
			for (i=0;;i++) {
				menu.index = i;
				if (ioctl(video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
					if (errno == EINVAL)
						break; /* end of enumeration */
					else {
						fprintf(stderr,
							"Failed to get no. %d in menu %d enumeration for %s: %s\n",
							i, n, device, strerror(errno));
						return 0;
					}
				}

				printf("  menu item %d: %s\n",
					i, menu.name);
			}
		}
	}

	printf("\n");

	return 1;
}

static int
enumerate_formats (__u32 *pixelformat)
{
	int n, type;
	struct v4l2_fmtdesc format;

	/* format enumeration */
	for (type=V4L2_BUF_TYPE_VIDEO_CAPTURE;type<=V4L2_BUF_TYPE_VIDEO_OVERLAY;type++) {

		switch (type) {
			case V4L2_BUF_TYPE_VIDEO_CAPTURE:
				if (!pixelformat) {
					printf("Supported capture formats:\n");
				}
				break;
			case V4L2_BUF_TYPE_VIDEO_OUTPUT:
				printf("Supported playback formats:\n");
				break;
			case V4L2_BUF_TYPE_VIDEO_OVERLAY:
				printf("Supported overlay formats:\n");
				break;
		}

		for (n=0;;n++) {
			format.index = n;
			format.type = type;
			if (ioctl(video_fd, VIDIOC_ENUM_FMT, &format) < 0) {
				if (errno == EINVAL)
					break; /* end of enumeration */
				else {
					fprintf(stderr,
						"Failed to get no. %d in pixelformat enumeration for %s: %s\n",
						n, device, strerror(errno));
					return 0;
				}
			}

			if (!pixelformat) {
				printf(" format %d: %s (0x%x)%s\n",
					n, format.description, format.pixelformat,
					format.flags & V4L2_FMT_FLAG_COMPRESSED ? " - compressed":"");
			} else {
				switch (format.pixelformat) {
					case V4L2_PIX_FMT_YUYV:
					case V4L2_PIX_FMT_YUV420:
						*pixelformat = format.pixelformat;
						return 1;
				}
			}
		}

		if (pixelformat) {
			fprintf(stderr,
				"No pixelformat found to capture video...\n");
			return 0;
		}

		printf("\n");
	}

	return 1;
}

/**
 * setting properties
 **/

static int
set_input (int input)
{
	if (ioctl(video_fd, VIDIOC_S_INPUT, &input) < 0) {
		fprintf(stderr,
			"Failed to set input %d on device %s: %s\n",
			input, device, strerror(errno));
		return 0;
	}

	return 1;
}

static int
set_norm (int norm)
{
	struct v4l2_standard standard;

	/* norms... */
	standard.index = norm;
	if (ioctl(video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
		fprintf(stderr,
			"Failed to get no. %d in norm enumeration for %s: %s\n",
			norm, device, strerror(errno));
		return 0;
	}

	if (ioctl(video_fd, VIDIOC_S_STD, &standard.id) < 0) {
		fprintf(stderr,
			"Failed to set norm %s (%d - %llu) for device %s: %s\n",
			standard.name, norm, standard.id, device, strerror(errno));
		return 0;
	}

	return 1;
}

#ifdef HAVE_SDL
static int
setup_sdl (__u32 pix_fmt,
           int   width,
           int   height)
{
	unsigned long sdl_fmt;

	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
		fprintf(stderr,
			"SDL Failed to initialise...");
		return 0;
	}

	switch (pix_fmt) {
		case V4L2_PIX_FMT_YUV420:
			sdl_fmt = SDL_IYUV_OVERLAY;
			break;
		case V4L2_PIX_FMT_YUYV:
			sdl_fmt = SDL_YUY2_OVERLAY;
			break;
		default:
			fprintf(stderr,
				"Unsupported SDL pix fmt 0x%x\n",
				pix_fmt);
			return 0;
	}

	screen = SDL_SetVideoMode(width, height, 0, SDL_HWSURFACE);
	if (!screen) {
		fprintf(stderr,
			"SDL output screen error: %s",
			SDL_GetError());
		return 0;
	}

	yuv_overlay = SDL_CreateYUVOverlay(width, height, sdl_fmt, screen);
	if (!yuv_overlay) {
		fprintf(stderr,
			"SDL: Couldn't create SDL_yuv_overlay: %s\n",
			SDL_GetError());
		return 0;
	}

	dimensions.x = dimensions.y = 0;
	dimensions.w = width;
	dimensions.h = height;

	return 1;
}
#endif

static int
set_format (__u32 pix_fmt,
            int   width,
            int   height,
            int  *sizeimage)
{
	struct v4l2_format format;

	memset(&format, 0, sizeof(struct v4l2_format));
	format.fmt.pix.width = width;
	format.fmt.pix.height = height;
	format.fmt.pix.pixelformat = pix_fmt;
	format.fmt.pix.field = V4L2_FIELD_ANY;
	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	if (ioctl(video_fd, VIDIOC_S_FMT, &format) < 0) {
		fprintf(stderr,
			"Failed to set pixel format to 0x%x @ %dx%d for device %s: %s\n",
			pix_fmt, width, height, device, strerror(errno));
		return 0;
	}

	*sizeimage = format.fmt.pix.sizeimage;

#ifdef HAVE_SDL
	/* setup SDL */
	return setup_sdl(pix_fmt, width, height);
#else
	return 1;
#endif
}

/**
 * capture
 **/

static void
got_signal (int signal)
{
	stop = 1;
}

static int
capture (int   width,
         int   height,
         __u32 pix_fmt,
         int   sizeimage)
{
	int n;
	struct v4l2_requestbuffers breq;
	void **buffer;
	struct v4l2_buffer bufinfo;

	breq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl(video_fd, VIDIOC_REQBUFS, &breq) < 0) {
		fprintf(stderr,
			"Error requesting buffers for %s: %s\n",
			device, strerror(errno));
		return 0;
	}

	buffer = (void **) malloc(sizeof(void *) * breq.count);
	for (n=0;n<breq.count;n++) {
		buffer[n] = mmap(0, sizeimage, PROT_READ|PROT_WRITE,
						MAP_SHARED, video_fd, sizeimage * n);
		if (buffer[n] == MAP_FAILED) {
			fprintf(stderr,
				"Failed to map buffer %d on device %s: %s\n",
				n, device, strerror(errno));
			return 0;
		}
	}

	printf("Received %d buffers of size %d kB\n",
		breq.count, sizeimage >> 10);

	/* prepare */
	bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	for (n=0;n<breq.count;n++) {
		bufinfo.index = n;
		if (ioctl(video_fd, VIDIOC_QBUF, &bufinfo) < 0) {
			fprintf(stderr,
				"Error queueing buffer %d on device %s: %s\n",
				n, device, strerror(errno));
			return 0;
		}
	}
	n = 1;
	if (ioctl(video_fd, VIDIOC_STREAMON, &n) < 0) {
		fprintf(stderr,
			"Error starting streaming capture for %s: %s\n",
			device, strerror(errno));
		return 0;
	}

	/* capture loop */
	signal(SIGINT, got_signal);
	for (n=0;!stop;n++) {
		bufinfo.index = n % breq.count;
		if (ioctl(video_fd, VIDIOC_DQBUF, &bufinfo) < 0) {
			fprintf(stderr,
				"Error dequeueing buffer %d on device %s: %s\n",
				n, device, strerror(errno));
			return 0;
		}

#ifdef HAVE_SDL
		/* lock */
		if (SDL_MUSTLOCK(screen))  {
			if (SDL_LockSurface(screen) < 0) {
				fprintf(stderr,
					"Error locking output screen: %s\n",
					SDL_GetError());
				return 0;
			}
		}
		if (SDL_LockYUVOverlay(yuv_overlay) < 0) {
			fprintf(stderr,
				"Error locking yuv overlay: %s\n",
				SDL_GetError());
			return 0;
		}

		switch (pix_fmt) {
			case V4L2_PIX_FMT_YUYV:
				memcpy(yuv_overlay->pixels[0],
					buffer[n % breq.count],
					sizeimage);
				break;
			case V4L2_PIX_FMT_YUV420:
				memcpy(yuv_overlay->pixels[0],
					buffer[n % breq.count],
					width*height);
				memcpy(yuv_overlay->pixels[1],
					buffer[n % breq.count] + (width*height),
					width*height/4);
				memcpy(yuv_overlay->pixels[2],
					yuv_overlay->pixels[1] + (width*height/4),
					width*height/4);
				break;
			default:
				fprintf(stderr,
					"PANIC! What the hell are you doing here!\n");
				exit(1000);
		}

		/* unlock */
		if (SDL_MUSTLOCK(screen))  {
			SDL_UnlockSurface(screen);
		}
		SDL_UnlockYUVOverlay(yuv_overlay);

		/* update */
		SDL_DisplayYUVOverlay(yuv_overlay, &dimensions);
#endif
		printf(".");
		fflush(stdout);

		bufinfo.index = n % breq.count;
		if (ioctl(video_fd, VIDIOC_QBUF, &bufinfo) < 0) {
			fprintf(stderr,
				"Error requeueing buffer %d on device %s: %s\n",
				n, device, strerror(errno));
			return 0;
		}
	}

	/* shut down */
	printf("\nShutting down\n");
	n = 0;
	if (ioctl(video_fd, VIDIOC_STREAMOFF, &n) < 0) {
		fprintf(stderr,
			"Error stopping streaming capture for %s: %s\n",
			device, strerror(errno));
		return 0;
	}

	for (n=0;n<breq.count;n++) {
		munmap(buffer[n], sizeimage);
	}
	free(buffer);

	return 1;
}

/**
 * close/open stuff
 **/

static int
open_device (void)
{
	struct v4l2_capability vcap;

	if ((video_fd = open(device, O_RDWR)) <= 0) {
		fprintf(stderr,
			"Failed to open %s: %s\n",
			device, strerror(errno));
		return 0;
	}

	if (ioctl(video_fd, VIDIOC_QUERYCAP, &vcap) < 0) {
		fprintf(stderr,
			"Error getting %s capabilities: %s\n",
			device, strerror(errno));
		return 0;
	}

	printf("Device %s on bus %s (driver %s version %d.%d.%d) opened successfully\n",
		vcap.card, vcap.bus_info, vcap.driver,
		(vcap.version>>16)&0xff, (vcap.version>>8)&0xff, vcap.version&0xff);
	printf("\n");

	printf("Capabilities:\n"
		" overlay: %s, capture: %s, playback: %s\n"
		" audio: %s, tuner: %s\n"
		" streaming I/O: %s, async I/O: %s, read()/write() I/O: %s\n",
		vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY ? "yes":"no",
		vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE ? "yes":"no",
		vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT  ? "yes":"no",
		vcap.capabilities & V4L2_CAP_TUNER         ? "yes":"no",
		vcap.capabilities & V4L2_CAP_AUDIO         ? "yes":"no",
		vcap.capabilities & V4L2_CAP_STREAMING     ? "yes":"no",
		vcap.capabilities & V4L2_CAP_ASYNCIO       ? "yes":"no",
		vcap.capabilities & V4L2_CAP_READWRITE     ? "yes":"no");
	printf("\n");

	return 1;
}

static int
close_device (void)
{
	close(video_fd);
	video_fd = -1;

	return 1;
}

/**
 * let's call this 'C boiler plate code' from now on
 **/

int
main (int   argc,
      char *argv[])
{
	int input = -1, norm = -1, width = -1, height = -1, n, sizeimage;
	__u32 format = 0;

	while ((n = getopt(argc,argv,"hi:n:s:")) != EOF) {
		switch (n) {
			case 'i':
				input = atoi(optarg);
				break;
			case 'n':
				norm = atoi(optarg);
				break;
			case 's':
				sscanf(optarg, "%dx%d", &width, &height);
				break;
			case 'h':
			default:
				fprintf(stderr,
					"Usage: %s [-i input] [-n norm] [-s widthxheight] [device]\n",
					argv[0]);
				return 1;
		}
	}

	if (optind != argc) {
		device = argv[optind];
	}

	if (!open_device()) {
		return 1;
	}

	if (width <= 0 || height <= 0) {
		if (!enumerate_inputs()   ||
		    !enumerate_outputs()  ||
		    !enumerate_norms()    ||
		    !enumerate_controls() ||
		    !enumerate_formats(NULL)) {
			goto close;
		}
	} else {
		if (input != -1) {
			if (!set_input(input)) {
				goto close;
			}
		}
		if (norm != -1) {
			if (!set_norm(norm)) {
				goto close;
			}
		}
		if (!enumerate_formats(&format)                    ||
		    !set_format(format, width, height, &sizeimage) ||
		    !capture(width, height, format, sizeimage)) {
			goto close;
		}
	}

close:
	if (!close_device()) {
		return 1;
	}

	return 0;
}

[Index of Archives]     [Linux DVB]     [Video Disk Recorder]     [Asterisk]     [Photo]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Free Photo Albums]     [Fedora Users]     [Fedora Women]     [ALSA Users]     [ALSA Devel]     [Linux USB]

Powered by Linux