首页 > 其他分享 >8、V4L2接口学习、显示摄像头画面、集成项目使用

8、V4L2接口学习、显示摄像头画面、集成项目使用

时间:2022-12-09 22:32:38浏览次数:65  
标签:perror int unsigned 接口 fd ret fail V4L2 摄像头


基本思想:因为手中有一块RK3399 PRO的开发板,外接了AHD的摄像头,为了实现实时获取视频帧,所以需要学习一下v4l2编程。。

第一步:先拷贝一张图,整个v4l2的取帧流程

8、V4L2接口学习、显示摄像头画面、集成项目使用_学习

 具体讲解参考这个大佬的bilibili和有道笔记 ​9118c374edb&type=note&_time=1652171642765">​https://note.youdao.com/ynoteshare/index.html?id=7c4c0e28888d03ec70d339118c374edb&type=note&_time=1652171642765​

第一步

查看支持的显示 分辨率 

ubutnu@ubuntu~$: v4l2-ctl --list-formats-ext -d /dev/video0

代码,完成摄像头的帧获取和写入本地显示

#include <iostream>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include <zconf.h>
#include<linux/videodev2.h>
#include<sys/mman.h>
int main() {
//打开设备
int fd=open("/dev/video0",O_RDWR);
if(fd<0){
perror("open device fail\n");
return -1;
}
// 获取摄像头支持的格式
//获取摄像头支持的格式
struct v4l2_fmtdesc v4fmt;
v4fmt.index=0;
v4fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

int ret=ioctl(fd,VIDIOC_ENUM_FMT,&v4fmt );
if(ret<0){
perror("acvquire fail\n");
}
printf("%s\n",v4fmt.description);
unsigned char *p=(unsigned char*)&v4fmt.pixelformat;
printf("%c %c %c %c\n",p[0],p[1],p[2],p[3]);
// 设置摄像头支持的格式
//设置摄像头支持的格式
struct v4l2_format vFormat;
vFormat.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
vFormat.fmt.pix.width=640;
vFormat.fmt.pix.height=480;
vFormat.fmt.pix.pixelformat=V4L2_PIX_FMT_MJPEG;
ret=ioctl(fd,VIDIOC_S_FMT,&vFormat);
if(ret<0){
perror("set fail\n");
}
//申请内核空间
struct v4l2_requestbuffers vqbuff;
vqbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
vqbuff.count=4;
vqbuff.memory=V4L2_MEMORY_MMAP;
ret =ioctl(fd,VIDIOC_REQBUFS,&vqbuff);
if(ret<0){
perror("buff fail\n");
}
//申请内存空间
struct v4l2_buffer vbuff;
vbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

unsigned char * mptr[4];
for(int i=0;i<4;i++){
vbuff.index=i;
ret=ioctl(fd,VIDIOC_QUERYBUF,&vbuff);
if(ret<0)
{
perror("requeire buff fail\n");
}
mptr[i]= (unsigned char *s)mmap(NULL,vbuff.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,vbuff.m.offset);
//通知完毕
ret=ioctl(fd,VIDIOC_QBUF,&vbuff);
if(ret<0){
perror("put fail");
}

}
// 开始采集
int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(fd,VIDIOC_STREAMON,&type);
if(ret<0){
perror("open fail");
}
//从队列中取数据
struct v4l2_buffer readbuff;
readbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(fd,VIDIOC_DQBUF,&readbuff);
if(ret<0){
perror("read fail");

}

FILE* file=fopen("example.jpg","w+");
fwrite(mptr[readbuff.index],readbuff.length,1,file);
fclose(file);

//通知内核 已经使用完
ret=ioctl(fd,VIDIOC_QBUF,&readbuff);
if(ret<0){

perror("put quee fail\n");
}
//停止采集
ret=ioctl(fd,VIDIOC_STREAMOFF,&type);
if(ret<0){

perror("stop eque@e fail\n");
}
//关闭设备
close(fd);
printf("close device successfully\n");
return 0;
}

写到本地的图片

8、V4L2接口学习、显示摄像头画面、集成项目使用_#include_02

cmakelist.txt

cmake_minimum_required(VERSION 3.16)
project(untitled3)

set(CMAKE_CXX_STANDARD 14)
#sudo apt install libjpeg8-dev
add_executable(untitled3 main.cpp)
target_link_libraries(
untitled3
-ljpeg
)

源代码,编译ctrl+alt+f1在命令行执行

#include <iostream>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include <zconf.h>
#include<linux/videodev2.h>
#include<sys/mman.h>
#include <jpeglib.h>
#include <cstring>
#include <linux/fb.h>

int read_JPEG_file(unsigned char *jpegData, unsigned char *rgbdata, int size) {
struct jpeg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr);
//1创建解码对象并且初始化
jpeg_create_decompress(&cinfo);
//2.装备解码的数据
//jpeg_stdio_src(&cinfo, infile);
jpeg_mem_src(&cinfo, (unsigned char *) jpegData, size);
//3.获取jpeg图片文件的参数
(void) jpeg_read_header(&cinfo, TRUE);
/* Step 4: set parameters for decompression */
//5.开始解码
(void) jpeg_start_decompress(&cinfo);
//6.申请存储一行数据的内存空间
int row_stride = cinfo.output_width * cinfo.output_components;
unsigned char *buffer = (unsigned char *) (malloc(row_stride));
int i = 0;
while (cinfo.output_scanline < cinfo.output_height) {
//printf("****%d\n",i);
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);
memcpy(rgbdata + i * 640 * 3, buffer, row_stride);
i++;
}
//7.解码完成
(void) jpeg_finish_decompress(&cinfo);
//8.释放解码对象
jpeg_destroy_decompress(&cinfo);
return 1;
}


void lcd_show_rgb(unsigned int *lcdptr, int lcd_w, int lcd_h, unsigned char *rgb_data, int width, int height) {
unsigned int *ptr = lcdptr;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
memcpy(ptr + j, rgb_data + j * 3, 3);
}
ptr += lcd_w;
rgb_data += width * 3;
}
}

int main() {
//打开lcd
int lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd < 0) {
perror("open lcdfd fail");
return -1;
}
struct fb_var_screeninfo info;
ioctl(lcdfd, FBIOGET_VSCREENINFO, &info);
printf("xres = %u, yres = %u.\n", info.xres, info.yres);
printf("xres_virtual = %u, yres_virtual = %u.\n", info.xres_virtual, info.yres_virtual);
printf("bpp = %u.\n", info.bits_per_pixel);
//xu ni ji ubuntu
// int lcd_w = info.xres_virtual;
//int lcd_h = info.yres_virtual;
// arm
int lcd_w = info.xres;
int lcd_h = info.yres;
unsigned int *lcdptr = (unsigned int *) mmap(NULL, lcd_w * lcd_h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdptr == NULL) {
perror("create lcdptr fail");
return -1;
}
//打开设备
int fd = open("/dev/video0", O_RDWR);
if (fd < 0) {
perror("open device fail\n");
return -1;
}
// 获取摄像头支持的格式
//获取摄像头支持的格式
struct v4l2_fmtdesc v4fmt;
v4fmt.index = 0;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if (ret < 0) {
perror("acquire fail\n");
return -1;
}
printf("%s\n", v4fmt.description);
unsigned char *p = (unsigned char *) &v4fmt.pixelformat;
printf("%c %c %c %c\n", p[0], p[1], p[2], p[3]);
// 设置摄像头支持的格式
//设置摄像头支持的格式
struct v4l2_format vFormat;
vFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vFormat.fmt.pix.width = 640;
vFormat.fmt.pix.height = 480;
vFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
ret = ioctl(fd, VIDIOC_S_FMT, &vFormat);
if (ret < 0) {
perror("set fail\n");
return -1;
}
//申请内核空间
struct v4l2_requestbuffers vqbuff;
vqbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vqbuff.count = 4;
vqbuff.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_REQBUFS, &vqbuff);
if (ret < 0) {
perror("buff fail\n");
return -1;
}
//申请内存空间
struct v4l2_buffer mptrbuff;
mptrbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

unsigned char *mptr[4];
unsigned int mptr_len[4];
for (int i = 0; i < 4; i++) {
mptrbuff.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mptrbuff);
if (ret < 0) {
perror("require buff fail\n");
return -1;
}
mptr[i] = (unsigned char *) mmap(NULL, mptrbuff.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
mptrbuff.m.offset);
mptr_len[i] = mptrbuff.length;
//通知完毕
ret = ioctl(fd, VIDIOC_QBUF, &mptrbuff);
if (ret < 0) {
perror("put fail");
return -1;
}

}
// 开始采集
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if (ret < 0) {
perror("open fail");
return -1;
}
unsigned char rgb_data[640 * 480 * 3];
while (true) {
//从队列中取数据
struct v4l2_buffer readbuff;
readbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuff);
if (ret < 0) {
perror("read fail");
}
read_JPEG_file(mptr[readbuff.index], rgb_data, readbuff.length);
lcd_show_rgb(lcdptr,lcd_w,lcd_h, rgb_data, 640, 480);

// FILE* file=fopen("example.jpg","w+");
// fwrite(mptr[readbuff.index],readbuff.length,1,file);
//fclose(file);

//通知内核 已经使用完
ret = ioctl(fd, VIDIOC_QBUF, &readbuff);
if (ret < 0) {
perror("put equee fail\n");

}
}
//停止采集
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
if (ret < 0) {
perror("stop eque@e fail\n");

}

//释放资源、
for (int i = 0; i < 4; i++) {
munmap(mptr[i], mptr_len[i]);
}
//关闭设备
close(fd);
printf("close device successfully\n");
return 0;
}

测是的MJPEG的格式的图片显示

8、V4L2接口学习、显示摄像头画面、集成项目使用_#include_03

 yuyv转bgr显示 640*480

#include <iostream>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include <zconf.h>
#include<linux/videodev2.h>
#include<sys/mman.h>
#include <jpeglib.h>
#include <cstring>
#include <linux/fb.h>

int read_JPEG_file(unsigned char *jpegData, unsigned char *rgbdata, int size) {
struct jpeg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr);
//1创建解码对象并且初始化
jpeg_create_decompress(&cinfo);
//2.装备解码的数据
//jpeg_stdio_src(&cinfo, infile);
jpeg_mem_src(&cinfo, (unsigned char *) jpegData, size);
//3.获取jpeg图片文件的参数
(void) jpeg_read_header(&cinfo, TRUE);
/* Step 4: set parameters for decompression */
//5.开始解码
(void) jpeg_start_decompress(&cinfo);
//6.申请存储一行数据的内存空间
int row_stride = cinfo.output_width * cinfo.output_components;
unsigned char *buffer = (unsigned char *) (malloc(row_stride));
int i = 0;
while (cinfo.output_scanline < cinfo.output_height) {
//printf("****%d\n",i);
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);
memcpy(rgbdata + i * 640 * 3, buffer, row_stride);
i++;
}
//7.解码完成
(void) jpeg_finish_decompress(&cinfo);
//8.释放解码对象
jpeg_destroy_decompress(&cinfo);
return 1;
}
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{
//码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素
int r1, g1, b1;
int r2, g2, b2;
for(int i=0; i<w*h/2; i++)
{
char data[4];
memcpy(data, yuyvdata+i*4, 4);
unsigned char Y0=data[0];
unsigned char U0=data[1];
unsigned char Y1=data[2];
unsigned char V1=data[3];
//Y0U0Y1V1 -->[Y0 U0 V1] [Y1 U0 V1]
r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;
g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;
b1 = Y0 + 1.779 * (U0-128); if(b1>255)b1=255; if(b1<0)b1=0;

r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;
g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;
b2 = Y1 + 1.779 * (U0-128); if(b2>255)b2=255; if(b2<0)b2=0;
// rgb的数据
// rgbdata[i*6+0]=r1;
// rgbdata[i*6+1]=g1;
// rgbdata[i*6+2]=b1;
// rgbdata[i*6+3]=r2;
// rgbdata[i*6+4]=g2;
// rgbdata[i*6+5]=b2;
//bgr的数据
rgbdata[i*6+0]=b1;
rgbdata[i*6+1]=g1;
rgbdata[i*6+2]=r1;
rgbdata[i*6+3]=b2;
rgbdata[i*6+4]=g2;
rgbdata[i*6+5]=r2;
}
}

void lcd_show_rgb(unsigned int *lcdptr, int lcd_w, int lcd_h, unsigned char *rgb_data, int width, int height) {
unsigned int *ptr = lcdptr;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
memcpy(ptr + j, rgb_data + j * 3, 3);
}
ptr += lcd_w;
rgb_data += width * 3;
}
}

int main() {
//打开lcd
int lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd < 0) {
perror("open lcdfd fail");
return -1;
}
struct fb_var_screeninfo info;
ioctl(lcdfd, FBIOGET_VSCREENINFO, &info);
printf("xres = %u, yres = %u.\n", info.xres, info.yres);
printf("xres_virtual = %u, yres_virtual = %u.\n", info.xres_virtual, info.yres_virtual);
printf("bpp = %u.\n", info.bits_per_pixel);
//xu ni ji ubuntu
// int lcd_w = info.xres_virtual;
//int lcd_h = info.yres_virtual;
// arm
int lcd_w = info.xres;
int lcd_h = info.yres;
unsigned int *lcdptr = (unsigned int *) mmap(NULL, lcd_w * lcd_h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdptr == NULL) {
perror("create lcdptr fail");
return -1;
}
//打开设备
int fd = open("/dev/video0", O_RDWR);
if (fd < 0) {
perror("open device fail\n");
return -1;
}
// 获取摄像头支持的格式
//获取摄像头支持的格式
struct v4l2_fmtdesc v4fmt;
v4fmt.index = 0;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if (ret < 0) {
perror("acquire fail\n");
return -1;
}
printf("%s\n", v4fmt.description);
unsigned char *p = (unsigned char *) &v4fmt.pixelformat;
printf("%c %c %c %c\n", p[0], p[1], p[2], p[3]);
// 设置摄像头支持的格式
//设置摄像头支持的格式
struct v4l2_format vFormat;
vFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vFormat.fmt.pix.width = 640;
vFormat.fmt.pix.height = 480;
vFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
ret = ioctl(fd, VIDIOC_S_FMT, &vFormat);
if (ret < 0) {
perror("set fail\n");
return -1;
}
//申请内核空间
struct v4l2_requestbuffers vqbuff;
vqbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vqbuff.count = 4;
vqbuff.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_REQBUFS, &vqbuff);
if (ret < 0) {
perror("buff fail\n");
return -1;
}
//申请内存空间
struct v4l2_buffer mptrbuff;
mptrbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

unsigned char *mptr[4];
unsigned int mptr_len[4];
for (int i = 0; i < 4; i++) {
mptrbuff.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mptrbuff);
if (ret < 0) {
perror("require buff fail\n");
return -1;
}
mptr[i] = (unsigned char *) mmap(NULL, mptrbuff.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
mptrbuff.m.offset);
mptr_len[i] = mptrbuff.length;
//通知完毕
ret = ioctl(fd, VIDIOC_QBUF, &mptrbuff);
if (ret < 0) {
perror("put fail");
return -1;
}

}
// 开始采集
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if (ret < 0) {
perror("open fail");
return -1;
}
unsigned char rgb_data[640 * 480 * 3];
while (true) {
//从队列中取数据
struct v4l2_buffer readbuff;
readbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuff);
if (ret < 0) {
perror("read fail");
}
yuyv_to_rgb(mptr[readbuff.index], rgb_data, 640, 480);
//read_JPEG_file(mptr[readbuff.index], rgb_data, readbuff.length);
lcd_show_rgb(lcdptr,lcd_w,lcd_h, rgb_data, 640, 480);

// FILE* file=fopen("example.jpg","w+");
// fwrite(mptr[readbuff.index],readbuff.length,1,file);
//fclose(file);

//通知内核 已经使用完
ret = ioctl(fd, VIDIOC_QBUF, &readbuff);
if (ret < 0) {
perror("put equee fail\n");

}
}
//停止采集
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
if (ret < 0) {
perror("stop eque@e fail\n");

}

//释放资源、
for (int i = 0; i < 4; i++) {
munmap(mptr[i], mptr_len[i]);
}
//关闭设备
close(fd);
printf("close device successfully\n");
return 0;
}

测试图片 

8、V4L2接口学习、显示摄像头画面、集成项目使用_ci_04

参考

有道云笔记

 Linux应用开发【第七章】摄像头V4L2编程应用开发 - 知乎


标签:perror,int,unsigned,接口,fd,ret,fail,V4L2,摄像头
From: https://blog.51cto.com/u_12504263/5926652

相关文章

  • 37、在OAK摄像头上部署tensorflow deeplabv3+进行实例分割
    基本思想:手中有个OAK摄像头,一直想移植一下官方的deeplabv3的模型,逐记录一下训练过程和模型转换,从pb转模型到openvino,然后在移植oak摄像头,tensorflow/model的版本为2022-09-1......
  • Java 中的接口还可以这样用,你知道吗?
    Java程序员都知道要面向接口编程,那Java中的接口除了定义接口方法之外还能怎么用你知道吗?今天阿粉就来带大家看一下Java中的接口还可以有哪些用法。基本特性我们先看......
  • Metal 开发 | 使用 C++ 进行接口调用~~
    文章首发博客:https://glumes.com前两天在群里面看到大佬转发一篇文章:GettingstartedwithMetal-cpp。链接在此:​​https://developer.apple.com/metal/cpp/​​文章大意......
  • Qt:QCamera打开系统摄像头,截图保存实例
    ​​          Qt,C++学习交流群:302558294(欢迎你的加入)效果图:动态图太大,这里我就不弄动态图了。用到三个库:#include<QCamera>#include<QCameraImage......
  • Unity调用摄像头拍照及保存图片
    可能大家在使用Unity开发项目的时候,会碰到这样一个需求,就是去调用摄像头拍照或者保存图片,其中摄像头拍照方式也有多种,这里就给大家简单介绍下。usingSystem.Collections;......
  • UML类图、类、接口、聚合、组合的区别
    在UML类图中:类、接口、聚合、组合的UML表示1)类用空心三角实线连接2)接口用空心三角虚线连接3)聚合关系用空心菱形实线连接4)组合用实心菱形实线连接类:表示子类与父类的继承......
  • 在proteus软件80C51芯片隐藏电源接口VCC
    下面是proteus软件中80C51单片机芯片图:很奇怪为什么没有VCC引脚,百度了一下,原来很奇怪为什么没有VCC引脚,百度了一下,原来PROTEUS里面已经默认接上电源了,所以把电源脚隐藏了,所......
  • 接口协议(5) - SMBus
    SMBus(SystemManagementBus),系统管理总线,是一种双线总线,它源自I2C总线,属于I2C总线的子集,用于主板上的低带宽设备进行通信,尤其是与电源相关的芯片,像可充电电源子子系统、......
  • Unity 基础 之 WebCamTexture 获取设备摄像头(包括PC和移动端),简单渲染到 UGUI 或 游戏
    一、简单介绍Unity中的一些基础知识点。本节介绍,使用WebCamTexture 获取设别的摄像头,并且进行渲染,同时解决有可能第一次授权却没有显示画面的情况。 二、实现原理......
  • springboot项目搭建,访问controller接口失败
    错误表现:Thisapplicationhasnoexplicitmappingfor/error,soyouareseeingthisasafallback.在启动类上的注解:@EnableAutoConfiguration解决:使用这个注解......