#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/mman.h>
//ioctl头文件
#include <sys/ioctl.h>
#include <linux/videodev2.h> //V4L2架构有关的头文件
#include "inc/DRMwrap.h"
#include "inc/show_bmp.h"
#include "inc/touch.h"
#define DRM_PATH "/dev/dri/card0"
/*
1.打开DRM设备
2.初始化DRM设备
3.申请一块映射到用户空间的显存
4.把像素值写入到内存地址
5.写完之后要及时刷新DRM设备
6.关闭DRM设备,释放资源
*/
int de = -1;//摄像头打开判断
int cm_bmp[640*480];//获取ARGB一帧数据
int drm_fd;
struct drmHandle drm;
int camerafd;
int *drm_p;
int x=0,y=0;
int pai=0;
struct usraddr *alladdr;
int* drm_init()
{
drm_fd = open(DRM_PATH,O_RDWR | O_CLOEXEC);
if(drm_fd == -1)
{
perror("open drm error");
return NULL;
}
// 初始化 DRM 设备,并获得连接器信息
DRMinit(drm_fd);
bzero(&drm,sizeof(drm));
DRMcreateFB(drm_fd, &drm);
drm_p = (int*)drm.vaddr; //映射到用户空间的显存地址
}
void drm_exit()
{
DRMfreeResources(drm_fd, &drm);
close(drm_fd);
}
/*
使用V4L2架构提供的宏定义和结构体,枚举实现对摄像头的操作
R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U
R = Y + 1.40 *(V-128)
G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
*/
//封装成结构体存放每个缓存块的首地址和长度
struct usraddr
{
int somelength; //缓存的大小
void *someaddr; //缓存的首地址
};
//封装yuv转换成rgb的函数
int yuvtorgb(int y,int u,int v)
{
int point;
//YUYV-->两组RGB
int r,g,b; //定义了rgb三个分量
b = 1164*(y - 16)/1000 + 2018*(u - 128)/1000;
g = 1164*(y - 16)/1000 - 813*(v - 128)/1000 - 391*(u - 128)/1000;
r = 1164*(y - 16)/1000 + 1596*(v - 128)/1000;
//处理值越界的情况
if(r>255)
r=255;
if(g>255)
g=255;
if(b>255)
b=255;
if(r<0)
r=0;
if(g<0)
g=0;
if(b<0)
b=0;
//将rgb转换成ARGB返回
point=0x00<<24|r<<16|g<<8|b;
return point;
}
//封装将一整个画面yuv数据--》rgb数据的函数
int yuvbuftorgbbuf(char *yuvbuf,int *rgbbuf)// 10
{
int i,j;
for(i=0,j=0; j<640*480; i+=4,j+=2)
{
rgbbuf[j]=yuvtorgb(yuvbuf[i],yuvbuf[i+1],yuvbuf[i+3]);
rgbbuf[j+1]=yuvtorgb(yuvbuf[i+2],yuvbuf[i+1],yuvbuf[i+3]);
}
/* yuvtorgb(yuvbuf[0],yuvbuf[1],yuvbuf[3])
yuvtorgb(yuvbuf[2],yuvbuf[1],yuvbuf[3])
yuvtorgb(yuvbuf[4],yuvbuf[5],yuvbuf[7])
yuvtorgb(yuvbuf[6],yuvbuf[5],yuvbuf[7]) */
}
int camera_init()
{
//打开摄像头的驱动
camerafd=open("/dev/video6",O_RDWR);
if(camerafd==-1)
{
perror("打开摄像头的驱动失败!\n");
return -1;
}
//设置摄像头的采集格式
struct v4l2_format myfmt;
bzero(&myfmt,sizeof(myfmt));
myfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
myfmt.fmt.pix.width=640;//可以缩小,但是需要按比例缩小
myfmt.fmt.pix.height=480;
myfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV; //yuv格式
myfmt.fmt.pix.field =V4L2_FIELD_NONE;
int ret=ioctl(camerafd,VIDIOC_S_FMT,&myfmt);
if(ret==-1)
{
perror("设置采集格式失败!\n");
return -1;
}
//申请缓存
struct v4l2_requestbuffers req;
bzero(&req,sizeof(req));
req.count=4; //缓冲块数量,一般1---5之间,一个缓冲块存放一帧画面
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_REQBUFS,&req);
if(ret==-1)
{
perror("申请缓存失败!\n");
return -1;
}
//定义指针分配堆空间存放每个缓存块的信息
alladdr = calloc(4,sizeof(struct usraddr));
//分配你刚才申请的缓存
for(int i=0; i<4; i++)
{
struct v4l2_buffer buf;
bzero(&buf,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=i; //你申请的缓冲块的索引,从0开始
ret=ioctl(camerafd,VIDIOC_QUERYBUF,&buf);
if(ret==-1)
{
perror("分配缓存失败!\n");
return -1;
}
//趁热打铁,马上映射分配得到的某一块缓存
alladdr[i].someaddr=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,buf.m.offset);
if(alladdr[i].someaddr==NULL)
{
perror("映射首地址失败!\n");
return -1;
}
alladdr[i].somelength=buf.length;
}
//入队
for(int i=0; i<4; i++)
{
struct v4l2_buffer buf;
bzero(&buf,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=i; //你申请的缓冲块的索引,从0开始
ret=ioctl(camerafd,VIDIOC_QBUF,&buf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
//开始采集
enum v4l2_buf_type mytype;
mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
if(ret==-1)
{
perror("开始采集失败!\n");
return -1;
}
}
int camera_show()//显示摄像头捕获的画面到开发板
{
//定义指针存放yuv转换得到的完整rgb
int *rgbbuf=malloc(640*480*4);
//定义超时时间1秒
struct timeval mytime;
bzero(&mytime,sizeof(mytime));
mytime.tv_sec=1;
int ret;
//循环入队出队显示视频流,顺便用多路复用检测是否真的开始采集有数据了
while(1) //fd1 fd2 fd3 --》FD_ISSET()
{
if(de == 0)break;
fd_set myset;
FD_ZERO(&myset); // 初始化 myset,清除所有文件描述符
FD_SET(camerafd,&myset);//将文件描述符 fd 添加到 myset 集合中
select(camerafd+1,&myset,NULL,NULL,&mytime);
//检测到有数据了,就立马出队并显示出来
or(int i=0; i<4; i++)
{
struct v4l2_buffer buf;
bzero(&buf,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=i; //你申请的缓冲块的索引,从0开始
ret=ioctl(camerafd,VIDIOC_DQBUF,&buf);
if(ret==-1)
{
perror("出队失败!\n");
return -1;
}
ret=ioctl(camerafd,VIDIOC_QBUF,&buf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
//将出队的画面数据显示在开发板的液晶屏上 yuv格式-->rgb格式显示
yuvbuftorgbbuf(alladdr[i].someaddr,rgbbuf);
//将转换得到的ARGB显示在开发板的液晶屏上
for(int j=0; j<480; j++)
{
memcpy(drm_p+j*1024,&rgbbuf[j*640],640*4);
}
if(pai == 1)
{
pai=0;
for(int i=0; i<640*480; i++)
{
cm_bmp[i]=rgbbuf[i];
}
}
DRMshowUp(drm_fd,&drm);
}
}
}
void *camera_on()
{
static int num = 56 ;
drm_init();//初始化DRM
camera_init();//初始化预处理摄像头
camera_show();//打开摄像头显示画面
pthread_exit(&num);//被返回的地址不允许返回栈空间的内存地址
}
int main()
{
pthread_t tid ;
//创建线程
int lcd_fd = lcd_open();
int a =-1;
while (1)
{
scanf("%d",&a);
if(a==1)
{
if(de == 1)
{
printf("重复打开了\n");
continue;
}
printf("打开摄像头1111111111111\n");
int ret_val = pthread_create( &tid, NULL , camera_on , NULL );
if(ret_val)
{
fprintf( stderr , "线程创建失败:%s\n" , strerror(ret_val));
return -1 ;
}
de = 1;
}
else if(a == 2)
{
if(de == 0)
{
printf("重复关闭了\n");
continue;
}
de = 0;
int * p ;
printf("正在等待子线程退出...\n");
//pthread_join 会修改 指针 p的指向 (因此这里使用的是地址传递)
pthread_join(tid , (void**)&p); //void **retval 阻塞等待子线程退出
printf("子线程已经退出:%d\n" , *p );
drm_exit();//关闭DRM
}
}
lcd_close(lcd_fd);
return 0;
}
在这个代码的基础上,只实现基本功能,完整代码。项目功能:
1.基础功能
a.实现智能安防监控系统(摄像头捕获画面显示在开发板上面)
b.能够通过开发板的触摸屏控制摄像头的开关(画几个按钮,点击按钮实现摄像头开或关)
c.关闭摄像头后实现图片播放