V4L(video4linux是一些視頻系統,視頻軟件、音頻軟件的基礎,經常時候在需要采集圖像的場合,如視頻監控,webcam,可視電話,經常
使用在embedded linux中是linux嵌入式開發中經常使用的系統接口。它是linux內核提供給用戶空間的編程接口,各種的視頻和音頻設備開
發相應的驅動程序后,就可以通過v4l提供的系統API來控制視頻和音頻設備,也就是說v4l分為兩層,底層為音視頻設備在內核中的驅動,上
層為系統提供的API,而對於我們來說需要的就是使用這些系統API。
V4L2是V4L的升級版本,為linux下視頻設備程序提供了一套接口規范。包括一套數據結構和底層V4L2驅動接口。V4L2采用流水線的方式,
操作更簡單直觀,基本遵循打開視頻設備、設置格式、處理數據、關閉設備,更多的具體操作通過ioctl函數來實現。
1、打開設備
int open(const char *device_name, int flags);
int fd = open("/dev/video0", O_RDONLY); //O_NONBLOCK --非阻塞(不推薦使用)
2、關閉設備 int close(int fd)
int ret = close(fd);
3、v4l2_capability 查看屬性
int ioctl(int fd, int request, struct v4l2_capability *argp);
struct v4l2_capability
{
u8 driver[16]; // 驅動名字
u8 card[32]; // 設備名字
u8 bus_info[32]; // 設備在系統中的位置
u32 version; // 驅動版本號
u32 capabilities; // 設備支持的操作
u32 reserved[4]; // 保留字段
};
4、設置視頻格式與制式
相關函數:
int ioctl(int fd, int request, struct v4l2_fmtdesc *argp);
int ioctl(int fd, int request, struct v4l2_format *argp);
相關結構體:
v4l2_cropcap 結構體用來設置攝像頭的捕捉能力,在捕捉上視頻時應先先設置
v4l2_cropcap 的 type 域,再通過 VIDIO_CROPCAP 操作命令獲取設備捕捉能力的參數,保存於 v4l2_cropcap 結構體中,包括 bounds
(最大捕捉方框的左上角坐標和寬高),defrect(默認捕捉方框的左上角坐標和寬高)等。
v4l2_format 結構體用來設置攝像頭的視頻制式、幀格式等,在設置這個參數時應先填 好 v4l2_format 的各個域,如 type(傳輸流類型),
fmt.pix.width(寬),fmt.pix.heigth(高),fmt.pix.field(采樣區域,如隔行采樣),fmt.pix.pixelformat(采樣類型,如 YUV4:2:2),
然后通過 VIDIO_S_FMT 操作命令設置視頻捕捉格式。
struct v4l2_fmtdesc
{
u32 index; // 要查詢的格式序號,應用程序設置
enum v4l2_buf_type type; // 幀類型,應用程序設置
u32 flags; // 是否為壓縮格式
u8 description[32]; // 格式名稱
u32 pixelformat; // 格式
u32 reserved[4]; // 保留
};
所有的視頻格式可以能下面的方法查看
#define v4l2_fourcc(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
All format : VIDIOC_ENUM_FMT, v4l2_fmtdesc
struct v4l2_format
{
enum v4l2_buf_type type; // 幀類型,應用程序設置
union fmt
{
struct v4l2_pix_format pix; // 視頻設備使用
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
u8 raw_data[200];
};
};
struct v4l2_pix_format
{
u32 width; // 幀寬,單位像素
u32 height; // 幀高,單位像素
u32 pixelformat; // 幀格式
enum v4l2_field field;
u32 bytesperline;
u32 sizeimage;
enum v4l2_colorspace colorspace;
u32 priv;
};
5、查看視頻的幀率
int ioctl(int fd, int request, struct v4l2_streamparm parm* argp);
struct v4l2_streamparm parm;
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
其中VIDIOC_G_PARM是用取幀率的,VIDIOC_S_PARM是用來設定幀率
6、定制ioctl函數
在內核目錄下找到kernel/include/linux/videodev2.h頭文件,你可查看所有的io控制的命令
/*
* Experimental, third param 0--video, 1--tracking
*/
#define VIDIOC_POCCESS_NOTIFY _IOW('V', 99, int) //add by Henry.Wen 20131126
實例:
#ifndef CAMERA_V4L2CAMERA_H #define CAMERA_V4L2CAMERA_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <getopt.h> /* getopt_long() */ #include <fcntl.h> /* low-level i/o */ #include <unistd.h> #include <errno.h> #include <malloc.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> /* for videodev2.h */ #include <linux/videodev2.h> #define CONFIG_CAMERA_UVC_INVAL_FRAMECNT 5 namespace v4l2 { /** * return error code */ enum _RET_ERROR_CODE { RET_ERROR_FAIL = -1, RET_ERROR_OK = 0, RET_ERROR_CAPTURE_NULL = -9999, RET_ERROR_CAPTURE_NAME, RET_ERROR_CAPTURE_CAPABILITY, RET_ERROR_CAPTURE_FORMAT, RET_ERROR_CAPTURE_BUFFER, RET_ERROR_CAPTURE_OUTMEMORY, RET_ERROR_CAPTURE_MMAP, RET_ERROR_CAPTURE_FORM, RET_ERROR_CAPTURE_MUMAP, RET_ERROR_CAPTURE_VIDIOC, }; /** * Name: video_format enum * Function: Describe formats V4L2 will support */ typedef enum _pFormat { UNKNOWN, YUYV, MJPEG, YV12, YU12, NV12, NV21, H264, }pFormat; /** * frame width and height infomation */ typedef struct _V4l2Info { unsigned int width; unsigned int height; unsigned int stepWidth; unsigned int length; void* buffer; }V4l2Info; /** * caputre properties */ typedef struct _V4l2Capture { pFormat format; char name[31];//dev_name int fd; unsigned int rate; unsigned int quality; unsigned int brightness; V4l2Info v4l2Info; }V4l2Capture; /** * */ class V4l2Camera { public: V4l2Camera(); virtual ~V4l2Camera(); public: /** * get the number of cameras * * @return the number of camera */ static int getNumberOfCameras(); /** * initialize v4l2 device * @param capture v4l2 capture handl * @param width frame width * @param height fram height * * @return 0/other successful or failure */ int InitDevice(V4l2Capture *capture, pFormat format, const char* name, unsigned int rate, unsigned int width, unsigned int height); /** * initialize v4l2 device * @param capture v4l2 capture handl * @param width frame width * @param height fram height * * @return 0/other successful or failure */ int UninitDevice(V4l2Capture *capture); /** * Set v4l2 device brightness * @param capture v4l2 capture handl * @param value brightness value * * @return 0/other successful or failure */ int SetBrightness(V4l2Capture *capture, unsigned int value); /** * start v4l2 device * @param fd v4l2 capture handl * * @return 0/other successful or failure */ int StartDevice(int fd); /** * stop v4l2 device * @fd capture v4l2 capture handl * * @return 0/other successful or failure */ int StopDevice(int fd); /** * Get frame data * @param capture v4l2 capture handl * * @return 0/other successful or failure */ int GetFrame(V4l2Capture *capture); private: int InitMmap(int fd); int xioctl(int fd, int request, void *arg); unsigned int GetCameraFormat(pFormat format); int AdjustV4l2Info(unsigned int& width, unsigned int& height); int MSleep(int fd, unsigned int msec); int MatchCameraAuto(int cameraId); private: typedef struct _Buffers { void *start; size_t length; }V4l2Buffers; int m_stime; V4l2Buffers* m_buffers; unsigned int m_nBuffers; V4l2Capture* m_capture; static int mNumberOfCameras; static int mCameraIndex[10]; int mUsbCameraIvalidFrameCnt; bool m_InitDevice; }; }//end namespace #endif //CAMERA_V4L2CAMERA_H
V4l2Camera.cpp
#include "V4l2Camera.h" namespace v4l2 { #define BUFFERS_COUNT 4 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define MEMST_VALUE(x) memset(&(x), 0, sizeof (x)) const V4l2Info g_v4l2Info[] = {{160, 120}, {320, 240}, {640, 480}, {1024, 768}, {1200, 900}, {1440, 1080}, {1600, 900}, {1600, 1200}}; V4l2Camera::V4l2Camera() { // TODO Auto-generated constructor stub m_capture = NULL; mUsbCameraIvalidFrameCnt = 0; m_InitDevice = false; } V4l2Camera::~V4l2Camera() { // TODO Auto-generated destructor stub if(m_capture) UninitDevice(m_capture); } int V4l2Camera::xioctl(int fd, int request, void *arg) { int r; int nCount= 0; do { r = ioctl (fd, request, arg); }while (RET_ERROR_FAIL == r && (EINTR == errno) && (++nCount < 100)); return r; } int V4l2Camera::mNumberOfCameras = 0; int V4l2Camera::mCameraIndex[] = {0}; unsigned int V4l2Camera::GetCameraFormat(pFormat format) { unsigned int ret = UNKNOWN; switch(format) { case YUYV: ret = V4L2_PIX_FMT_YUYV; break; case MJPEG: ret = V4L2_PIX_FMT_MJPEG; break; case YV12: ret = V4L2_PIX_FMT_YVU420; break; case YU12: ret = V4L2_PIX_FMT_YUV420; break; case NV12: ret = V4L2_PIX_FMT_NV12; break; case NV21: ret = V4L2_PIX_FMT_NV21; break; case H264: ret = V4L2_PIX_FMT_MPEG; break; default: break; } return ret; } int V4l2Camera::InitMmap(int fd) { int ret = RET_ERROR_OK; struct v4l2_requestbuffers req; req.count = BUFFERS_COUNT; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (RET_ERROR_FAIL == xioctl (fd, VIDIOC_REQBUFS, &req) || req.count < 2) { return RET_ERROR_CAPTURE_BUFFER; } m_buffers = (V4l2Buffers*)calloc(req.count, sizeof(V4l2Buffers)); if (!m_buffers) { return RET_ERROR_CAPTURE_OUTMEMORY; } for (m_nBuffers = 0; m_nBuffers < req.count; ++m_nBuffers) { struct v4l2_buffer buf; MEMST_VALUE (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = m_nBuffers; if (RET_ERROR_FAIL == xioctl (fd, VIDIOC_QUERYBUF, &buf)) { ret = RET_ERROR_CAPTURE_BUFFER; break; } m_buffers[m_nBuffers].length = buf.length; m_buffers[m_nBuffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (MAP_FAILED == m_buffers[m_nBuffers].start) { ret = RET_ERROR_CAPTURE_MMAP; break; } } return ret; } int V4l2Camera::AdjustV4l2Info(unsigned int& width, unsigned int& height) { int ret = RET_ERROR_FAIL; int index = 0; for(int nCount = ARRAY_LEN(g_v4l2Info) - 1, i = nCount; i >= 0; --i) { if(width <= (g_v4l2Info[i].width + 50)) { index = i; ret = RET_ERROR_OK; } else if(0 != nCount) { width = g_v4l2Info[index].width; height = g_v4l2Info[index].height; break; } } return ret; } int V4l2Camera::getNumberOfCameras() { char cam_path[20]; int fd = -1, i=0; struct v4l2_capability capability; mNumberOfCameras = 0; memset(mCameraIndex,0x00,sizeof(mCameraIndex)); for (i = 0; i < 10; ++i) { memset(cam_path,0x00,20); sprintf(cam_path, "/dev/video%d",i); fd = open(cam_path, O_RDONLY); if (fd < 0) continue; memset(&capability, 0, sizeof(struct v4l2_capability)); if (ioctl(fd, VIDIOC_QUERYCAP, &capability) < 0) { //LOGE("Video device(%s): query capability not supported.\n", cam_path); goto loop_continue; } if ((capability.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)) != (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING)) { } else { mCameraIndex[mNumberOfCameras] = i; mNumberOfCameras++; } loop_continue: if (fd > 0) { close(fd); fd = -1; } continue; } return mNumberOfCameras; } int V4l2Camera::MatchCameraAuto(int cameraId) { if (mNumberOfCameras <= 0 || mNumberOfCameras > 10 || cameraId < 0) { return -1; } // search for camera ID normally for (int i = 0; i < mNumberOfCameras; ++i) { if (cameraId == mCameraIndex[i]) { return cameraId; } } if (mNumberOfCameras == 1) { return mCameraIndex[0]; } return cameraId > mCameraIndex[mNumberOfCameras -1] ? mCameraIndex[mNumberOfCameras -1] : mCameraIndex[0]; } int V4l2Camera::InitDevice(V4l2Capture *capture, pFormat format, const char* name, unsigned int rate, unsigned int width, unsigned int height) { // TODO Auto-generated function stub struct stat st; int fd = 0, nRealCameraNameLen = 0, nMinCameraNameLen = 0; int ret = RET_ERROR_OK; char szcameraID[4], szCameraName[20]; int cameraId = 0; struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; if(NULL == capture || NULL == name || 0 == rate || 0 == width || 0 == height) { ret = RET_ERROR_CAPTURE_NULL; goto InitDeviceFAILED; } if ((nRealCameraNameLen = strlen(name)) < (nMinCameraNameLen =strlen("/dev/video0"))) { ret = RET_ERROR_CAPTURE_NULL; goto InitDeviceFAILED; } // Get camera ID memset(szcameraID,0x00,4); for (int i=0;i<3;i++) { if (nRealCameraNameLen >= (nMinCameraNameLen + i)) szcameraID[i] = name[nMinCameraNameLen - 1 + i]; } cameraId = atoi(szcameraID); mNumberOfCameras = 0; memset(mCameraIndex, 0x00, sizeof(mCameraIndex)); if (0 == getNumberOfCameras()) { //LOGE("There is NO camera!"); ret = RET_ERROR_CAPTURE_NAME; goto InitDeviceFAILED; } if (-1 == (cameraId = MatchCameraAuto(cameraId))) { //LOGE("There is NO camera!"); ret = RET_ERROR_CAPTURE_NAME; goto InitDeviceFAILED; } memset(szCameraName, 0x00, 20); sprintf(szCameraName, "/dev/video%d", cameraId); //LOGI("camera name is %s.", name); // if ((RET_ERROR_FAIL == stat (szCameraName, &st)) || (!S_ISCHR (st.st_mode)) || (RET_ERROR_FAIL == (fd = open(szCameraName, O_RDWR | O_NONBLOCK, 0)))) { ret = RET_ERROR_CAPTURE_NAME; goto InitDeviceFAILED; } if (RET_ERROR_FAIL == xioctl(fd, VIDIOC_QUERYCAP, &cap) || !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || !(cap.capabilities & V4L2_CAP_STREAMING)) { ret = RET_ERROR_CAPTURE_CAPABILITY; goto InitDeviceFAILED; } MEMST_VALUE(cropcap); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(RET_ERROR_OK == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) { crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; /* reset to default */ xioctl(fd, VIDIOC_S_CROP, &crop); } struct v4l2_format fmt; MEMST_VALUE (fmt); ret = GetCameraFormat(format); if(UNKNOWN == ret) { return RET_ERROR_CAPTURE_FORMAT; goto InitDeviceFAILED; } if(RET_ERROR_OK != AdjustV4l2Info(width, height)) { ret = RET_ERROR_CAPTURE_FORM; goto InitDeviceFAILED; } //SetBrightness(capture, capture->brightness); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = ret; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; printf("%s(%d) pixel format is %d\n", __FUNCTION__,__LINE__,ret); if (RET_ERROR_FAIL == xioctl (fd, VIDIOC_S_FMT, &fmt)) { ret = RET_ERROR_CAPTURE_FORMAT; goto InitDeviceFAILED; } if(RET_ERROR_OK != (ret = InitMmap(fd)) ) {// || RET_ERROR_OK != (ret = SetBrightness(capture, capture->brightness))) goto InitDeviceFAILED; } // set video frame rate struct v4l2_streamparm parm; parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (RET_ERROR_OK != ioctl(fd, VIDIOC_G_PARM, &parm)) { //LOGI("VIDIOC_G_PARM fail...."); } parm.parm.capture.timeperframe.numerator = 1; parm.parm.capture.timeperframe.denominator = rate; if (RET_ERROR_OK != ioctl(fd, VIDIOC_S_PARM, &parm)) { //LOGI("VIDIOC_S_PARM Fail...."); } //check setting of frame rate memset(&parm, 0x00, sizeof(v4l2_streamparm)); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_G_PARM, &parm); if (ret != RET_ERROR_OK) { //LOGI("VIDIOC_G_PARM fail...."); } capture->fd = fd; capture->v4l2Info.width = fmt.fmt.pix.width; capture->v4l2Info.height = fmt.fmt.pix.height; capture->format = format; capture->rate = rate; capture->v4l2Info.length = fmt.fmt.pix.sizeimage; capture->v4l2Info.stepWidth = fmt.fmt.pix.bytesperline; capture->v4l2Info.buffer = malloc(fmt.fmt.pix.sizeimage); strncpy(capture->name, name, sizeof(capture->name)); m_stime = 1000 / rate; m_capture = capture; m_InitDevice = true; return ret; InitDeviceFAILED: if (fd >= 0) { close(fd); fd = -1; } return ret; } int V4l2Camera::UninitDevice(V4l2Capture *capture) { // TODO Auto-generated function stub printf("%s(%d)...[BEGIN]\n", __FUNCTION__, __LINE__); int ret = RET_ERROR_OK; if (!m_InitDevice) { return ret; } else { m_InitDevice = false; } if (m_buffers) { for (unsigned int i = 0; i < m_nBuffers; ++i) { printf("%s(%d) munmap() i = %d\n", __FUNCTION__, __LINE__, i); if (RET_ERROR_FAIL == munmap(m_buffers[i].start, m_buffers[i].length)) { ret = RET_ERROR_CAPTURE_MUMAP; break; } } if (RET_ERROR_OK == ret) { printf("%s(%d) free(m_buffers)\n", __FUNCTION__, __LINE__); free(m_buffers); } m_buffers = NULL; if (capture) { if (capture->v4l2Info.buffer) { printf("%s(%d) free(capture->v4l2Info.buffer)\n", __FUNCTION__, __LINE__); free(capture->v4l2Info.buffer); capture->v4l2Info.buffer = NULL; } if (capture->fd >= 0) { printf("%s(%d) close(capture->fd)\n", __FUNCTION__, __LINE__); ret = close(capture->fd); } } } printf("%s(%d)...[END]\n", __FUNCTION__, __LINE__); return ret; } int V4l2Camera::SetBrightness(V4l2Capture *capture, unsigned int value) { // TODO Auto-generated function stub if(!capture || value > 10000) return RET_ERROR_FAIL; struct v4l2_control control; control.id = V4L2_CID_BRIGHTNESS; control.value = 255; if(RET_ERROR_FAIL == xioctl(capture->fd, VIDIOC_S_CTRL, &control)) { return RET_ERROR_FAIL; } capture->brightness = control.value; return RET_ERROR_OK; } int V4l2Camera::StartDevice(int fd) { // TODO Auto-generated function stub if(fd < 0) return RET_ERROR_FAIL; int ret = RET_ERROR_OK; for (unsigned int i = 0; i < m_nBuffers; ++i) { struct v4l2_buffer buf; MEMST_VALUE(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (RET_ERROR_FAIL == xioctl(fd, VIDIOC_QBUF, &buf)) { ret = RET_ERROR_CAPTURE_VIDIOC; break; } } enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (RET_ERROR_FAIL == xioctl(fd, VIDIOC_STREAMON, &type)) ret = RET_ERROR_CAPTURE_VIDIOC; return ret; } int V4l2Camera::StopDevice(int fd) { // TODO Auto-generated function stub enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (RET_ERROR_FAIL == xioctl(fd, VIDIOC_STREAMOFF, &type)) { return RET_ERROR_FAIL; } return 0; } int V4l2Camera::MSleep(int fd, unsigned int msec) { fd_set fds; FD_ZERO (&fds); FD_SET (fd, &fds); struct timeval tv; tv.tv_sec = msec; tv.tv_usec = 0; return select (fd + 1, &fds, NULL, NULL, &tv); } int V4l2Camera::GetFrame(V4l2Capture *capture) { // TODO Auto-generated function stub if(!capture) return RET_ERROR_FAIL; int fd = capture->fd; if(RET_ERROR_FAIL == MSleep(capture->fd, m_stime)) return RET_ERROR_FAIL; struct v4l2_buffer buf; MEMST_VALUE(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.reserved = 0; // Skip the first CONFIG_CAMERA_UVC_INVAL_FRAMECNT video frames // because they are possibly invalid if (mUsbCameraIvalidFrameCnt< CONFIG_CAMERA_UVC_INVAL_FRAMECNT) { mUsbCameraIvalidFrameCnt++; if(xioctl(fd, VIDIOC_DQBUF, &buf) >= 0) { xioctl(fd, VIDIOC_QBUF, &buf); } return RET_ERROR_OK; } if(RET_ERROR_FAIL == xioctl(fd, VIDIOC_DQBUF, &buf) || buf.index >= m_nBuffers) { return RET_ERROR_FAIL; } memcpy(capture->v4l2Info.buffer, m_buffers[buf.index].start, buf.bytesused); if(RET_ERROR_FAIL == xioctl(fd, VIDIOC_QBUF, &buf)) return RET_ERROR_FAIL; return RET_ERROR_OK; } }//end namespace