Linux下使用v4l2程式設計操作攝像頭裝置獲取圖片
阿新 • • 發佈:2019-02-12
進行操作前請參考我的另一篇部落格:
看懂之後再研究下面的程式:
/*****************************************************
* 檔名:GetYuyv.c
* 檔案描述:linux下使用v4l2程式設計操作攝像頭裝置獲取圖片
* 編寫人:王廷雲
* 編寫日期:2017-12-1
* 修改日期:2018-1-1
*****************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> // 下面四個標頭檔案是linux系統程式設計特有的
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/videodev2.h> // 操作攝像頭裝置
#define WIDTH 640 // 圖片的寬度
#define HEIGHT 480 // 圖片的高度
#define FMT V4L2_PIX_FMT_YUYV // 圖片格式
#define COUNT 5 // 緩衝區個數
int main(int argc, char **argv)
{
unsigned char *datas[COUNT]; // 緩衝區資料地址
int ret, i;
int fd;
/* 第一步:開啟攝像頭裝置檔案 */
fd = open("/dev/video0", O_RDWR); // 注意檢視攝像頭裝置名
if (-1 == fd)
{
perror("open /dev/video0");
return -1;
}
/* 第二步:設定捕捉圖片幀格式 */
struct v4l2_format format;
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 操作型別為獲取圖片
format.fmt.pix.width = WIDTH; // 圖片的寬度
format.fmt.pix.height = HEIGHT; // 圖片的高度
format.fmt.pix.pixelformat = FMT; // 圖片格式
ret = ioctl(fd, VIDIOC_S_FMT, &format); // 進行設定(Set)
if (-1 == ret)
{
perror("ioctl VIDIOC_S_FMT");
close(fd);
return -2;
}
/* 第三步:檢查是否設定成功 */
ret = ioctl(fd, VIDIOC_G_FMT, &format); // Get
if (-1 == ret)
{
perror("ioctl VIDIOC_G_FMT");
close(fd);
return -3;
}
if (format.fmt.pix.pixelformat == FMT)
{
printf("ioctl VIDIOC_S_FMT sucessful\n");
}
else
{
printf("ioctl VIDIOC_S_FMT failed\n");
}
/* 第四步:讓攝像頭驅動申請存放影象資料的緩衝區 */
struct v4l2_requestbuffers reqbuf;
reqbuf.count = COUNT; // 緩衝區個數
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 緩衝區型別
reqbuf.memory = V4L2_MEMORY_MMAP; // 緩衝區的用途:用於記憶體對映
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuf);
if (-1 == ret)
{
perror("ioctl VIDIOC_REQBUFS");
close(fd);
return -4;
}
/* 第五步:查詢每個緩衝區的資訊,同時進行記憶體對映 */
struct v4l2_buffer buff;
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
for (i = 0; i < COUNT; i++)
{
buff.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &buff);
if (-1 == ret) // 操作失敗
{
break;
}
/* 列印緩衝區的長度和偏移量 */
printf("buf[%d]: len = %d offset: %d\n", i, buff.length, buff.m.offset);
/* 把每塊緩衝區對映到當前程序來 */
datas[i] = mmap(NULL, buff.length, PROT_READ, MAP_SHARED, fd, buff.m.offset);
if (MAP_FAILED == datas[i]) // 對映失敗
{
perror("mmap failed");
return -5;
}
/* 把對映成功的緩衝區加入到攝像頭驅動的影象資料採集佇列裡 */
ret = ioctl(fd, VIDIOC_QBUF, &buff); // Queue
if (-1 == ret)
{
perror("VIDIOC_QBUF");
return -6;
}
}
/* 第六步:啟動採集 */
int on = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 設定啟動標誌位
ret = ioctl(fd, VIDIOC_STREAMON, &on); // 開啟攝像頭流
if (-1 == ret)
{
perror("ioctl VIDIOC_STREAMON");
return -7;
}
/* 第七步:讓已經採集好的資料緩衝退出佇列 */
ret = ioctl(fd, VIDIOC_DQBUF, &buff); // Dequeue
if (-1 == ret)
{
perror("ioctl VIDIOC_DQUF");
return -8;
}
/* 第八步:從退出佇列的緩衝區中獲取資料並儲存到檔案中 */
FILE *fl;
fl = fopen("./my.yuyv", "w");
if (NULL == fl)
{
fprintf(stderr, "open write file failed.");
}
fwrite(datas[buff.index], buff.bytesused, 1, fl);
fclose(fl); // 記得關閉已開啟的檔案
close(fd); // 記得關閉已開啟的裝置
return 0;
}