V4L2 视频采集技术

Video4linux 简介
Video4Linux是为市场现在常见的电视捕获卡和并口及USB口的摄像头提供统一的编程接口。同时也提供无线电通信和文字电视广播解码和垂直消隐的数据接口。本文主要针对USB摄像头设备文件/dev/video0,进行视频图像采集方面的程序设计。

Video4linux 编程指南
1.视频编程的流程
(1)打开视频设备:
(2)读取设备信息
(3)更改设备当前设置(可以不做)
(4)进行视频采集,两种方法:
 a.内存映射
 b.直接从设备读取
(5)对采集的视频进行处理( 本程序没做,下次再show给大家)
(6)关闭视频设备

//下面我对这些操作做了个简单的函数封装
#ifndef _V4L_H
#define _V4L_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*采集的图像的最大长和宽*/
#define MAX_WIDTH 400
#define MAX_HEIGHT 300
/*设备文件*/
#define DEFAULT_DEVICE “/dev/video0″
/*自定义数据结构,包含v4l 中用到的数据结构*/
typedef struct v4l_struct
{
    int fd;/*设备号*/
    struct video_capability capability; //包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等)
    struct video_channel channel[8];//信号源个数
    struct video_picture picture;//设备采集的图象的各种属性
    struct video_mmap mmap;//用于mmap         
    struct video_mbuf mbuf;//利用mmap进行映射的帧的信息            
    unsigned char *buffer ;/*图像数据存放区*/
    unsigned char *map;/*mmap方式获取数据时,数据的首地址*/
    int frame_current;
    int frame_using[2]; /*这个帧的状态0 表示可用,1表示不可用*/
}v4l_device;

 

/**************************************************************
* 函数名:v4l_open
* 功  能: 打开设备
* 输  入: dev,vd
* 输  出: 无
* 返  回:  -1—-失败  0—-成功
**************************************************************/
int v4l_open( char *dev, v4l_device *vd )
{
    if( !dev )
    {
        dev=DEFAULT_DEVICE ;
    }
    if( ( vd->fd = open( dev, O_RDWR ) )  < 0 )
    {
        perror( “v4l_open error” );
        return -1;
    }
    return 0;
}

/**************************************************************
* 函数名: v4l_get_capability
* 功  能: 获取设备属性
* 输  入: vd
* 输  出: 无
* 返  回:  -1—-失败 0—-成功
**************************************************************/
int v4l_get_capability( v4l_device *vd )
{
    if( ioctl( vd->fd, VIDIOCGCAP, &( vd->capability ) ) <0 )
    {
        perror( “v4l_get_capability” );
        return -1 ;
    }
    return 0; 
}

/***************************************************************
* 函数名:v4l_get_picture
* 功  能:获取图片属性
* 输  入: vd
* 输  出: 无
* 返  回:  -1—-失败  0—-成功
***************************************************************/
int v4l_get_picture( v4l_device *vd )
{
    if( ioctl( vd->fd,VIDIOCGPICT,&( vd->picture ) ) < 0 )
    {
        return -1;
    }
    return 0;
}

/**************************************************************
* 函数名: v4l_set_picture
* 功  能: 设置图片属性
* 输  入: vd
* 输  出: 无
* 返  回: -1—-失败 0—-成功
**************************************************************/
int v4l_set_picture( v4l_device *vd ) 
{
    if( ioctl( vd->fd, VIDIOCSPICT, &( vd->picture ) ) < 0 )
    {
        return -1;
    }
    return 0;
}

/*************************************************************
* 函数名:v4l_get_channels
* 功  能:获取通道信息
* 输  入: vd
* 输  出: 无
* 返  回:  -1—-失败 0—-成功
*************************************************************/
int v4l_get_channels( v4l_device *vd )
{
    int i;
    for( i=0;i < vd->capability.channels ; i++ )
    {
        vd->channel[i].channel = i;               //确定通道
        if( ioctl( vd->fd , VIDIOCGCHAN, &( vd->channel[i] ) ) <0 )
        {
            perror( “v4l_get_channel” );
            return -1;
        }
    }
    return 0;
}

/*************************************************************
* 函数名: v4l_get_mbuf
* 功  能: 获取内存映射信息
* 输  入: vd
* 输  出: 无
* 返  回:  -1—-失败 0—-成功
**************************************************************/
int v4l_get_mbuf( v4l_device *vd )
{
    if( ioctl ( vd->fd,VIDIOCGMBUF,&( vd->mbuf ) ) <0 )
    {
        perror( “get_mbuf:” );
        return -1;
    }
    if ( ( vd->map = ( unsigned char * )mmap( 0, vd->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd, 0 ) ) < 0 )
    {
        perror(“v4l_mmap_init:mmap”);
        return -1;
    }
    return 0 ;
}

/*************************************************************
* 函数名: v4l_init_mbuff
* 功  能: 初始化内存映射信息
* 输  入: vd
* 输  出: 无
* 返  回: 0—-成功
**************************************************************/
int v4l_init_mbuf(v4l_device *vd)
{
    //vd->mmap.frame = 10 ; //不懂双帧是怎样设置的这个frame 该是当前帧的可mbuf 以又没有设置怎么确定是双帧不是单帧还是更多
    vd->mmap.width = MAX_WIDTH;
    vd->mmap.height = MAX_HEIGHT;
    vd->mmap.format = vd->picture.palette;
    vd->frame_current = 0;
    vd->frame_using[0] = 0;
    vd->frame_using[1] = 0;
    return 0;
}

/**************************************************************
* 函数名: v4l_get_address
* 功  能: 获取数据在图像的地址
***************************************************************/
unsigned char *v4l_get_address(v4l_device *vd)
{
    return (vd->map + vd->mbuf.offsets[vd->frame_current]);
}

/*************************************************************
* 函数名: v4l_grab_frame
* 功  能: 捕获帧
**************************************************************/
int v4l_grab_frame(v4l_device *vd, int frame)
{
    if (vd->frame_using[frame]) 
    {
        fprintf(stderr, “v4l_grab_frame: frame %d is already used.\n”, frame);
        return -1;
    }
    vd->mmap.frame = frame;
    if ( ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap ) ) < 0 ) 
    {
        perror( “v4l_grab_frame” );
        return -1;
    }
    vd->frame_using[frame] = 1;
    vd->frame_current = frame;
    return 0;    
}

/**************************************************************
* 函数名: v4l_grab_sync
* 功  能:与内存映射捕获一致
**************************************************************/
int v4l_grab_sync(v4l_device *vd)
{  
    if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0) 
    {
        perror(“v4l_grab_sync”);
    }
    vd->frame_using[vd->frame_current] = 0;
    return 0;
}

/***************************************************************
* 函数名: v4l_munmap
* 功  能:停止内存映射
***************************************************************/
int v4l_munmap( v4l_device *vd )
{
    if ( munmap( vd->map, vd->mbuf.size ) < 0 ) 
    {
        perror( “v4lmunmap:munmap” );
        return -1;
    }
    return 0;
}

/***************************************************************
* 函数名: v4l_close
* 功  能:关闭设备
***************************************************************/
int v4l_close(v4l_device *vd)
{
    close(vd->fd);
    return 0;
}

#endif

 
//简单的封装了关于SDL的相关操作”ScreenSurface.h”
#ifndef SCREEN_SURFACE_H
#define SCREEN_SURFACE_H

#include 
#include 
#include 

class ScreenSurface 
{
public:
    ScreenSurface();
    bool screen_init(int w, int h, int b = 0, Uint32 f = 0);//初始化
    ~ScreenSurface();
    SDL_Surface* point() const;
    int screen_lock();
    void screen_unlock();
    void screen_quit();
    void screen_set_caption( const char *str );//设置标题
    bool flip( unsigned char * src) ;//显示
    int startTV();//开始采集
private:
    static int screenNum;
    int width;
    int height;
    int bpp;
    Uint32 flags;
    SDL_Surface* pScreen;
};

#endif

//ScreenSurface.cpp
#include “ScreenSurface.h”
#include “qt_v4l.h”
v4l_device  v4l_dev;
/**************************************************************
* 函数名: v4l_grab_movie
* 功  能:捕获连续图像
**************************************************************/
void  v4l_grab_movie()
{
    v4l_grab_frame(&v4l_dev, v4l_dev.frame_current);/*获取下一 帧*/
    v4l_grab_sync(&v4l_dev);/*等待传完一 帧*/
    v4l_dev.buffer = v4l_get_address(&v4l_dev);/*得到这一帧的地址*/
    v4l_dev.frame_current = (v4l_dev.frame_current+1)%2; /* 下一帧的frame*/
}

//构造函数。如果创建1个以上的screen surface,则会抛出异常
ScreenSurface::ScreenSurface():width(640), height(480), bpp(32), flags(0)

    pScreen = 0;
    v4l_open(DEFAULT_DEVICE, &v4l_dev);/*打开设备*/
    v4l_get_capability(&v4l_dev);
    v4l_get_picture(&v4l_dev);
    v4l_init_mbuf(&v4l_dev);/*初始化设备*/
    v4l_get_mbuf(&v4l_dev);/*内存映射*/
}

bool ScreenSurface::screen_init(int w, int h, int b, Uint32 f)
{
    width = w ;
    height = h ;
    bpp = b ;
    flags = f ;
    
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf(“SDL_Init Failed!\n”);
        return false;
    }
    //设置图象模式(宽*高 位数 标志 SDL_SWSURFACE | SDL_DOUBLEBUF)
    pScreen = SDL_SetVideoMode(width, height, bpp, flags);
    if ( pScreen == 0 )
    {
        printf(“Could’t set display mode \n”);
        SDL_Quit();
        return false;
    }
    SDL_ShowCursor(SDL_DISABLE);
    return true;
}

//析构函数。在对象消亡时,退出SDL系统。
ScreenSurface::~ScreenSurface()
{
    
}

//返回screen surface中SDL_Surface结构的指针,主要提供给SDL的函数调用
SDL_Surface* ScreenSurface::point() const
{
    return pScreen;
}

int ScreenSurface::screen_lock()
{
    if ( SDL_MUSTLOCK(pScreen))
        return SDL_LockSurface(pScreen);
    return 0;
}
void ScreenSurface::screen_unlock()
{
    if ( SDL_MUSTLOCK(pScreen))
        SDL_UnlockSurface(pScreen); 
}

void ScreenSurface::screen_quit()
{
    SDL_Quit();
    v4l_munmap(&v4l_dev) ;
    v4l_close(&v4l_dev); 
}

void ScreenSurface::screen_set_caption( const char *str )
{
    SDL_WM_SetCaption( str, 0 );
}

//显示(弹出flip)screen surface到屏幕上
bool ScreenSurface::flip( unsigned char * src )
{
    if ( screen_lock() < 0)
        return false;
    unsigned char  *dest;
    dest = ( unsigned char * )pScreen->pixels;
    memcpy( dest , src , width * height * 4 );
    screen_unlock();
    
    if ( SDL_Flip(pScreen) < 0 )
        return false;
    else 
        return true;   
}

int ScreenSurface::startTV()
{
    bool bFlag = true;
    while(bFlag)
    {
        v4l_grab_movie(); 
        unsigned char *buf= v4l_dev.buffer;
        if (buf != NULL)
        {
            flip(buf);
        }
        SDL_Event event;
        while(SDL_PollEvent(event))
        {
            if (event.type == SDL_QUIT)
            {
                //bFlag = false;
                screen_quit();
            }
        }
    }
}
//main.cpp
#include “ScreenSurface.h”
void main()
{
    ScreenSurface *m_pScreen;
    m_pScreen = new ScreenSurface( );
    m_pScreen->screen_init(400 , 300 , 32 , SDL_SWSURFACE | SDL_ANYFORMAT);
    m_pScreen->screen_set_caption(“DemoTV”);   
    m_pScreen->startTV();
}

你可能感兴趣的:(C/C++,Linux)