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 <[EMAIL PROTECTED]>
/* video4linux2 sample code
* Copyright (C) 2002 Ronald Bultje <[EMAIL PROTECTED]>
*
* 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;
}