1. 程式人生 > 其它 >裝置驅動-【轉載】v4l2攝像頭採集流程及應用程式

裝置驅動-【轉載】v4l2攝像頭採集流程及應用程式

上一篇:v4l2 驅動-ioctl 呼叫流程

 https://www.cnblogs.com/zhangzhiwei122/p/16156730.html

 

原文連結:https://blog.csdn.net/liujun3512159/article/details/123857276

 

 V4L2是linux上用於採集圖片、視訊、音訊資料的一套框架,對上嚮應用程式提供統一的介面,對下支援各類複雜硬體的靈活擴充套件,

在遠端會議、視訊監控系統、嵌入式多媒體終端中都有廣泛的應用。 V4L2通過開啟驅動中建立的/dev/videoX裝置,就可以進行一系列的操作,

比如申請記憶體、設定格式、設定屬性、開啟採集等等。

通過v4l2採集步驟

 

 示例程式碼:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <fcntl.h>
  5 #include <sys/ioctl.h>
  6 #include <linux/videodev2.h>
  7 #include <string.h>
  8 #include <sys/mman.h>
  9  
 10  
 11 //使用者層緩衝區儲存結構體
12 typedef struct BUFTYPE 13 { 14 void *start; 15 int length; 16 }*usr_buf; 17 unsigned int buf_num = 4;//指定緩衝區個數 18 19 int fd;//開啟的裝置fd 20 /** 21 * @brief init_camera 初始化相機裝置屬性 22 * @param dev 裝置名稱 23 * @return 成功返回0,失敗返回-1 24 */ 25 int init_camera(const char* dev) 26 { 27 fd = open(dev, O_RDWR);
28 if(fd < 0){ 29 printf("open \"%s\" error\n", dev); 30 return -1; 31 } 32 33 /** 34 * 查詢裝置屬性 35 */ 36 struct v4l2_capability cap; 37 int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); 38 if (ret < 0) { 39 printf("VIDIOC_QUERYCAP error\n"); 40 return -1; 41 } 42 43 printf("driver : %s\n",cap.driver); 44 printf("device : %s\n",cap.card); 45 printf("bus : %s\n",cap.bus_info); 46 printf("version : %d\n",cap.version); 47 48 if(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE){ /*判斷是否為視訊捕獲裝置*/ 49 if(cap.capabilities & V4L2_CAP_STREAMING){/*判斷是否支援視訊流捕獲*/ 50 printf("support capture\n"); 51 }else{ 52 printf("unsupport capture\n"); 53 } 54 }else { 55 printf("error\n"); 56 return -1; 57 } 58 59 struct v4l2_fmtdesc fmtdesc; 60 fmtdesc.index=0; 61 fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; 62 while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) 63 { 64 printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description); 65 fmtdesc.index++; 66 } 67 68 /*設定格式*/ 69 struct v4l2_format fmt; 70 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//攝像頭緩衝 71 fmt.fmt.pix.width = 640; 72 fmt.fmt.pix.height = 480; 73 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; 74 if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) 75 { 76 printf("set foramt:V4L2_PIX_FMT_MJPEG failed\n"); 77 return -1; 78 } 79 return 0; 80 } 81 82 83 /** 84 * @brief mmap_buffer 分配使用者緩衝區記憶體,並建立記憶體對映 85 * @return 成功返回0,失敗返回-1 86 */ 87 int mmap_buffer() 88 { 89 usr_buf = (BUFTYPE*)calloc(buf_num, sizeof(BUFTYPE)); 90 if (!usr_buf) { 91 printf("calloc \"frame buffer\" error : Out of memory\n"); 92 return -1; 93 } 94 95 struct v4l2_requestbuffers req; 96 req.count = buf_num; //幀緩衝數量 97 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //視訊捕獲緩衝區型別 98 req.memory = V4L2_MEMORY_MMAP; //記憶體對映方式 99 if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { 100 printf("VIDIOC_REQBUFS fail\n"); 101 return -1; 102 } 103 104 105 /*對映核心快取區到使用者空間緩衝區*/ 106 for(unsigned int i = 0; i < buf_num; ++i) 107 { 108 /*查詢核心緩衝區資訊*/ 109 struct v4l2_buffer v4l2_buf; 110 memset(&v4l2_buf, 0, sizeof(v4l2_buf)); 111 v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 112 v4l2_buf.memory = V4L2_MEMORY_MMAP; 113 v4l2_buf.index = i; 114 if(ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf) < 0){ 115 printf("VIDIOC_QUERYBUF failed\n"); 116 return -1; 117 } 118 119 /* 建立對映關係 120 * 注意這裡的索引號,v4l2_buf.index 與 usr_buf 的索引是一一對應的, 121 * 當我們將核心緩衝區出隊時,可以通過查詢核心緩衝區的索引來獲取使用者緩衝區的索引號, 122 * 進而能夠知道應該在第幾個使用者緩衝區中取資料 123 */ 124 usr_buf[i].length = v4l2_buf.length; 125 usr_buf[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset); 126 127 if (MAP_FAILED == usr_buf[i].start){//若對映失敗,列印錯誤 128 printf("mmap failed: %d\n",i); 129 return -1; 130 }else{ 131 if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0){ // 若對映成功則將核心緩衝區入隊 132 printf("VIDIOC_QBUF failed\n"); 133 return -1; 134 } 135 } 136 } 137 return 0; 138 } 139 140 /** 141 * @brief stream_on 開啟視訊流 142 * @return 成功返回0,失敗返回-1 143 */ 144 int stream_on() 145 { 146 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 147 if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) 148 { 149 printf("VIDIOC_STREAMON failed\n"); 150 return -1; 151 } 152 return 0; 153 } 154 155 /** 156 * @brief write_frame 讀取一幀影象 157 * @return 返回影象幀的索引index,讀取失敗返回-1 158 */ 159 int write_frame() 160 { 161 struct v4l2_buffer v4l2_buf; 162 v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 163 v4l2_buf.memory = V4L2_MEMORY_MMAP; 164 if(ioctl(fd, VIDIOC_DQBUF, &v4l2_buf) < 0) // 核心緩衝區出佇列 165 { 166 printf("VIDIOC_DQBUF failed, dropped frame\n"); 167 return -1; 168 } 169 170 171 /* 172 * 因為核心緩衝區與使用者緩衝區建立的對映,所以可以通過使用者空間緩衝區直接訪問這個緩衝區的資料 173 */ 174 char buffer[256]; 175 sprintf(buffer,"/home/fox/qt_project/build-qt_cpp-Debug/%d.mjpg",v4l2_buf.index); 176 int file_fd = open(buffer,O_RDWR | O_CREAT); // 若開啟失敗則不儲存該幀影象 177 if(file_fd > 0){ 178 printf("saving %d images\n",v4l2_buf.index); 179 write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused); 180 close(file_fd); 181 } 182 183 184 if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) //緩衝區重新入隊 185 { 186 printf("VIDIOC_QBUF failed, dropped frame\n"); 187 return -1; 188 } 189 return v4l2_buf.index; 190 } 191 192 193 /** 194 * @brief stream_off 關閉視訊流 195 * @return 成功返回0,失敗返回-1 196 */ 197 int stream_off() 198 { 199 /*關閉視訊流*/ 200 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 201 if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1) 202 { 203 printf("Fail to ioctl 'VIDIOC_STREAMOFF'"); 204 return -1; 205 } 206 return 0; 207 } 208 209 /** 210 * @brief unmap_buffer 解除緩衝區對映 211 * @return 成功返回0,失敗返回-1 212 */ 213 int unmap_buffer() 214 { 215 /*解除核心緩衝區到使用者緩衝區的對映*/ 216 for(unsigned int i = 0; i < buf_num; i++) 217 { 218 int ret = munmap(usr_buf[i].start, usr_buf[i].length); 219 if (ret < 0) 220 { 221 printf("munmap failed\n"); 222 return -1; 223 } 224 } 225 free(usr_buf); // 釋放使用者緩衝區記憶體 226 return 0; 227 } 228 229 /** 230 * @brief release_camera 關閉裝置 231 */ 232 void release_camera() 233 { 234 close(fd); 235 } 236 237 int main(void) 238 { 239 int ret = init_camera("/dev/video1"); 240 if(ret < 0){ 241 printf("init_camera error\n"); 242 return -1; 243 } 244 245 ret = mmap_buffer(); 246 if(ret < 0){ 247 printf("mmap_buffer error\n"); 248 return -1; 249 } 250 251 ret = stream_on(); 252 if(ret < 0){ 253 printf("stream_on error\n"); 254 return -1; 255 } 256 257 for(int i=0;i<5;i++) 258 { 259 write_frame(); 260 } 261 262 ret = stream_off(); 263 if(ret < 0){ 264 printf("stream_off error\n"); 265 return -1; 266 } 267 268 ret = unmap_buffer(); 269 if(ret < 0){ 270 printf("unmap_buffer error\n"); 271 return -1; 272 } 273 274 release_camera(); 275 return 0; 276 }