红薯 video4linux2编程实例

http://www.oschina.net/code/snippet_12_368?from=rss

         使用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]
一个Y'对应一个视频帧的像素。
[swc目录结构]
--------------------------------------------------------------------
.
|-- Makefile
|-- book-2010-05-21
|-- opt.c
|-- opt.h
|-- screen.c
|-- screen.h
|-- swc.c
|-- video.c
`-- video.h
标签: <无>

代码片段(8)[全屏查看所有代码]

1. [代码]Makefile    

01CC = gcc
02CFLAGS = -Wall -Werror -Wcast-align -g
03LDFLAGS =
04 
05SWCOBJECT = swc.o opt.o video.o screen.o
06 
07all: swc
08 
09swc: $(SWCOBJECT)
10    $(CC) $(LDFLAGS) `pkg-config --libs sdl` $(SWCOBJECT) -o $@
11 
12swc.o: swc.c
13    $(CC) $(CFLAGS) `pkg-config --cflags sdl` -c $<
14 
15opt.o: opt.c opt.h
16    $(CC) $(CFLAGS) -c $<
17 
18video.o: video.c video.h
19    $(CC) $(CFLAGS) -c $<
20 
21screen.o: screen.c screen.h
22    $(CC) $(CFLAGS) `pkg-config --cflags sdl` -c $<
23 
24clean:
25    rm -f *.o *~ swc
26 
27.PHONY: all clean

2. [代码]screen.h    

01#ifndef SCREEN_H
02#define SCREEN_H
03 
04#include <SDL.h>
05 
06struct rgb_surface {
07  SDL_Surface *surface;
08  unsigned int rmask;
09  unsigned int gmask;
10  unsigned int bmask;
11  unsigned int amask;
12  int width;
13  int height;
14  int bpp;
15  int pitch;
16  unsigned char *pixels;
17  int pixels_num;
18};
19 
20struct screen {
21  SDL_Surface *display;
22  SDL_Event event;
23  int width;
24  int height;
25  int bpp;
26  int running;
27  struct rgb_surface rgb;
28};
29 
30void screen_init();
31void screen_quit();
32void screen_mainloop();
33 
34#endif

3. [代码]opt.h    

01#ifndef OPT_H
02#define OPT_H
03 
04struct options {
05  int verbose;
06  int width;
07  int height;
08};
09 
10void options_init();
11void options_deal(int argc, char *argv[]);
12 
13#endif

4. [代码]screen.c    

001#include <stdio.h>
002#include <stdlib.h>
003#include <string.h>
004#include "screen.h"
005#include "opt.h"
006#include "video.h"
007 
008extern struct options opt;
009extern struct video video;
010struct screen screen;
011 
012static void sdl_init();
013static void create_rgb_surface();
014static void update_rgb_surface(int index);
015static void update_rgb_pixels(const void *start);
016static void yuv2rgb(unsigned char Y,
017            unsigned char Cb,
018            unsigned char Cr,
019            int *ER,
020            int *EG,
021            int *EB);
022static int clamp(double x);
023 
024static void
025sdl_init()
026{
027  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTTHREAD) < 0) {
028    perror("SDL_Init");
029    exit(EXIT_FAILURE);
030  }
031  SDL_WM_SetCaption("Simple WebCam", NULL);
032  atexit(SDL_Quit);
033}
034 
035static void
036create_rgb_surface()
037{
038  screen.rgb.rmask = 0x000000ff;
039  screen.rgb.gmask = 0x0000ff00;
040  screen.rgb.bmask = 0x00ff0000;
041  screen.rgb.amask = 0xff000000;
042  screen.rgb.width = screen.width;
043  screen.rgb.height = screen.height;
044  screen.rgb.bpp = screen.bpp;
045  screen.rgb.pitch = screen.width * 4;
046  screen.rgb.pixels_num = screen.width * screen.height * 4;
047  screen.rgb.pixels = (unsigned char *)malloc(screen.rgb.pixels_num);
048  memset(screen.rgb.pixels, 0, screen.rgb.pixels_num);
049  screen.rgb.surface = SDL_CreateRGBSurfaceFrom(screen.rgb.pixels,
050                        screen.rgb.width,
051                        screen.rgb.height,
052                        screen.rgb.bpp,
053                        screen.rgb.pitch,
054                        screen.rgb.rmask,
055                        screen.rgb.gmask,
056                        screen.rgb.bmask,
057                        screen.rgb.amask);
058}
059 
060static void
061update_rgb_surface(int index)
062{
063  update_rgb_pixels(video.buffer.buf[index].start);
064  SDL_BlitSurface(screen.rgb.surface, NULL, screen.display, NULL);
065  SDL_Flip(screen.display);
066}
067 
068static void
069update_rgb_pixels(const void *start)
070{
071  unsigned char *data = (unsigned char *)start;
072  unsigned char *pixels = screen.rgb.pixels;
073  int width = screen.rgb.width;
074  int height = screen.rgb.height;
075  unsigned char Y, Cr, Cb;
076  int r, g, b;
077  int x, y;
078  int p1, p2, p3, p4;
079 
080  for (y = 0; y < height; y++) {
081    for (x = 0; x < width; x++) {
082      p1 = y * width * 2 + x * 2;
083      Y = data[p1];
084      if (x % 2 == 0) {
085    p2 = y * width * 2 + (x * 2 + 1);
086    p3 = y * width * 2 + (x * 2 + 3);
087      }
088      else {
089    p2 = y * width * 2 + (x * 2 - 1);
090    p3 = y * width * 2 + (x * 2 + 1);
091      }
092      Cb = data[p2];
093      Cr = data[p3];
094      yuv2rgb(Y, Cb, Cr, &r, &g, &b);
095      p4 = y * width * 4 + x * 4;
096      pixels[p4] = r;
097      pixels[p4 + 1] = g;
098      pixels[p4 + 2] = b;
099      pixels[p4 + 3] = 255;
100    }
101  }
102}
103 
104static void
105yuv2rgb(unsigned char Y,
106    unsigned char Cb,
107    unsigned char Cr,
108    int *ER,
109    int *EG,
110    int *EB)
111{
112  double y1, pb, pr, r, g, b;
113 
114  y1 = (255 / 219.0) * (Y - 16);
115  pb = (255 / 224.0) * (Cb - 128);
116  pr = (255 / 224.0) * (Cr - 128);
117  r = 1.0 * y1 + 0 * pb + 1.402 * pr;
118  g = 1.0 * y1 - 0.344 * pb - 0.714 * pr;
119  b = 1.0 * y1 + 1.722 * pb + 0 * pr;
120/* 用GDB调试了这么久终于将BUG找出来了:), 是v4l2的文档有问题 */
121/* 不应该为clamp(r * 255) */
122  *ER = clamp(r);
123  *EG = clamp(g);
124  *EB = clamp(b);
125}
126 
127static int
128clamp(double x)
129{
130  int r = x;
131  if (r < 0)
132    return 0;
133  else if (r > 255)
134    return 255;
135  else
136    return r;
137}
138 
139void
140screen_init()
141{
142  screen.width = opt.width;
143  screen.height = opt.height;
144  screen.bpp = 32;
145  screen.running = 1;
146  screen.display = SDL_SetVideoMode(screen.width,
147                    screen.height,
148                    screen.bpp,
149                    SDL_SWSURFACE | SDL_DOUBLEBUF);
150  if (screen.display == NULL) {
151    perror("SDL_SetVideoMode");
152    exit(EXIT_FAILURE);
153  }
154  sdl_init();
155  create_rgb_surface();
156}
157 
158void
159screen_quit()
160{
161  SDL_FreeSurface(screen.display);
162  SDL_FreeSurface(screen.rgb.surface);
163  free(screen.rgb.pixels);
164  SDL_Quit();
165}
166 
167void
168screen_mainloop()
169{
170  int i;
171 
172  for (i = 0; screen.running && i <= video.buffer.req.count; i++) {
173    if (i == video.buffer.req.count) {
174      i = 0;
175    }
176    buffer_dequeue(i);
177    update_rgb_surface(i);
178    if (SDL_PollEvent(&screen.event) == 1) {
179      switch (screen.event.type) {
180      case SDL_KEYDOWN:
181    switch (screen.event.key.keysym.sym) {
182    case SDLK_q:
183      puts("bye");
184      screen.running = 0;
185      break;
186    default:
187      break;
188    }
189    break;
190      case SDL_QUIT:
191    screen.running = 0;
192    break;
193      default:
194    break;
195      }
196    }
197    buffer_enqueue(i);
198  }
199}

5. [代码]video.h    

01#ifndef VIDEO_H
02#define VIDEO_H
03 
04#include <linux/videodev2.h>
05 
06struct buffer {
07  struct v4l2_requestbuffers req; // 请求/
08  struct v4l2_buffer query;      // 获取/
09  struct {              // 缓冲/
10    void *start;
11    size_t length;
12  } *buf;
13};
14 
15struct video {
16  int fd;
17  struct v4l2_format format;    // 视频帧格式
18  struct buffer buffer;        // 视频缓冲
19};
20 
21void video_init();
22void video_quit();
23void buffer_enqueue(int index);
24void buffer_dequeue(int index);
25 
26#endif

6. [代码]video.c    

001#include <stdio.h>
002#include <stdlib.h>
003#include <string.h>
004#include <sys/types.h>
005#include <sys/stat.h>
006#include <sys/ioctl.h>
007#include <sys/mman.h>
008#include <fcntl.h>
009#include <unistd.h>
010#include <errno.h>
011#include <assert.h>
012#include "video.h"
013#include "opt.h"
014 
015#define BUFFER_NUM 5
016 
017static int stream_flag = V4L2_BUF_TYPE_VIDEO_CAPTURE;
018extern struct options opt;
019struct video video;
020 
021static void video_open();
022static void video_close();
023static void video_set_format();
024static void video_streamon();
025static void video_streamoff();
026static void buffer_init();
027static void buffer_free();
028static void buffer_request();
029static void buffer_mmap(int index);
030 
031static void
032video_open()
033{
034  int i, fd;
035  char device[13];
036 
037  for (i = 0; i < 99; i++) {
038    sprintf(device, "%s%d", "/dev/video", i);
039    fd = open(device, O_RDWR);
040    if (fd != -1) {
041      if (opt.verbose) {
042    printf("open %s success\n", device);
043      }
044      break;
045    }
046  }
047  if (i == 100) {
048    perror("video open fail");
049    exit(EXIT_FAILURE);
050  }
051  video.fd = fd;
052}
053 
054static void
055video_close()
056{
057  close(video.fd);
058}
059 
060static void
061video_set_format()
062{
063  memset(&video.format, 0, sizeof(video.format));
064  video.format.type = stream_flag;
065  video.format.fmt.pix.width = opt.width;
066  video.format.fmt.pix.height = opt.height;
067  video.format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
068  if (ioctl(video.fd, VIDIOC_S_FMT, &video.format) == -1) {
069    perror("VIDIOC_S_FORMAT");
070    exit(EXIT_FAILURE);
071  }
072}
073 
074static void
075video_streamon()
076{
077  if (ioctl(video.fd, VIDIOC_STREAMON, &stream_flag) == -1) {
078    if (errno == EINVAL) {
079      perror("streaming i/o is not support");
080    }
081    else {
082      perror("VIDIOC_STREAMON");
083    }
084    exit(EXIT_FAILURE);
085  }
086}
087 
088static void
089video_streamoff()
090{
091  if (ioctl(video.fd, VIDIOC_STREAMOFF, &stream_flag) == -1) {
092    if (errno == EINVAL) {
093      perror("streaming i/o is not support");
094    }
095    else {
096      perror("VIDIOC_STREAMOFF");
097    }
098    exit(EXIT_FAILURE);
099  }
100}
101 
102static void
103buffer_init()
104{
105  int i;
106 
107  buffer_request();
108  for (i = 0; i < video.buffer.req.count; i++) {
109    buffer_mmap(i);
110    buffer_enqueue(i);
111  }
112}
113 
114static void
115buffer_free()
116{
117  int i;
118 
119  for (i = 0; i < video.buffer.req.count; i++) {
120    munmap(video.buffer.buf[i].start, video.buffer.buf[i].length);
121  }
122  free(video.buffer.buf);
123}
124 
125static void
126buffer_request()
127{
128 
129  memset(&video.buffer.req, 0, sizeof(video.buffer.req));
130  video.buffer.req.type = stream_flag;
131  video.buffer.req.memory = V4L2_MEMORY_MMAP;
132  video.buffer.req.count = BUFFER_NUM;
133  /* 设置视频帧缓冲规格 */
134  if (ioctl(video.fd, VIDIOC_REQBUFS, &video.buffer.req) == -1) {
135    if (errno == EINVAL) {
136      perror("video capturing or mmap-streaming is not support");
137    }
138    else {
139      perror("VIDIOC_REQBUFS");
140    }
141    exit(EXIT_FAILURE);
142  }
143  if (video.buffer.req.count < BUFFER_NUM) {
144    perror("no enough buffer");
145    exit(EXIT_FAILURE);
146  }
147  video.buffer.buf = calloc(video.buffer.req.count,
148                sizeof(*video.buffer.buf));
149  assert(video.buffer.buf != NULL);
150}
151 
152static void
153buffer_mmap(int index)
154{
155  memset(&video.buffer.query, 0, sizeof(video.buffer.query));
156  video.buffer.query.type = video.buffer.req.type;
157  video.buffer.query.memory = V4L2_MEMORY_MMAP;
158  video.buffer.query.index = index;
159  /* 视频帧缓冲映射 */
160  if (ioctl(video.fd, VIDIOC_QUERYBUF, &video.buffer.query) == -1) {
161    perror("VIDIOC_QUERYBUF");
162    exit(EXIT_FAILURE);
163  }
164  video.buffer.buf[index].length = video.buffer.query.length;
165  video.buffer.buf[index].start = mmap(NULL,
166                       video.buffer.query.length,
167                       PROT_READ | PROT_WRITE,
168                       MAP_SHARED,
169                       video.fd,
170                       video.buffer.query.m.offset);
171  if (video.buffer.buf[index].start == MAP_FAILED) {
172    perror("mmap");
173    exit(EXIT_FAILURE);
174  }
175}
176 
177void
178video_init()
179{
180  video_open();
181  video_set_format();
182  buffer_init();
183  video_streamon();
184}
185 
186void
187video_quit()
188{
189  video_streamoff();
190  video_close();
191  buffer_free();
192}
193 
194void
195buffer_enqueue(int index)
196{
197    memset(&video.buffer.query, 0, sizeof(video.buffer.query));
198    video.buffer.query.type = video.buffer.req.type;
199    video.buffer.query.memory = V4L2_MEMORY_MMAP;
200    video.buffer.query.index = index;
201    /* 视频帧缓冲入队 */
202    if (ioctl(video.fd, VIDIOC_QBUF, &video.buffer.query) == -1) {
203      perror("VIDIOC_QBUF");
204      exit(EXIT_FAILURE);
205    }
206}
207 
208void
209buffer_dequeue(int index)
210{
211    memset(&video.buffer.query, 0, sizeof(video.buffer.query));
212    video.buffer.query.type = video.buffer.req.type;
213    video.buffer.query.memory = V4L2_MEMORY_MMAP;
214    video.buffer.query.index = index;
215    /* 视频帧缓冲出队 */
216    if (ioctl(video.fd, VIDIOC_DQBUF, &video.buffer.query) == -1) {
217      perror("VIDIOC_DQBUF");
218      exit(EXIT_FAILURE);
219    }
220}

7. [代码]swc.c    

01#include <stdlib.h>
02#include "opt.h"
03#include "video.h"
04#include "screen.h"
05 
06int
07main(int argc, char *argv[])
08{
09  options_init();
10  options_deal(argc, argv);
11  video_init();
12  screen_init();
13  screen_mainloop();
14  screen_quit();
15  video_quit();
16  exit(EXIT_SUCCESS);
17}

8. [代码]opt.c    

01#include <stdio.h>
02#include <stdlib.h>
03#include <unistd.h>
04#include "opt.h"
05 
06struct options opt;
07 
08static void show_usage();
09 
10static void
11show_usage()
12{
13  printf("usage: swc [options]\n\
14-h 打印该帮助信息\n\
15-v 显示程序内部提示\n");
16}
17 
18void
19options_init()
20{
21  opt.verbose = 0;
22  opt.width = 640;
23  opt.height = 480;
24}
25 
26void
27options_deal(int argc, char *argv[])
28{
29  int my_opt;
30 
31  while ((my_opt = getopt(argc, argv, "hv")) != -1) {
32    switch(my_opt) {
33    case 'h':
34      show_usage();
35      break;
36    case 'v':
37      opt.verbose = 1;
38      break;
39    default:
40      show_usage();
41      exit(EXIT_FAILURE);
42    }
43  }
44}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值