【原创】IP摄像头技术纵览(三)---图像数据在帧缓存设备(framebuffer)上的显示

【原创】IP摄像头技术纵览(三)—图像数据在帧缓存设备(framebuffer)上的显示

本文属于《IP摄像头技术纵览》系列文章之一:

Author: chad

Mail: [email protected]

本文可以自由转载,但转载请务必注明出处以及本声明信息。

提起Linux的窗口系统,我们第一个想到的就是X-Window。X-Window是Unix/Linux上的图形系统,它是通过X-Server来控制硬件的。但有一些Linux的发行版在引导的时候就会在屏幕上出现图形,这时的图形是不可能由X来完成的,那是什么机制呢?答案就是FrameBuffer。

FrameBuffer不是一个图形系统,更不是窗口系统。它比X要低级,简单来说FrameBuffer就是一种虚拟显存。这种机制是把屏幕上的每个点映射成一段线性内存空间,程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。X的高度可移植性就是来自于这种机制。

1、FrameBuffer的原理

FrameBuffer 是出现在 2.2以上内核当中的一种驱动程序接口。帧缓冲(frame buffer)是Linux视频系统的核心概念。因为视频适配器可能基于不同的硬件体系架构,较高内核层和应用程序的实现可能会因视频卡的不同而不同,这会导致在使用不同视频卡的时需要采用不同的方案。随之而来的低可移植性和冗余的代码需要大量的投入和维护开销。帧缓冲的概念解决了这个问题,它进行了一般化的抽象并规定编程接口,从而开发人员可以以与平台无关的方式编写应用层和较高内核层程序。因此,内核的帧缓冲接口允许应用程序与底层图形硬件的变化无关,如果应用和显示器驱动程序遵循帧缓冲接口,应用程序不用改变就可以在不同类型的视频硬件上运行。

在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。Framebuffer本身不具备任何运算数据的能力,中间不会对数据做处理。FrameBuffer 设备还提供了若干 ioctl 命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩色模式下的调色板信息等等。

2、FrameBuffer在Linux中的实现和机制

Framebuffer设备驱动程序架构如下图,用户空间应用程序主要通过read、write、mmap、ioctl这四个接口与Framebuffer设备打交道。但是一般都是采用mmap方式直接操作内存,所以下文也主要介绍mmap方式。

(1)、Framebuffer主要结构体

用户空间可见的FrameBuffer设备的主要结构体几乎都是在fb.h(可以直接点击该链接查看fb.h文件)这个中文件定义的。这些结构包括:

1)fb_var_screeninfo主要是记录用户可以修改的控制器可变参数:

struct fb_var_screeninfo {
    /*可见解析度*/
    __u32 xres;
    __u32 yres;
    /*虚拟解析度*/
    __u32 xres_virtual;
    __u32 yres_virtual;
    /*虚拟到可见之间的偏移*/
    __u32 xoffset;
    __u32 yoffset;
    __u32 bits_per_pixel;       /*每像素的位数,BPP*/
    __u32 grayscale;        /*非0时指灰度*/
    /*fb缓存的R\G\B位域*/
    struct fb_bitfield red;
    struct fb_bitfield green;
    struct fb_bitfield blue;
    struct fb_bitfield transp;  /*透明度*/
    __u32 nonstd;           /*!=0非标准像素格式*/
    __u32 activate;
    __u32 height;           /*高度*/
    __u32 width;            /*宽度*/
    __u32 accel_flags;
    /*除pixclock本身外,其他都以像素时钟为单位*/
    __u32 pixclock;         /*像素时钟(皮秒)*/
    __u32 left_margin;      /*行切换:从同步到绘图之间的延迟*/
    __u32 right_margin;     /*行切换:从绘图到同步之间的延迟*/
    __u32 upper_margin;     /*帧切换:从同步到绘图之间的延迟*/
    __u32 lower_margin;     /*帧切换:从绘图到同步之间的延迟*/
    __u32 hsync_len;        /*水平同步的长度*/
    __u32 vsync_len;        /*垂直同步的长度*/
    __u32 sync;
    __u32 vmode;
    __u32 rotate;           /*顺时针旋转的角度*/
    __u32 reserved[5];      /*保留*/
};  

2) fb_fix_screeninfon

这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。

struct fb_fix_screeninfo {
    char id[16];            /*字符串形式的标识符*/
    unsigned long smem_start;   /*fb缓冲内存的开始位置(物理地址)*/
    __u32 smem_len;         /*fb缓冲的长度*/
    __u32 type;              /*FB_TYPE_* */
    __u32 type_aux;         /*Interleave*/
    __u32 visual;           /*FB_VISUAL_* */
    __u16 xpanstep;         /*如果没有硬件panning,赋0*/
    __u16 ypanstep;
    __u16 ywrapstep;
    __u32 line_length;      /*一行的字节数*/
    unsigned long mmio_start;   /*内存映射I/O的开始位置*/
    __u32 mmio_len;         /*内存映射I/O的长度*/
    __u32 accel;
    __u16 reserved[3];      /*保留*/
};  

fb_var_screeninfo与fb_fix_screeninfo的关系见下图:

3、FrameBuffer的应用

帧缓冲设备对应的设备文件为 /dev/fb*,如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达32 个,分别为/dev/fb0到/dev/fb31,而/dev/fb则为当前缺省的帧缓冲设备,通常指向/dev/fb0。当然在嵌入式系统中支持一个显 示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。通过/dev/fb, 应用程序的操作主要有这几种:

1、读/写(read/write)/dev/fb:相当于读/写屏幕缓冲区。

例如用 cp /dev/fb0 tmp命令可将当前屏幕的内容拷贝到一个文件中,而命令cp tmp > /dev/fb0 则将图形文件tmp显示在屏幕上。

2、映射(map)操作:由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此, Linux在文件操作 file_operations结构中提供了mmap函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址 映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。实际上,使用帧缓冲设备的应用程序都是通过映射操 作来显示图形的。由于映射操作都是由内核来完成,下面我们将看到,帧缓冲驱动留给开发人员的工作并不多。

3、 I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。

在应用程序中,操作/dev/fb的一般步骤如下:

  1. 打开/dev/fb设备文件。
  2. 用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
  3. 将屏幕缓冲区映射到用户空间。
  4. 映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。

典型实例程序—在屏幕上画一条线:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

int main ()
{
    int fp=0;
    struct fb_var_screeninfo  vinfo;
    struct fb_fix_screeninfo  finfo;
    long screensize=0;
    char *fbp = 0;
    int x = 0, y = 0;
    long location = 0;

    fp = open ("/dev/fb0",O_RDWR);

    if (fp < 0)
    {
        printf("Error : Can not open framebuffer device/n");
        exit(1);
    }

    if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo))
    {
        printf("Error reading fixed information/n");
        exit(2);
    }

    if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo))
    {
        printf("Error reading variable information/n");
        exit(3);
    }
    //显示结构体信息
    printf("The mem is :%d\n",finfo.smem_len);
    printf("The line_length is :%d\n",finfo.line_length);
    printf("The xres is :%d\n",vinfo.xres);
    printf("The yres is :%d\n",vinfo.yres);
    printf("bits_per_pixel is :%d\n",vinfo.bits_per_pixel);  

    //计算显存大小
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    /*这就是把fp所指的文件中从开始到screensize大小的内容给映射出来,得到一个指向这块空间的指针*/
    fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);
    if ((int) fbp == -1)
    {
        printf ("Error: failed to map framebuffer device to memory./n");
        exit (4);
    }
    /*这是你想画的点的位置坐标,(0,0)点在屏幕左上角*/
    int i,j;
    x = 10;
    y = 10;
    for(i=x,j=y;i<100;i++,j++)
    {
        location = i * (vinfo.bits_per_pixel / 8) + j  *  finfo.line_length;

        *(fbp + location) = 0;  /* 蓝色的色深 */  /*直接赋值来改变屏幕上某点的颜色*/
        *(fbp + location + 1) = 0xcc; /* 绿色的色深*/
        *(fbp + location + 2) = 0xcc; /* 红色的色深*/
        *(fbp + location + 3) = 0;  /* 是否透明*/
    }
    munmap (fbp, screensize); /*解除映射*/
    close (fp);    /*关闭文件*/

    return 0;
}

4、摄像头数据通过framebuffer进行显示

本试验使用的usb无驱摄像头,参数为:YUV422格式数据,320*240像素。

使用的mini2440开发板参数为:帧缓存设备fb的数据格式为RGB565,屏幕尺寸为240*320。

要实现usb摄像头数据直接完整显示在液晶屏上,需要执行以下几步:

(1)读取摄像头数据。

(2)转换YUV422格式到RGB565格式。

(3)将320*240尺寸图像转换为240*320尺寸,然后输出到帧缓存设备fb上。

读取摄像头数据前面的章节已经说过,此处略过。

YUV 4:2:2采样,每两个Y共用一组UV分量。

存放的码流为:Y0 U0 Y1 V1 Y2 U2 Y3 V3

也就是说每四个字节的YUV422数据对应两个RGB像素,每个RGB像素为3个字节。

RGB565彩色模式, 一个像素占两个字节, 其中:第一个字节的前5位用来表示R(Red),第一个字节的后三位+第二个字节的前三位用来表示G(Green),第二个字节的后5位用来表示B(Blue)。RGB656:[15-0bit]—— R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B4 B2 B1 B0

如果直接从YUV422转换到RGB565格式则过程非常复杂,不利于程序分析,本试验方法是先将YUV422格式转换为RGB24格式,然后再将RGB24格式转换为RGB565格式。具体转换程序见下文。

mini2440 帧缓存framebuffer默认的x=240,y=320,而摄像头正好相反。导致如果读取到的摄像头数据直接写入fb中显示会出错!(本处不讨论图像缩放问题)所以,我们写入fb之前需要先将图像90度旋转。

旋转有一个绕什么转的问题。

我们先来看最简单的,绕第一个像素转,则旋转的情况会像这样:

令旋转前有:

旋转a角度后有:

则旋转90度时有:

x1=y0

y1=x0

摄像头数据坐标视图如下:

设摄像头数据流中某像素对应的buf索引为i,则:

x0=i%320

y0=i/320

该点对应与帧缓存fb字节流中的位置为:

i(x1,y1)=y1?240+x1=(i%320)?240+i/320

伪代码如下:

// 320*240  90度旋转为  240*320 图像
for(i=0; i< 320*240; i++)
{
    fb[240*(i%320)+i/320] = camera_rgb[i];
}
//实际使用时要考虑fb一个像素2个字节,camera_rgb一个像素3个字节。

程序执行结果如下图:

完整程序源码:

/*************************************************************************
    > File Name: hello.c
    > Author: chad
    > Mail: [email protected]
    > Created Time: 2015年05月24日 星期三 15时19分49秒
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#define CLEAR(x) memset (&(x), 0, sizeof (x))

struct buffer {
    void * start;
    size_t length;
};

static char * dev_name = NULL;
static int fd = -1;
struct buffer * buffers = NULL;
static unsigned int n_buffers = 0;
static int time_in_sec_capture=5;
static int fbfd = -1;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static char *fbp=NULL;
static long screensize=0;
#define uchar unsigned char
#define uint unsigned int

void errno_exit (const char * s)
{
    fprintf (stderr, "%s error %d, %s\n",s, errno, strerror (errno));
    exit (EXIT_FAILURE);
}

int xioctl (int fd,int request,void * arg)
{
    int r;
    do r = ioctl (fd, request, arg);
    while (-1 == r && EINTR == errno);
    return r;
}

int clip(int value, int min, int max) {
    return (value > max ? max : value < min ? min : value);
  }

/*yuv4:2:2格式转换为rgb24格式*/
int convert_yuv_to_rgb_pixel(int y, int u, int v)
{
    uint pixel32 = 0;
    uchar *pixel = (uchar *)&pixel32;
    int r, g, b;
    r = y + (1.370705 * (v-128));
    g = y - (0.698001 * (v-128)) - (0.337633 * (u-128));
    b = y + (1.732446 * (u-128));
    if(r > 255) r = 255;
    if(g > 255) g = 255;
    if(b > 255) b = 255;
    if(r < 0) r = 0;
    if(g < 0) g = 0;
    if(b < 0) b = 0;
    pixel[0] = r * 220 / 256;
    pixel[1] = g * 220 / 256;
    pixel[2] = b * 220 / 256;

    return pixel32;
}
unsigned short RGB888toRGB565(unsigned short red, unsigned short green, unsigned short blue)
{
    unsigned short B = (blue >> 3) & 0x001F;
    unsigned short G = ((green >> 2) << 5) & 0x07E0;
    unsigned short R = ((red >> 3) << 11) & 0xF800;
    return (unsigned short) (R | G | B);
}
int convert_yuv_to_rgb_buffer(uchar *yuv, uchar *rgb, uint width,uint height)
{
    uint in, out = 0;
    uint pixel_16;
    uchar pixel_24[3];
    uint pixel32;
    int y0, u, y1, v;

    for(in = 0; in < width * height * 2; in += 4) {
        pixel_16 =
        yuv[in + 3] << 24 |
        yuv[in + 2] << 16 |
        yuv[in + 1] <<  8 |
        yuv[in + 0];//YUV422每个像素2字节,每两个像素共用一个Cr,Cb值,即u和v,RGB24每个像素3个字节
        y0 = (pixel_16 & 0x000000ff);
        u  = (pixel_16 & 0x0000ff00) >>  8;
        y1 = (pixel_16 & 0x00ff0000) >> 16;
        v  = (pixel_16 & 0xff000000) >> 24;
        pixel32 = convert_yuv_to_rgb_pixel(y0, u, v);
        pixel_24[0] = (pixel32 & 0x000000ff);
        pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
        pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
        rgb[out++] = pixel_24[0];
        rgb[out++] = pixel_24[1];
        rgb[out++] = pixel_24[2];//rgb的一个像素
        pixel32 = convert_yuv_to_rgb_pixel(y1, u, v);
        pixel_24[0] = (pixel32 & 0x000000ff);
        pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
        pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
        rgb[out++] = pixel_24[0];
        rgb[out++] = pixel_24[1];
        rgb[out++] = pixel_24[2];
    }
    return 0;
}
//////////////////////////////////////////////////////
//获取一帧数据
//////////////////////////////////////////////////////
int read_frame (unsigned char *rgbbuf)
{
    struct v4l2_buffer buf;

    CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    ioctl (fd, VIDIOC_DQBUF, &buf); //出列采集的帧缓冲

    assert (buf.index < n_buffers);
    printf ("buf.index dq is %d,\n",buf.index);

    convert_yuv_to_rgb_buffer(buffers[buf.index].start,rgbbuf,320,240);

    ioctl (fd, VIDIOC_QBUF, &buf); //再将其入列

    return 1;
}

void run (void)
{
    unsigned int count;
    int frames;
    int i,j;

    unsigned char *rgbbuf = malloc(320*240*3);//RGB24
    if( rgbbuf == NULL )
    {
        printf("malloc faile!\n");
        exit(1);
    }
    frames = 30 * time_in_sec_capture;
    while (frames-- > 0) {
        for (;;) {
            fd_set fds;
            struct timeval tv;
            int r;
            FD_ZERO (&fds);
            FD_SET (fd, &fds);

            tv.tv_sec = 2;
            tv.tv_usec = 0;

            r = select (fd + 1, &fds, NULL, NULL, &tv);

            if (-1 == r) {
                if (EINTR == errno)
                    continue;
                errno_exit ("select");
            }

            if (0 == r) {
                fprintf (stderr, "select timeout/n");
                exit (EXIT_FAILURE);
            }

            if (read_frame (rgbbuf))//如果可读,执行read_frame ()函数,并跳出循环
            {
                unsigned short *vout = (unsigned short*)fbp;
                for(i=0; i< 320*240; i++)
                {
                    j = i*3;
                    vout[240*(i%320)+i/320] = RGB888toRGB565(rgbbuf[j+0],rgbbuf[j+1],rgbbuf[j+2]);
                }
            }
        }
    }
}

void stop_capturing (void)
{
    enum v4l2_buf_type type;

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
        errno_exit ("VIDIOC_STREAMOFF");
}

void start_capturing (void)
{
    unsigned int i;
    enum v4l2_buf_type type;

    for (i = 0; i < n_buffers; ++i) {
        struct v4l2_buffer buf;
        CLEAR (buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
            errno_exit ("VIDIOC_QBUF");
        }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
        errno_exit ("VIDIOC_STREAMON");

}

void uninit_device (void)
{
    unsigned int i;

    for (i = 0; i < n_buffers; ++i)
        if (-1 == munmap (buffers[i].start, buffers[i].length))
            errno_exit ("munmap");

    if (-1 == munmap(fbp, screensize)) {
          printf(" Error: framebuffer device munmap() failed.\n");
          exit (EXIT_FAILURE) ;
        }
    free (buffers);
}

void init_mmap (void)
{
    struct v4l2_requestbuffers req;

    //mmap framebuffer
    fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);
    if ((int)fbp == -1) {
        printf("Error: failed to map framebuffer device to memory.\n");
        exit (EXIT_FAILURE) ;
    }
    memset(fbp, 0, screensize);
    CLEAR (req);

    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
        if (EINVAL == errno) {
            fprintf (stderr, "%s does not support memory mapping\n", dev_name);
            exit (EXIT_FAILURE);
        } else {
            errno_exit ("VIDIOC_REQBUFS");
        }
    }

    if (req.count < 4) {    //if (req.count < 2)
        fprintf (stderr, "Insufficient buffer memory on %s\n",dev_name);
        exit (EXIT_FAILURE);
    }

    buffers = calloc (req.count, sizeof (*buffers));

    if (!buffers) {
        fprintf (stderr, "Out of memory\n");
        exit (EXIT_FAILURE);
    }

    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
        struct v4l2_buffer buf;

        CLEAR (buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffers;

        if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
            errno_exit ("VIDIOC_QUERYBUF");

        buffers[n_buffers].length = buf.length;
        buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);

        if (MAP_FAILED == buffers[n_buffers].start)
            errno_exit ("mmap");
    }

}

void init_device (void)
{
    struct v4l2_capability cap;
    struct v4l2_cropcap cropcap;
    struct v4l2_crop crop;
    struct v4l2_format fmt;
    unsigned int min;

    // Get fixed screen information
    if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
        printf("Error reading fixed information.\n");
        exit (EXIT_FAILURE);
    }

    // Get variable screen information
    if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information.\n");
        exit (EXIT_FAILURE);
    }
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    printf("vinfo.xres=%d\n",vinfo.xres);
    printf("vinfo.yres=%d\n",vinfo.yres);
    printf("vinfo.bits_per_pixel=%d\n",vinfo.bits_per_pixel);
    printf("screensize=%d\n",screensize);

    if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
        if (EINVAL == errno) {
            fprintf (stderr, "%s is no V4L2 device/n",dev_name);
            exit (EXIT_FAILURE);
        } else {
            errno_exit ("VIDIOC_QUERYCAP");
        }
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf (stderr, "%s is no video capture device\n",dev_name);
        exit (EXIT_FAILURE);
    }

    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
        fprintf (stderr, "%s does not support streaming i/o\n",dev_name);
        exit (EXIT_FAILURE);
    }

    CLEAR (cropcap);

    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        crop.c = cropcap.defrect;

        if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
            switch (errno) {
            case EINVAL:
            break;
            default:
            break;
            }
        }
    }else {    }

    CLEAR (fmt);

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 320;
    fmt.fmt.pix.height = 240;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
        errno_exit ("VIDIOC_S_FMT");

    init_mmap ();

}

void close_device (void)
{
    if (-1 == close (fd))
    errno_exit ("close");
    fd = -1;
    close(fbfd);
}

void open_device (void)
{
    struct stat st;  

    if (-1 == stat (dev_name, &st)) {
    fprintf (stderr, "Cannot identify ‘%s‘: %d, %s\n",dev_name, errno, strerror (errno));
    exit (EXIT_FAILURE);
    }

    if (!S_ISCHR (st.st_mode)) {
        fprintf (stderr, "%s is no device\n", dev_name);
        exit (EXIT_FAILURE);
    }

    //open framebuffer
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd==-1) {
        printf("Error: cannot open framebuffer device.\n");
        exit (EXIT_FAILURE);
    }

    //open camera
    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);

    if (-1 == fd) {
        fprintf (stderr, "Cannot open ‘%s‘: %d, %s\n",dev_name, errno, strerror (errno));
        exit (EXIT_FAILURE);
    }
}

void usage (FILE * fp,int argc,char ** argv)
{
fprintf (fp,
"Usage: %s [options]\n\n"
"Options:/n"
"-d | --device name Video device name [/dev/video]\n"
"-h | --help Print this message\n"
"-t | --how long will display in seconds\n"
"",
argv[0]);
}

static const char short_options [] = "d:ht:";
static const struct option long_options [] = {
{ "device", required_argument, NULL, ‘d‘ },
{ "help", no_argument, NULL, ‘h‘ },
{ "time", no_argument, NULL, ‘t‘ },
{ 0, 0, 0, 0 }
};

int main (int argc,char ** argv)
{
    dev_name = "/dev/video0";

    for (;;)
    {
        int index;
        int c;

        c = getopt_long (argc, argv,short_options, long_options,&index);
        if (-1 == c)
        break;

        switch (c) {
        case 0:
        break;

        case ‘d‘:
        dev_name = optarg;
        break;

        case ‘h‘:
        usage (stdout, argc, argv);
        exit (EXIT_SUCCESS);
        case ‘t‘:
          time_in_sec_capture = atoi(optarg);
            break;

        default:
        usage (stderr, argc, argv);
        exit (EXIT_FAILURE);
        }
    }

    open_device ();

    init_device ();

    start_capturing ();

    run ();

    stop_capturing ();

    uninit_device ();

    close_device ();

    exit (EXIT_SUCCESS);

    return 0;
}

时间: 2024-11-09 01:35:27

【原创】IP摄像头技术纵览(三)---图像数据在帧缓存设备(framebuffer)上的显示的相关文章

【原创】IP摄像头技术纵览(五)---网络摄像头初试—mjpg-streamer移植与部署

[原创]IP摄像头技术纵览(五)-网络摄像头初试-mjpg-streamer移植与部署 本文属于<IP摄像头技术纵览>系列文章之一: Author: chad Mail: [email protected] 1.vgrabbj.spacview.Luvcview.mjpg-streamer评测对比 vgrabbj-0.9.6是基于v4l1设计的,与v4l2的API差别很大,该软件已经没有使用或参考价值. spcaview 也相当古老,并且调用了SDL库,不适合嵌入式系统,不建议研究. 什么是S

【原创】IP摄像头技术纵览(七)---P2P技术—UDP打洞实现内网NAT穿透

[原创]IP摄像头技术纵览(七)-P2P技术-UDP打洞实现内网NAT穿透 本文属于<IP摄像头技术纵览>系列文章之一: Author: chad Mail: [email protected] 本文可以自由转载,但转载请务必注明出处以及本声明信息. NAT技术的实际需求在10几年前就已经出现,为了解决这个问题,10几年来全世界的牛人早已经研究好了完整的解决方案,网上有大量优秀的解决方案文章,笔者自知无法超越,所以秉承拿来主义,将优秀文章根据个人实验及理解整理汇录于此,用于解释IP摄像头整个技

【原创】IP摄像头技术纵览(六)---通过internet访问摄像头

[原创]IP摄像头技术纵览(六)-通过internet访问摄像头 本文属于<IP摄像头技术纵览>系列文章之一: Author: chad Mail: [email protected] 本文可以自由转载,但转载请务必注明出处以及本声明信息. 1.路由器配置-DMZ虚拟主机.端口映射测试 上一节已经讲解了端口映射的方法实现internet访问,本节主要讲解DMZ虚拟主机的方法. (1)什么是DMZ? DMZ全称"demilitarized zone",中文名称为"隔

Windows Caffe(三) 图像数据转化为Caffe可以运行的数据

在运行Caffe自带的两个例子的时候,我们的数据都来自互联网,是直接下载的二进制文件. 但我们大多数情况下使用的是原始的图片数据(如.jpg .png等),接下来研究如何将原始的图片数据转化为caffe可以运行的数据. 1.准备图片数据 caffe安装完成之后,在example/images文件下会有四张.jpg图片,cat.jpg, cat gray,jpg, cat_gray.jpg, fish-back.jpg 2.生成图片清单 我们写需要一个sh脚本,调用Linux命令生成图片的清单.W

从IP代理引出的其他大数据技术

大数据在我们生活和工作当中的应用越来越成熟,它大大方便了我们的生活.商家利用它将我们所想要的产品送到我们的眼前,常用的客户端能够将我们感兴趣的内容推送到我们跟前.大数据之所以如此神奇,是因为它背后有着技术的支撑.除了我们常常谈到的以IP代理技术[芝麻软件]为支撑的爬虫技术之外,还有这些: 一. 分布式计算技术 分布式计算结合了NoSQL与实时分析技术,如果想要同时处理实时分析与NoSQL数据功能,那么你就需要分布式计算技术.分布式技术结合了一系列技术,可以对海量数据进行实时分析.更重要的是,它所

MATLAB获取“非免驱的相机或者摄像头”的图像数据

Image Acquisition Toolbox? Adaptor Kit 图像采集工具箱 当要使用MATLAB获取"非免驱的相机或者摄像头"的图像数据时,需要开发一个插件,MATLAB提供了插件接口,MATLAB中有范例和说明书,以方便二次开发. 通过imaqtool命令注册调用插件,可以预览相机或摄像头,也可以查看和设置参数. 有意思的事情是: 刚开始在计算机win7 32位系统安装了MATLAB R2009a,安装目录中有32位和64位的lib,用vs2008都开发了插件. 拿

寻找丢失的微服务-HAProxy热加载问题的发现与分析 原创: 单既喜 一点大数据技术团队 4月8日 在一点资讯的容器计算平台中,我们通过HAProxy进行Marathon服务发现。本文记录HAProxy服务热加载后某微服务50%概率失效的问题。设计3组对比实验,验证了陈旧配置的HAProxy在Reload时没有退出进而导致微服务丢失,并给出了解决方案. Keywords:HAProxy热加

寻找丢失的微服务-HAProxy热加载问题的发现与分析 原创: 单既喜 一点大数据技术团队 4月8日 在一点资讯的容器计算平台中,我们通过HAProxy进行Marathon服务发现.本文记录HAProxy服务热加载后某微服务50%概率失效的问题.设计3组对比实验,验证了陈旧配置的HAProxy在Reload时没有退出进而导致微服务丢失,并给出了解决方案. Keywords:HAProxy热加载.Marathon.端口重用 01 原文地址:https://www.cnblogs.com/yuanj

图像数据到网格数据-1——MarchingCubes算法

原文:http://blog.csdn.net/u013339596/article/details/19167907 概述 之前的博文已经完整的介绍了三维图像数据和三角形网格数据.在实际应用中,利用遥感硬件或者各种探测仪器,可以获得表征现实世界中物体的三维图像.比如利用CT机扫描人体得到人体断层扫描图像,就是一个表征人体内部组织器官形状的一个三维图像.其中的感兴趣的组织器官通过体素的颜色和背景加以区别.如下图的人体足骨扫描图像.医生通过观察这样的图像可以分析病人足骨的特征,从而对症下药. 这类

第七章 KinectV2结合MFC显示和处理图像数据(下)

第七章  KinectV2结合MFC显示和处理图像数据(下) 首先声明一下,本系统所使用的开发环境版本是计算机系统Windows 10.Visual Studio 2013.Opencv3.0和Kinect SDK v2.0.这些都可以在百度上找到,download下来安装一下即可. 一.在MFC中如何显示OpenCV的图像Mat 前段时间一直在学习opencv,但学习过程中写的例子都是基于控制台的.今天打算把之前写的一些例子都移植到MFC中,基本上就是复制以前的代码,唯一的区别在于在控制台中,