废话不多说,看程序运行效果
以上是笔者使用video4linux2编程接口,获得笔记本摄像头影像后,用SDL显示在X Window下的情景
关键点有二:
其一,从video4linux2的编程接口笔者了解到其笔记本摄像头支持YUYV视频帧格式,显示在SDL上需要将YUYV格式转换成SDL支持的显示格式RGB。
其二,要学会使用video4linux2获得摄像头YUYV数据流的程序框架。
YUV到RGB颜色空间的转换,可阅读video4linux2的文献,网址如下:
http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/ch02s02.html
YUYV数据流每个字节的含义,可阅读video4linux2的文献,网址如下:
http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/re09.html
举个例子,4x4像素的视频帧,内存中每个字节的存放情况如下:
start + 0: Y'[00] Cb[00] Y'[01] Cr[00] Y'[02] Cb[01] Y'[03] Cr[01] start + 8: Y'[10] Cb[10] Y'[11] Cr[10] Y'[12] Cb[11] Y'[13] Cr[11] start + 16: Y'[20] Cb[20] Y'[21] Cr[20] Y'[22] Cb[21] Y'[23] Cr[21] start + 24: Y'[30] Cb[30] Y'[31] Cr[30] Y'[32] Cb[31] Y'[33] Cr[31] |
video4linux2编程框架的中文教程,网上一搜一大把,这里不详细介绍。
以下给出上图中运行的程序的所有源代码。可作为video4linux2编程快速入门的学习资料。
-------------------------------------------------------------------- [swc目录结构] -------------------------------------------------------------------- . |-- Makefile |-- book-2010-05-21 |-- opt.c |-- opt.h |-- screen.c |-- screen.h |-- swc.c |-- video.c `-- video.h 0 directories, 9 files -------------------------------------------------------------------- [./Makefile] -------------------------------------------------------------------- CC = gcc CFLAGS = -Wall -Werror -Wcast-align -g LDFLAGS = SWCOBJECT = swc.o opt.o video.o screen.o all: swc swc: $(SWCOBJECT) $(CC) $(LDFLAGS) `pkg-config --libs sdl` $(SWCOBJECT) -o $@ swc.o: swc.c $(CC) $(CFLAGS) `pkg-config --cflags sdl` -c $< opt.o: opt.c opt.h $(CC) $(CFLAGS) -c $< video.o: video.c video.h $(CC) $(CFLAGS) -c $< screen.o: screen.c screen.h $(CC) $(CFLAGS) `pkg-config --cflags sdl` -c $< clean: rm -f *.o *~ swc .PHONY: all clean -------------------------------------------------------------------- [./screen.h] -------------------------------------------------------------------- #ifndef SCREEN_H #define SCREEN_H #include <SDL.h> struct rgb_surface { SDL_Surface *surface; unsigned int rmask; unsigned int gmask; unsigned int bmask; unsigned int amask; int width; int height; int bpp; int pitch; unsigned char *pixels; int pixels_num; }; struct screen { SDL_Surface *display; SDL_Event event; int width; int height; int bpp; int running; struct rgb_surface rgb; }; void screen_init(); void screen_quit(); void screen_mainloop(); #endif -------------------------------------------------------------------- [./opt.h] -------------------------------------------------------------------- #ifndef OPT_H #define OPT_H struct options { int verbose; int width; int height; }; void options_init(); void options_deal(int argc, char *argv[]); #endif -------------------------------------------------------------------- [./screen.c] -------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #include "screen.h" #include "opt.h" #include "video.h" extern struct options opt; extern struct video video; struct screen screen; static void sdl_init(); static void create_rgb_surface(); static void update_rgb_surface(int index); static void update_rgb_pixels(const void *start); static void yuv2rgb(unsigned char Y, unsigned char Cb, unsigned char Cr, int *ER, int *EG, int *EB); static int clamp(double x); static void sdl_init() { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTTHREAD) < 0) { perror("SDL_Init"); exit(EXIT_FAILURE); } SDL_WM_SetCaption("Simple WebCam", NULL); atexit(SDL_Quit); } static void create_rgb_surface() { screen.rgb.rmask = 0x000000ff; screen.rgb.gmask = 0x0000ff00; screen.rgb.bmask = 0x00ff0000; screen.rgb.amask = 0xff000000; screen.rgb.width = screen.width; screen.rgb.height = screen.height; screen.rgb.bpp = screen.bpp; screen.rgb.pitch = screen.width * 4; screen.rgb.pixels_num = screen.width * screen.height * 4; screen.rgb.pixels = (unsigned char *)malloc(screen.rgb.pixels_num); memset(screen.rgb.pixels, 0, screen.rgb.pixels_num); screen.rgb.surface = SDL_CreateRGBSurfaceFrom(screen.rgb.pixels, screen.rgb.width, screen.rgb.height, screen.rgb.bpp, screen.rgb.pitch, screen.rgb.rmask, screen.rgb.gmask, screen.rgb.bmask, screen.rgb.amask); } static void update_rgb_surface(int index) { update_rgb_pixels(video.buffer.buf[index].start); SDL_BlitSurface(screen.rgb.surface, NULL, screen.display, NULL); SDL_Flip(screen.display); } static void update_rgb_pixels(const void *start) { unsigned char *data = (unsigned char *)start; unsigned char *pixels = screen.rgb.pixels; int width = screen.rgb.width; int height = screen.rgb.height; unsigned char Y, Cr, Cb; int r, g, b; int x, y; int p1, p2, p3, p4; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { p1 = y * width * 2 + x * 2; Y = data[p1]; if (x % 2 == 0) { p2 = y * width * 2 + (x * 2 + 1); p3 = y * width * 2 + (x * 2 + 3); } else { p2 = y * width * 2 + (x * 2 - 1); p3 = y * width * 2 + (x * 2 + 1); } Cb = data[p2]; Cr = data[p3]; yuv2rgb(Y, Cb, Cr, &r, &g, &b); p4 = y * width * 4 + x * 4; pixels[p4] = r; pixels[p4 + 1] = g; pixels[p4 + 2] = b; pixels[p4 + 3] = 255; } } } static void yuv2rgb(unsigned char Y, unsigned char Cb, unsigned char Cr, int *ER, int *EG, int *EB) { double y1, pb, pr, r, g, b; y1 = (255 / 219.0) * (Y - 16); pb = (255 / 224.0) * (Cb - 128); pr = (255 / 224.0) * (Cr - 128); r = 1.0 * y1 + 0 * pb + 1.402 * pr; g = 1.0 * y1 - 0.344 * pb - 0.714 * pr; b = 1.0 * y1 + 1.722 * pb + 0 * pr; /* 用GDB调试了这么久终于将BUG找出来了:), 是v4l2的文档有问题 */ /* 不应该为clamp(r * 255) */ *ER = clamp(r); *EG = clamp(g); *EB = clamp(b); } static int clamp(double x) { int r = x; if (r < 0) return 0; else if (r > 255) return 255; else return r; } void screen_init() { screen.width = opt.width; screen.height = opt.height; screen.bpp = 32; screen.running = 1; screen.display = SDL_SetVideoMode(screen.width, screen.height, screen.bpp, SDL_SWSURFACE | SDL_DOUBLEBUF); if (screen.display == NULL) { perror("SDL_SetVideoMode"); exit(EXIT_FAILURE); } sdl_init(); create_rgb_surface(); } void screen_quit() { SDL_FreeSurface(screen.display); SDL_FreeSurface(screen.rgb.surface); free(screen.rgb.pixels); SDL_Quit(); } void screen_mainloop() { int i; for (i = 0; screen.running && i <= video.buffer.req.count; i++) { if (i == video.buffer.req.count) { i = 0; } buffer_dequeue(i); update_rgb_surface(i); if (SDL_PollEvent(&screen.event) == 1) { switch (screen.event.type) { case SDL_KEYDOWN: switch (screen.event.key.keysym.sym) { case SDLK_q: puts("bye"); screen.running = 0; break; default: break; } break; case SDL_QUIT: screen.running = 0; break; default: break; } } buffer_enqueue(i); } } -------------------------------------------------------------------- [./video.h] -------------------------------------------------------------------- #ifndef VIDEO_H #define VIDEO_H #include <linux/videodev2.h> struct buffer { struct v4l2_requestbuffers req; /* 请求 */ struct v4l2_buffer query; /* 获取 */ struct { /* 缓冲 */ void *start; size_t length; } *buf; }; struct video { int fd; struct v4l2_format format; /* 视频帧格式 */ struct buffer buffer; /* 视频缓冲 */ }; void video_init(); void video_quit(); void buffer_enqueue(int index); void buffer_dequeue(int index); #endif -------------------------------------------------------------------- [./video.c] -------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <assert.h> #include "video.h" #include "opt.h" #define BUFFER_NUM 5 static int stream_flag = V4L2_BUF_TYPE_VIDEO_CAPTURE; extern struct options opt; struct video video; static void video_open(); static void video_close(); static void video_set_format(); static void video_streamon(); static void video_streamoff(); static void buffer_init(); static void buffer_free(); static void buffer_request(); static void buffer_mmap(int index); static void video_open() { int i, fd; char device[13]; for (i = 0; i < 99; i++) { sprintf(device, "%s%d", "/dev/video", i); fd = open(device, O_RDWR); if (fd != -1) { if (opt.verbose) { printf("open %s success\n", device); } break; } } if (i == 100) { perror("video open fail"); exit(EXIT_FAILURE); } video.fd = fd; } static void video_close() { close(video.fd); } static void video_set_format() { memset(&video.format, 0, sizeof(video.format)); video.format.type = stream_flag; video.format.fmt.pix.width = opt.width; video.format.fmt.pix.height = opt.height; video.format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; if (ioctl(video.fd, VIDIOC_S_FMT, &video.format) == -1) { perror("VIDIOC_S_FORMAT"); exit(EXIT_FAILURE); } } static void video_streamon() { if (ioctl(video.fd, VIDIOC_STREAMON, &stream_flag) == -1) { if (errno == EINVAL) { perror("streaming i/o is not support"); } else { perror("VIDIOC_STREAMON"); } exit(EXIT_FAILURE); } } static void video_streamoff() { if (ioctl(video.fd, VIDIOC_STREAMOFF, &stream_flag) == -1) { if (errno == EINVAL) { perror("streaming i/o is not support"); } else { perror("VIDIOC_STREAMOFF"); } exit(EXIT_FAILURE); } } static void buffer_init() { int i; buffer_request(); for (i = 0; i < video.buffer.req.count; i++) { buffer_mmap(i); buffer_enqueue(i); } } static void buffer_free() { int i; for (i = 0; i < video.buffer.req.count; i++) { munmap(video.buffer.buf[i].start, video.buffer.buf[i].length); } free(video.buffer.buf); } static void buffer_request() { memset(&video.buffer.req, 0, sizeof(video.buffer.req)); video.buffer.req.type = stream_flag; video.buffer.req.memory = V4L2_MEMORY_MMAP; video.buffer.req.count = BUFFER_NUM; /* 设置视频帧缓冲规格 */ if (ioctl(video.fd, VIDIOC_REQBUFS, &video.buffer.req) == -1) { if (errno == EINVAL) { perror("video capturing or mmap-streaming is not support"); } else { perror("VIDIOC_REQBUFS"); } exit(EXIT_FAILURE); } if (video.buffer.req.count < BUFFER_NUM) { perror("no enough buffer"); exit(EXIT_FAILURE); } video.buffer.buf = calloc(video.buffer.req.count, sizeof(*video.buffer.buf)); assert(video.buffer.buf != NULL); } static void buffer_mmap(int index) { memset(&video.buffer.query, 0, sizeof(video.buffer.query)); video.buffer.query.type = video.buffer.req.type; video.buffer.query.memory = V4L2_MEMORY_MMAP; video.buffer.query.index = index; /* 视频帧缓冲映射 */ if (ioctl(video.fd, VIDIOC_QUERYBUF, &video.buffer.query) == -1) { perror("VIDIOC_QUERYBUF"); exit(EXIT_FAILURE); } video.buffer.buf[index].length = video.buffer.query.length; video.buffer.buf[index].start = mmap(NULL, video.buffer.query.length, PROT_READ | PROT_WRITE, MAP_SHARED, video.fd, video.buffer.query.m.offset); if (video.buffer.buf[index].start == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } } void video_init() { video_open(); video_set_format(); buffer_init(); video_streamon(); } void video_quit() { video_streamoff(); video_close(); buffer_free(); } void buffer_enqueue(int index) { memset(&video.buffer.query, 0, sizeof(video.buffer.query)); video.buffer.query.type = video.buffer.req.type; video.buffer.query.memory = V4L2_MEMORY_MMAP; video.buffer.query.index = index; /* 视频帧缓冲入队 */ if (ioctl(video.fd, VIDIOC_QBUF, &video.buffer.query) == -1) { perror("VIDIOC_QBUF"); exit(EXIT_FAILURE); } } void buffer_dequeue(int index) { memset(&video.buffer.query, 0, sizeof(video.buffer.query)); video.buffer.query.type = video.buffer.req.type; video.buffer.query.memory = V4L2_MEMORY_MMAP; video.buffer.query.index = index; /* 视频帧缓冲出队 */ if (ioctl(video.fd, VIDIOC_DQBUF, &video.buffer.query) == -1) { perror("VIDIOC_DQBUF"); exit(EXIT_FAILURE); } } -------------------------------------------------------------------- [./swc.c] -------------------------------------------------------------------- #include <stdlib.h> #include "opt.h" #include "video.h" #include "screen.h" int main(int argc, char *argv[]) { options_init(); options_deal(argc, argv); video_init(); screen_init(); screen_mainloop(); screen_quit(); video_quit(); exit(EXIT_SUCCESS); } -------------------------------------------------------------------- [./opt.c] -------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "opt.h" struct options opt; static void show_usage(); static void show_usage() { printf("usage: swc [options]\n\ -h 打印该帮助信息\n\ -v 显示程序内部提示\n"); } void options_init() { opt.verbose = 0; opt.width = 640; opt.height = 480; } void options_deal(int argc, char *argv[]) { int my_opt; while ((my_opt = getopt(argc, argv, "hv")) != -1) { switch(my_opt) { case 'h': show_usage(); break; case 'v': opt.verbose = 1; break; default: show_usage(); exit(EXIT_FAILURE); } } } |
时间:2010-05-21 15:58
来源:Linuxeden
作者:c-aries
原文链接