本程序的開發環境是(我當前的系統):操作系統:RedHat 9.0 及 CG300的視頻採集卡
Video4linux 簡介
Video4Linux是爲市場現在常見的電視捕獲卡和並口及USB口的攝像頭提供統一的編程接口。同時也提供無線電通信和文字電視廣播解碼和垂直消隱的數據接口。本文主要針對USB攝像頭設備文件/dev/video0,進行視頻圖像採集方面的程序設計。
Video4linux 編程指南
1.視頻編程的流程
(1)打開視頻設備:
(2)讀取設備信息
(3)更改設備當前設置(可以不做)
(4)進行視頻採集,兩種方法:
a.內存映射
b.直接從設備讀取
(5)對採集的視頻進行處理( 本程序沒做,下次再show給大家)
(6)關閉視頻設備
- //下面我對這些操作做了個簡單的函數封裝 (o(∩_∩)o...哈哈)
- #ifndef _V4L_H
- #define _V4L_H
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <error.h>
- #include <assert.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/mman.h>
- #include <linux/videodev.h>
- #include <sys/types.h>
- #include <string.h>
- /*採集的圖像的最大長和寬*/
- #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 <stdio.h>
- #include <stdlib.h>
- #include <SDL/SDL.h>
- 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();
- }
代碼SHOW完了,下面再附點小信息供大家看看,剛玩LINUX不久,有錯誤的地方還請大俠們指出。