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