C语言调用摄像头生成avi视频程序
作者:乐山劲松
这篇文章主要为大家详细介绍了C语言如何调用摄像头生成avi视频程序,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下
时间控制是指:生成了n张图片帧用了多少时间m。帧率等于n/m。对应于头文件,m等于scale, n等于rate.为了精确,采用微秒计时。
此程序生成的视频远好于ffmpeg,可能是此程序没有压缩数据原因吧。
现在的帧率不高,是因为只用了一个摄像头缓存区。
avi 头文件
#ifndef AVI_H #define AVI_H #include <stdio.h> //FILE * avi_ks(void); //int avi_add(FILE*fp,char *data,int size); //int avi_end(FILE *f_file); struct avi{ struct riff{ unsigned char id[4]; unsigned int size; unsigned char type[4]; }ri1; struct hdrl{ unsigned char id[4]; //块ID,固定为LIST unsigned int size; //块大小,等于struct avi_hdrl_list去掉id和size的大小 unsigned char type[4]; //块类型,固定为hdrl struct avih{ unsigned char id[4]; //块ID,固定为avih unsigned int size; //块大小,等于struct avi_avih_chunk去掉id和size的大小 unsigned int us_per_frame; //视频帧间隔时间(以微秒为单位) unsigned int max_bytes_per_sec; //AVI文件的最大数据率 unsigned int padding; //设为0即可 unsigned int flags; //AVI文件全局属性,如是否含有索引块、音视频数据是否交叉存储等 unsigned int total_frames; //总帧数 unsigned int init_frames; //为交互格式指定初始帧数(非交互格式应该指定为0) unsigned int streams; //文件包含的流的个数,仅有视频流时为1 unsigned int suggest_buff_size; //指定读取本文件建议使用的缓冲区大小,通常为存储一桢图像 //以及同步声音所需的数据之和,不指定时设为0 unsigned int width; //视频主窗口宽度(单位:像素) unsigned int height; //视频主窗口高度(单位:像素) unsigned int reserved[4]; //保留段,设为0即可 }ah1; struct strl{ unsigned char id[4]; //块ID,固定为LIST unsigned int size; //块大小,等于struct avi_strl_list去掉id和size的大小 unsigned char type[4]; //块类型,固定为strl struct strh{ unsigned char id[4]; //块ID,固定为strh unsigned int size; //块大小,等于struct avi_strh_chunk去掉id和size的大小 unsigned char stream_type[4]; //流的类型,vids表示视频流,auds表示音频流 unsigned char codec[4]; //指定处理这个流需要的解码器,如JPEG unsigned int flags; //标记,如是否允许这个流输出、调色板是否变化等,一般设为0即可 unsigned short priority; //流的优先级,视频流设为0即可 unsigned short language; //音频语言代号,视频流设为0即可 unsigned int init_frames; //为交互格式指定初始帧数(非交互格式应该指定为0) unsigned int scale; // unsigned int rate; //对于视频流,rate / scale = 帧率fps unsigned int start; //对于视频流,设为0即可 unsigned int length; //对于视频流,length即总帧数 unsigned int suggest_buff_size; //读取这个流数据建议使用的缓冲区大小 unsigned int quality; //流数据的质量指标 unsigned int sample_size; //音频采样大小,视频流设为0即可 struct rcFrame{ //这个流在视频主窗口中的显示位置,设为{0,0,width,height}即可 short left; short top; short right; short bottom; } AVI_RECT_FRAME; }sh1; struct strf{ unsigned char id[4]; //块ID,固定为strf unsigned int size; //块大小,等于struct avi_strf_chunk去掉id和size的大小 unsigned int size1; //size1含义和值同size一样 unsigned int width; //视频主窗口宽度(单位:像素) unsigned int height; //视频主窗口高度(单位:像素) unsigned short planes; //始终为1 unsigned short bitcount; //每个像素占的位数,只能是1、4、8、16、24和32中的一个 unsigned char compression[4]; //视频流编码格式,如JPEG、MJPG等 unsigned int image_size; //视频图像大小,等于width * height * bitcount / 8 unsigned int x_pixels_per_meter; //显示设备的水平分辨率,设为0即可 unsigned int y_pixels_per_meter; //显示设备的垂直分辨率,设为0即可 unsigned int num_colors; //含义不清楚,设为0即可 unsigned int imp_colors; //含义不清楚,设为0即可 }sf1; }sl1; }hd1; struct movi{ unsigned char id[4]; unsigned int size; unsigned char type[4]; }movi1; }HEAD; #endif
主程序
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> #include <string.h> #include <sys/mman.h> #include "Avi.h" #include <sys/time.h> #define wid 1280 //摄像头图像宽度 #define hei 720 //图像高度 #define bitlen 24 //图像采样宽度 #define perframe 30 //先预估一帧率,可以为摄像头最大帧率,实际使用时的帧率小于次值 #define jhframe 30 //准备要录像的图片帧数,控制录像的时间长度 static int nframes=0; //总帧数 static int totalsize=0; //总字节数 FILE * avi_ks(void) { FILE *fp = fopen("sample.avi", "w+b"); fseek(fp, sizeof(HEAD), SEEK_SET); return fp; } int avi_add(FILE*fp, char *data, int size) { unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据 fwrite(tmp, 4, 1, fp); //写入是否是压缩的视频数据信息 fwrite(&size, 4, 1, fp); //写入4字节对齐后的JPEG图像大小 fwrite(data, size, 1, fp); //写入真正的JPEG数据 return 0; } //---------------------------------------------------------------------------------- int avi_end(FILE *f_file) { int width = wid; int height = hei; typedef struct hdrl AVI_HDRL_LIST; typedef struct movi AVI_LIST_HEAD; typedef struct avih AVI_AVIH_CHUNK; typedef struct strl AVI_STRL_LIST; typedef struct strh AVI_STRH_CHUNK; typedef struct strf AVI_STRF_CHUNK; typedef struct avi AVI_HEAD; AVI_HEAD avi_head = { { {'R', 'I', 'F', 'F'}, 4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize, {'A', 'V', 'I', ' '} }, { {'L', 'I', 'S', 'T'}, sizeof(AVI_HDRL_LIST) - 8, {'h', 'd', 'r', 'l'}, { {'a', 'v', 'i', 'h'}, sizeof(AVI_AVIH_CHUNK) - 8, 1000000/perframe,width*height*bitlen*perframe/8, 0, 0, nframes, 0, 1,width*height*bitlen/8, width, height, {0, 0, 0, 0} }, { {'L', 'I', 'S', 'T'}, sizeof(AVI_STRL_LIST) - 8, {'s', 't', 'r', 'l'}, { {'s', 't', 'r', 'h'}, sizeof(AVI_STRH_CHUNK) - 8, {'v', 'i', 'd', 's'}, {'J', 'P', 'E', 'G'}, 0, 0, 0, 0, 1, //4750 20000, //20000 0, nframes,width*height*bitlen*perframe/8,10000, 0, {0, 0, width, height} }, { {'s', 't', 'r', 'f'}, sizeof(AVI_STRF_CHUNK) - 8, sizeof(AVI_STRF_CHUNK) - 8, width, height, 1, bitlen, {'J', 'P', 'E', 'G'}, width * height *bitlen/8, 0, 0, 0, 0 } } }, { {'L', 'I', 'S', 'T'}, 4 + nframes * 8 + totalsize, {'m', 'o', 'v', 'i'} } }; fseek(f_file, 0, SEEK_SET); fwrite(&avi_head, sizeof(HEAD), 1, f_file); return 0; } int main(void){ int fd = open("/dev/video0", O_RDWR); if(fd < 0) { perror("打开设备失败"); return -1; } struct v4l2_format vfmt; vfmt.type=1; vfmt.fmt.pix.width=wid; vfmt.fmt.pix.height=hei; vfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_MJPEG; int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt); if(ret < 0) { perror("设置格式失败"); } struct v4l2_requestbuffers reqbuffer; reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuffer.count = 1; reqbuffer.memory = V4L2_MEMORY_MMAP ; ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); if(ret < 0) { perror("申请队列空间失败"); } struct v4l2_buffer mapbuffer; unsigned char *mptr; unsigned int size; mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; mapbuffer.index = 0; ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//查询缓冲区状态 if(ret < 0) { perror("查询内核空间队列失败"); } int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &type); //启动流 if(ret < 0) { perror("开启失败"); } mptr= (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0); //设备映射到缓冲区内存 size=mapbuffer.length; ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer); //把缓冲区数据放入读队列中 if(ret < 0) { perror("放回失败"); } //--------------------------------------------------------------------------- FILE *file=avi_ks(); struct timeval start,end; gettimeofday(&start, NULL ); //微秒记时开始 while(nframes<jhframe){ ret = ioctl(fd, VIDIOC_DQBUF, &mapbuffer); //读当前队列缓冲区的数据 if(ret < 0) { perror("提取数据失败"); } int size=wid*hei*bitlen/8; unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据 fwrite(tmp, 4, 1, file); //写入是否是压缩的视频数据信息 fwrite(&size, 4, 1, file); //写入4字节对齐后的JPEG图像大小 fwrite(mptr,size, 1, file); //写入真正的JPEG数据 ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer); //把缓冲区数据放入读队列中 if(ret < 0) { perror("放回失败"); } nframes++; totalsize++; } static float timeuse1=0; gettimeofday(&end, NULL ); //记时结束 timeuse1 =1000*(1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec); int timeuse=(int)(timeuse1/1000000); //转换为豪秒 //--------------------------------------------------------- ret = ioctl(fd, VIDIOC_STREAMOFF, &type); avi_end(file); int scale=timeuse; int rate=nframes*1000; //重写实际帧率=rate/scale ,乘1000是timeuse 为毫秒,转换为秒 fseek(file,128,SEEK_SET); //128 为scale 在文件中的字节位数(偏移) fwrite(&scale,4,1,file); fwrite(&rate,4,1,file); //rate 为128+4 位 fclose(file); munmap(mptr, size); /* FILE *ss=fopen("sample.avi","rb"); //检验scale ,rate 新值 fseek(ss,0,SEEK_END); int len=ftell(ss); int ff=fileno(ss); char *tf=mmap(NULL,len,PROT_READ,MAP_SHARED,ff,0); memcpy(&HEAD,&tf[0],sizeof(HEAD)); printf("scale:%d\n",HEAD.hd1.sl1.sh1.scale); printf("rate:%d\n",HEAD.hd1.sl1.sh1.rate); printf("end\n"); */ close(fd); return 0; }
到此这篇关于C语言调用摄像头生成avi视频程序的文章就介绍到这了,更多相关C语言生成avi视频内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!