video4linux2编程实例

来源:Linuxeden 作者:c-aries
  
废话不多说,看程序运行效果

video4linux2编程实例

以上是笔者使用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'对应一个视频帧的像素。

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 原文链接

好文,顶一下
(35)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量