??本人從0開始學習linux,使用的是韋東山的教程,在跟著課程學習的情況下的所遇到的問題的總結,理論雖枯燥但是是基礎。本人將前幾章的內容大致學完之后,考慮到后續驅動方面得更多的開始實操,后續的內容將以韋東山教程Linux項目的內容為主,學習其中的代碼并手敲。做到鍛煉動手能力的同時鉆研其中的理論知識點。
摘要:相機部分代碼的理解
摘要關鍵詞:相機驅動、照片亮度調節、開發板ip配置
問題匯總
1.為什么int變量給枚舉不會報錯? int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2.void *bufs[32] 是什么?
3.為什么需要配置和檢查捕獲能力?
4.申請緩沖區是向誰申請?
5.緩沖區位于哪里?
6.明明要傳入函數指針,為什么可以傳入結構體?
7.是將數據都放到了bufs[i]里面了對吧,是這段代碼實現的數據存放嗎?
8.嘗試了adb拉取所有圖片發現拉取不了,只能從頭開始設置IP地址。
第一個項目相關使用的命令行,得記錄使用的數據分辨率大小
arm-buildroot-linux-gnueabihf-gcc -o video_get_data video_get_data.c
adb push video_get_data root
./video_get_data /dev/video1
adb pull root/video_raw_data_0010.jpg home
代碼程序
代碼實現輸出支持的圖像大小以及格式,并截取圖片。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <sys/mman.h>
#include <unistd.h>/* ./video_test </dev/video0> */int main(int argc, char **argv)
{int fd;struct v4l2_fmtdesc fmtdesc; // 參數結構體初始化int fmt_index = 0;int frame_index = 0;struct v4l2_frmsizeenum fsenum;void *bufs[32]; int buf_cnt;int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;struct pollfd fds[1];char filename[32];int file_cnt = 0;if (argc != 2){printf("Usage: %s </dev/videoX>,print detail for video device\n",argv[0]);return -1;}/* open */fd = open(argv[1],O_RDWR);if (fd < 0){printf("can not open %s \n",argv[1]);return -1;}// 查詢能力struct v4l2_capability capability;memset(&capability, 0, sizeof(struct v4l2_capability));if (0 == ioctl(fd, VIDIOC_QUERYCAP, &capability)){if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {printf("Error opening device %s: video capture not supported.\n",argv[1]);return -1;}if(!(capability.capabilities & V4L2_CAP_STREAMING)) {printf("%s does not support streaming i/o\n", argv[1]);return -1;}}else {printf("can not get capability\n");return -1;}/* */while(1){/* 枚舉格式 *//* 枚舉這種格式所支持的幀大小*/fmtdesc.index = fmt_index; // 比如從0開始fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type為"捕獲"if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) break;frame_index = 0;while(1){//枚舉所支持的幀大小memset(&fsenum,0,sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;if(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&fsenum) == 0){printf("format %s %d ,framesize %d x %d \n",fmtdesc.description,fmtdesc.pixelformat,fsenum.discrete.width,fsenum.discrete.height);}else break;frame_index++;}fmt_index++;}// 設置攝像頭分辨率struct v4l2_format fmt;memset(&fmt, 0, sizeof(struct v4l2_format));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = 800;fmt.fmt.pix.height = 480;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;fmt.fmt.pix.field = V4L2_FIELD_ANY;if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt)){printf("set format ok :%d x %d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);}else {printf("can not set format\n");return -1;}/** 申請 buffers*/struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if ( 0 == ioctl(fd, VIDIOC_REQBUFS, &rb)){/** 申請成功后,map the buffers*/buf_cnt = rb.count;for(int i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if(0 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){// mmapbufs[i] = mmap(0 /* start anywhere */ ,buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,buf.m.offset);if(bufs[i] == MAP_FAILED) {printf("Unable to map buffer");return -1;}}else{printf("can not query buffer");return -1 ;}}printf("map %d bufders ok \n ",buf_cnt);}else {printf("can not request buffers\n");return -1;}// 把所有buffer放入"空閑鏈表"/** Queue the buffers.*/for(int i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if ( 0 != ioctl(fd, VIDIOC_QBUF, &buf)){printf("Unable to queue buffer");return -1;}}printf("queue buffers ok \n");// ioctl VIDIOC_STREAMON:啟動攝像頭if( 0 == ioctl(fd, VIDIOC_STREAMON, &type)){printf("start capture ok");}else {printf("Unable to start capture");return -1;}//就是這段代碼的問題查原因 while(1) {/* poll */memset(fds,0,sizeof(fds));fds[0].fd = fd;fds[0].events = POLLIN;/* 把buffer取出隊列 */if (1 == poll(fds,1,-1)){struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_DQBUF, &buf));else{printf("Unable to dequeue buffer");return -1;}/* 把buffer的數據存儲為文件 */sprintf(filename,"video_raw_data_%04d.jpg",file_cnt++);int fd_file = open(filename,O_RDWR | O_CREAT, 0666);if (fd_file < 0){printf("can not create file: %s \n",filename);}printf("capture to %s\n",filename);write(fd_file,bufs[buf.index], buf.bytesused);close(fd_file);/* 把buffer放入隊列 */if (0 == ioctl(fd, VIDIOC_DQBUF, &buf));else{printf("Unable to dequeue buffer");return -1;}}}if (0 == ioctl(fd, VIDIOC_STREAMOFF, &type)){printf("stop capture");}else {printf("Unable to stop capture");return -1;}close(fd);return 0;}
代碼不詳細解釋,只有本人不熟的問題。
1.為什么int變量給枚舉不會報錯? int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
在C++中,枚舉類型本質上就是整數類型。每個枚舉值在編譯時會被替換為對應的整數值。因此,將枚舉值賦給int變量是完全合法的,編譯器不會報錯。
2.void bufs[32] 是什么?
這是一個包含32個void指針的數組:void 是一種通用指針類型,可以指向任何類型的數據
[32] 表示這是一個包含32個元素的數組,每個元素都是一個指針,可以存儲任何數據類型的地址這種結構在系統編程中很常見,特別是在需要管理多種類型數據或需要通用數據存儲的場景中。
3.為什么需要配置和檢查捕獲能力?
1. 設備多樣性
不是所有視頻設備都支持視頻捕獲功能。有些設備可能是:
視頻輸出設備(如HDMI輸出)
音頻設備
無線電接收設備
其他特殊用途設備
2. 功能支持差異
不同設備支持不同的I/O方法:
V4L2_CAP_STREAMING: 支持流式I/O(內存映射或用戶指針)
V4L2_CAP_READWRITE: 支持傳統的read()/write() I/O
V4L2_CAP_VIDEO_CAPTURE: 支持視頻捕獲
V4L2_CAP_VIDEO_OUTPUT: 支持視頻輸出
4.申請緩沖區是向誰申請?
不是直接向攝像頭硬件申請,而是向內核中的V4L2驅動程序申請
驅動程序負責管理視頻捕獲所需的內存區域
這些內存區域用于存儲攝像頭捕獲的幀數據
5.緩沖區位于哪里?
緩沖區位于內核空間,通過mmap系統調用映射到用戶空間,攝像頭硬件通過DMA(直接內存訪問) 直接將數據寫入這些緩沖區
void *memset(void *s, int c, size_t count)
{char *xs = s;while (count--)*xs++ = c;return s;
} memset
6.明明要傳入函數指針,為什么可以傳入結構體? `
struct v4l2_requestbuffers rb;
memset(&rb, 0, sizeof(struct v4l2_requestbuffers));
void* 的通用性??:
void* 是“無類型指針”,可接受任何類型的數據指針(如 int*、char*、結構體指針等)。編譯器會自動將具體指針類型轉換為 void*。
??結構體的內存本質??:
結構體在內存中是連續的字節塊。memset 按字節操作內存,與數據類型無關。傳入 &rb 時,函數會從該地址開始,將 sizeof(struct v4l2_requestbuffers) 個字節全部設為 0
7.是將數據都放到了bufs[i]里面了對吧,是這段代碼實現的數據存放嗎? 是的,你的理解非常準確!這段代碼確實是實現數據存放的關鍵步驟之一。它通過 mmap 系統調用,??將驅動在內核空間申請的內存映射到你的用戶空間應用程序中??,這樣你就可以直接讀取或處理采集到的視頻幀數據了。
為了讓這個過程更清晰,我們來分解一下:
內存映射 (mmap) 的作用 直接訪問??:mmap 讓你無需在用戶態和內核態之間拷貝數據(避免了 read/write 之類的系統調用開銷),就能直接訪問驅動中申請的幀緩沖區。這對于需要高效處理大量視頻數據的應用至關重要。
8.嘗試了adb拉取所有圖片發現拉取不了,只能從頭開始設置IP地址。
直接輸入ifconfig eth0 192.168.5.9設置IP地址。
方案2:更改永久保存如圖所示輸入 vi/etc/network/interfaces 命令行,更改設置,設置內容如圖所示。
重啟之后可以看到。
電腦之間實現相互應答。
arm-buildroot-linux-gnueabihf-gcc -o video_brightness video_brightness.c
adb push video_brightness root
./video_brightness /dev/video1
shift+鼠標左鍵指定選擇區域
d+回車要慢一點按
實現攝像機亮度調節如圖所示。
pthread_create(&thread, NULL, thread_brightness_control, (void )fd);
void 的通用性:void* 是C語言中的通用指針類型,可以指向任何數據類型,類型轉換:C語言允許在整數類型和指針類型之間進行顯式,轉換大小兼容:在大多數系統上,指針的大小足以存儲一個int值