Linux下V4L2框架基于SDL库本地USB摄像头监控
1.摄像头框架编程步骤
(1)打开摄像头设备(/dev/video0 、/dev/video1 )
(2)设置图像格式:VIDIOC_S_FMT(视频捕获格式、图像颜色数据格式、图像宽和高)
(3)申请缓冲区:VIDIOC_REQBUFS(缓冲区数量、缓冲映射方式、视频捕获格式)
(4)将缓冲区映射到进程空间:VIDIOC_QUERYBUF(要映射的缓冲区下标、缓冲映射方式、视频捕获格式)
(5)将缓冲区添加到队列中:VIDIOC_QBUF(映射的缓冲区下标、缓冲映射方式、视频捕获格式)
(6)开启摄像头采集:VIDIOC_STREAMON (视频捕获格式) (7)从采集队列中取出图像数据,通过SDL图像渲染
2.摄像头v4L2框架应用编程示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
struct video
{
int width;//摄像头采集图像宽
int height;//摄像头采集图像高
char *mmapbuf[4];//保存映射的地址
int mmap_size;/*映射缓冲区大小*/
};
/*摄像头应用编程框架*/
int Video_Init(u8 *dev,int video_fd,struct video *video_info)
{
/*1.打开摄像设备文件*/
video_fd=open(dev,O_RDWR);
if(video_fd<0)return -1;
/*2.图像数据格式*/
struct v4l2_format video_format;
memset(&video_format,0,sizeof(struct v4l2_format));
video_format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//捕获格式
video_format.fmt.pix.width=1920;
video_format.fmt.pix.height=1080;
video_format.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
if(ioctl(video_fd,VIDIOC_S_FMT,&video_format))return -2;
video_info->width=video_format.fmt.pix.width;
video_info->height=video_format.fmt.pix.height;
printf("图像尺寸:%d * %d\n",video_info->width,video_info->height);
/*3.申请空间*/
struct v4l2_requestbuffers video_requestbuffers;
memset(&video_requestbuffers,0,sizeof(struct v4l2_requestbuffers));
video_requestbuffers.count=4;//缓冲区个数
video_requestbuffers.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2捕获框架格式
video_requestbuffers.memory=V4L2_MEMORY_MMAP;//内存映射
if(ioctl(video_fd,VIDIOC_REQBUFS,&video_requestbuffers))return -3;
printf("缓冲区个数:%d\n",video_requestbuffers.count);
/*4.将缓冲映射到进程空间*/
int i=0;
struct v4l2_buffer video_buffer;
for(i=0;i<video_requestbuffers.count;i++)
{
memset(&video_buffer,0,sizeof(struct v4l2_buffer));
video_buffer.index=i;//
video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_QUERYBUF,&video_buffer))return -4;
video_info->mmap_size=video_buffer.length;/*映射大小*/
video_info->mmapbuf[i]=mmap(NULL,video_buffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,video_buffer.m.offset);
}
/*5.将缓冲区添加到采集队列*/
for(i=0;i<video_requestbuffers.count;i++)
{
memset(&video_buffer,0,sizeof(struct v4l2_buffer));
video_buffer.index=i;//
video_buffer.memory=V4L2_MEMORY_MMAP;//内存映射
video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_QBUF,&video_buffer))return -5;
}
/*开启摄像头*/
int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2V4L2捕获框架格式
if(ioctl(video_fd,VIDIOC_STREAMON,&type))
{
printf("摄像头开启失败\n");
return -6;
}
return video_fd;
}
3.摄像头采集图像处理线程
/*线程清理函数*/
void pth_routine(void *arg)
{
/*关闭摄像头*/
free(arg);
pthread_mutex_lock(&fastmutex);//互斥锁上锁
pthread_cond_broadcast(&cond);//广播唤醒所有线程
pthread_mutex_unlock(&fastmutex);//互斥锁解锁
video_flag=0;
printf("资源清理完成\n");
}
/*摄像头处理线程*/
void *Video_CollectImage(void *arg)
{
u8 *rgb=malloc(video_info.height*video_info.width*3);//申请图像数据缓冲区
if(rgb==NULL)
{
pthread_exit(NULL);/*结束线程*/
}
pthread_cleanup_push(pth_routine,rgb);
struct pollfd fds;
fds.fd=video_fd;//监听摄像头描述符
fds.events=POLLIN;//读事件
fds.revents=0;
struct v4l2_buffer video_buff;
while(video_flag)
{
poll(&fds,1,-1);
/*1.从队列中取数据*/
memset(&video_buff,0,sizeof(struct v4l2_buffer));
video_buff.memory=V4L2_MEMORY_MMAP;//内存映射
video_buff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//V4L2视频捕获
if(ioctl(video_fd,VIDIOC_DQBUF,&video_buff))break;
/*yuv转RGB*/
yuv_to_rgb(video_info.mmapbuf[video_buff.index],rgb,video_info.width,video_info.height);//颜色数据转换
pthread_mutex_lock(&fastmutex);//互斥锁上锁
memcpy(rgb_buff,rgb,video_info.height*video_info.width*3);
pthread_cond_broadcast(&cond);//广播唤醒所有线程
pthread_mutex_unlock(&fastmutex);//互斥锁解锁
/*3.将缓冲区添加到队列*/
if(ioctl(video_fd,VIDIOC_QBUF,&video_buff))break;
}
pthread_cleanup_pop(1);/*注销清理函数*/
}
4.YUYV(YUV422)转RGB888
/*YUYV转RGB888*/
void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
int x;
int z=0;
unsigned char *ptr = rgb_buffer;
unsigned char *yuyv= yuv_buffer;
for (x = 0; x < iWidth*iHeight; x++)
{
int r, g, b;
int y, u, v;
if (!z)
y = yuyv[0] << 8;
else
y = yuyv[2] << 8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
b = (y + (359 * v)) >> 8;
g = (y - (88 * u) - (183 * v)) >> 8;
r = (y + (454 * u)) >> 8;
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
if(z++)
{
z = 0;
yuyv += 4;
}
}
}
5.主函数main.c
#include <stdio.h>
#include <pthread.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <SDL.h>
#include <SDL_image.h>
#include "video.h"
#define CAMERA_DEV "/dev/video0" //摄像头设备节点
int video_fd;/*摄像头描述符*/
struct video video_info;/*摄像头结构体信息*/
void *Video_CollectImage(void *arg);/*摄像头图像采集*/
u8 *rgb_buff=NULL;
u8 video_flag=1;
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;//互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//条件变量
typedef enum
{
false=0,
true,
}bool;
int main()
{
/*初始化摄像头*/
video_fd=Video_Init(CAMERA_DEV,video_fd,&video_info);
if(video_fd<=0)
{
printf("摄像头初始化失败,res=%d\n",video_fd);
return 0;
}
/*创建窗口 */
SDL_Window *window=SDL_CreateWindow("SDL_VIDEO", SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,800,480,SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_RESIZABLE);
/*创建渲染器*/
SDL_Renderer *render=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
/*清空渲染器*/
SDL_RenderClear(render);
printf("图像尺寸:%d * %d\n",video_info.width,video_info.height);
/*创建纹理*/
SDL_Texture*sdltext=SDL_CreateTexture(render,SDL_PIXELFORMAT_RGB24,SDL_TEXTUREACCESS_STREAMING,video_info.width,video_info.height);
/*创建摄像头采集线程*/
u8 *rgb_data=malloc(video_info.height*video_info.width*3);
rgb_buff=malloc(video_info.height*video_info.width*3);//保存RGB颜色数据
//printf("size=%d\n",video_info.mmap_size);
video_flag=1;/*摄像头采集标志*/
pthread_t pthid;
pthread_create(&pthid,NULL,Video_CollectImage, NULL);
bool quit=true;
SDL_Event event;
SDL_Rect rect;
while(quit)
{
while(SDL_PollEvent(&event))/*事件监测*/
{
if(event.type==SDL_QUIT)/*退出事件*/
{
quit=false;
video_flag=0;
pthread_cancel(pthid);/*杀死指定线程*/
continue;
}
}
if(!video_flag)
{
quit=false;
continue;
}
pthread_mutex_lock(&fastmutex);//互斥锁上锁
pthread_cond_wait(&cond,&fastmutex);
memcpy(rgb_data,rgb_buff,video_info.height*video_info.width*3);
pthread_mutex_unlock(&fastmutex);//互斥锁解锁
SDL_UpdateTexture(sdltext,NULL,rgb_data, video_info.width*3);
//SDL_RenderCopy(render, sdltext, NULL,NULL); // 拷贝纹理到渲染器
SDL_RenderCopyEx(render, sdltext,NULL,NULL,0,NULL,SDL_FLIP_NONE);
SDL_RenderPresent(render); // 渲染
}
SDL_DestroyTexture(sdltext);/*销毁纹理*/
SDL_DestroyRenderer(render);/*销毁渲染器*/
SDL_DestroyWindow(window);/*销毁窗口 */
SDL_Quit();/*关闭SDL*/
pthread_mutex_destroy(&fastmutex);/*销毁互斥锁*/
pthread_cond_destroy(&cond);/*销毁条件变量*/
free(rgb_buff);
free(rgb_data);
}
6.运行效果