• c 实用化的摄像头生成avi视频程序(加入精确的时间控制和随时停止录像功能)


    利用mmap 内存共享,用另一个进程来控制录像循环的退出。甚至添加延时停止录像等功能。

    时间控制是指:生成了n张图片帧用了多少时间m。帧率等于n/m。对应于头文件,m等于scale,  n等于rate.为了精确,采用微秒计时。

    I此程序生成的视频远好于ffmpeg,可能是此程序没有压缩数据原因吧。

    我的摄像头支持 YUYV422 和MJPG 两种格式,YUYV是不压缩的,MJPG是压缩的,查v4l2.h

    头文件得知:YUV422有4种,可任选一种,看到视频采样宽度为16 字节。也就是说每个像素点用16个字节的数据表示图像的亮度,色彩。mjpg  没有说用多少个字节表示一个像素点。我用16 ,24试过都可以用。如果实际观看时帧率不对,则大方向是此值没置错误。

    #define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
    #define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16  YUV 4:2:2     */
    #define V4L2_PIX_FMT_YVYU    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
    #define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16  YUV 4:2:2     */
    #define V4L2_PIX_FMT_VYUY    v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16  YUV 4:2:2     */

    /* compressed formats (压缩格式) */
    #define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */

    MJPG 格式为压缩格式

    这两种支持格式可任选一种,摄像头与avi文件的视频格式必须一致

    也就是程序中  vfmt.fmt.pix.pixelformat    与 strh .codec       strf.compression ,这三者要一致。   

     

    avi 头文件

    1. #ifndef AVI_H
    2. #define AVI_H
    3. #include <stdio.h>
    4. //FILE * avi_ks(void);
    5. //int avi_add(FILE*fp,char *data,int size);
    6. //int avi_end(FILE *f_file);
    7. struct avi{
    8. struct riff{
    9. unsigned char id[4];
    10. unsigned int size;
    11. unsigned char type[4];
    12. }ri1;
    13. struct hdrl{
    14. unsigned char id[4]; //块ID,固定为LIST
    15. unsigned int size; //块大小,等于struct avi_hdrl_list去掉id和size的大小
    16. unsigned char type[4]; //块类型,固定为hdrl
    17. struct avih{
    18. unsigned char id[4]; //块ID,固定为avih
    19. unsigned int size; //块大小,等于struct avi_avih_chunk去掉id和size的大小
    20. unsigned int us_per_frame; //视频帧间隔时间(以微秒为单位)
    21. unsigned int max_bytes_per_sec; //AVI文件的最大数据率
    22. unsigned int padding; //设为0即可
    23. unsigned int flags; //AVI文件全局属性,如是否含有索引块、音视频数据是否交叉存储等
    24. unsigned int total_frames; //总帧数
    25. unsigned int init_frames; //为交互格式指定初始帧数(非交互格式应该指定为0)
    26. unsigned int streams; //文件包含的流的个数,仅有视频流时为1
    27. unsigned int suggest_buff_size; //指定读取本文件建议使用的缓冲区大小,通常为存储一桢图像 //以及同步声音所需的数据之和,不指定时设为0
    28. unsigned int width; //视频主窗口宽度(单位:像素)
    29. unsigned int height; //视频主窗口高度(单位:像素)
    30. unsigned int reserved[4]; //保留段,设为0即可
    31. }ah1;
    32. struct strl{
    33. unsigned char id[4]; //块ID,固定为LIST
    34. unsigned int size; //块大小,等于struct avi_strl_list去掉id和size的大小
    35. unsigned char type[4]; //块类型,固定为strl
    36. struct strh{
    37. unsigned char id[4]; //块ID,固定为strh
    38. unsigned int size; //块大小,等于struct avi_strh_chunk去掉id和size的大小
    39. unsigned char stream_type[4]; //流的类型,vids表示视频流,auds表示音频流
    40. unsigned char codec[4]; //指定处理这个流需要的解码器,如JPEG
    41. unsigned int flags; //标记,如是否允许这个流输出、调色板是否变化等,一般设为0即可
    42. unsigned short priority; //流的优先级,视频流设为0即可
    43. unsigned short language; //音频语言代号,视频流设为0即可
    44. unsigned int init_frames; //为交互格式指定初始帧数(非交互格式应该指定为0)
    45. unsigned int scale; //
    46. unsigned int rate; //对于视频流,rate / scale = 帧率fps
    47. unsigned int start; //对于视频流,设为0即可
    48. unsigned int length; //对于视频流,length即总帧数
    49. unsigned int suggest_buff_size; //读取这个流数据建议使用的缓冲区大小
    50. unsigned int quality; //流数据的质量指标
    51. unsigned int sample_size; //音频采样大小,视频流设为0即可
    52. struct rcFrame{ //这个流在视频主窗口中的显示位置,设为{0,0,width,height}即可
    53. short left;
    54. short top;
    55. short right;
    56. short bottom;
    57. } AVI_RECT_FRAME;
    58. }sh1;
    59. struct strf{
    60. unsigned char id[4]; //块ID,固定为strf
    61. unsigned int size; //块大小,等于struct avi_strf_chunk去掉id和size的大小
    62. unsigned int size1; //size1含义和值同size一样
    63. unsigned int width; //视频主窗口宽度(单位:像素)
    64. unsigned int height; //视频主窗口高度(单位:像素)
    65. unsigned short planes; //始终为1
    66. unsigned short bitcount; //每个像素占的位数,只能是148162432中的一个
    67. unsigned char compression[4]; //视频流编码格式,如JPEG、MJPG等
    68. unsigned int image_size; //视频图像大小,等于width * height * bitcount / 8
    69. unsigned int x_pixels_per_meter; //显示设备的水平分辨率,设为0即可
    70. unsigned int y_pixels_per_meter; //显示设备的垂直分辨率,设为0即可
    71. unsigned int num_colors; //含义不清楚,设为0即可
    72. unsigned int imp_colors; //含义不清楚,设为0即可
    73. }sf1;
    74. }sl1;
    75. }hd1;
    76. struct movi{
    77. unsigned char id[4];
    78. unsigned int size;
    79. unsigned char type[4];
    80. }movi1;
    81. }HEAD;
    82. #endif

    主程序

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <stdlib.h>
    6. #include <unistd.h>
    7. #include <sys/ioctl.h>
    8. #include <linux/videodev2.h>
    9. #include <string.h>
    10. #include <sys/mman.h>
    11. #include "Avi.h"
    12. #include <sys/time.h>
    13. #define wid 1920 //摄像头图像宽度
    14. #define hei 1080 //图像高度
    15. #define bitlen 16 //图像采样宽度 如不正确,会影响帧率,必须设置正确,一般是8,16,24,32 之一,可试
    16. #define perframe 50 //先预估一帧率,可设为摄像头最大帧率,实际使用时的帧率小于次值,主要用于程序设定数据缓冲区大小
    17. #define jhframe 2000 //准备要录像的图片帧数,控制录像的时间长度
    18. #define HCQ 10 //v4l2 缓冲区个数
    19. #define vcodes {'M','J','P','G'}
    20. //#define vcodes {'Y','U','Y','V'}
    21. static int nframes = 0; //总帧数
    22. static int totalsize = 0; //总字节数
    23. FILE * avi_ks(void) {
    24. chdir("/home/wzpc/Videos/");
    25. FILE *fp = fopen("sample.avi", "w+b");
    26. fseek(fp, sizeof(HEAD), SEEK_SET);
    27. return fp;
    28. }
    29. int avi_add(FILE*fp, char *data, int size) {
    30. unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据00db:未压缩
    31. fwrite(tmp, 4, 1, fp); //写入是否是压缩的视频数据信息
    32. fwrite(&size, 4, 1, fp); //写入4字节对齐后的JPEG图像大小
    33. fwrite(data, size, 1, fp); //写入真正的JPEG数据
    34. return 0;
    35. }
    36. //----------------------------------------------------------------------------------
    37. int avi_end(FILE *f_file) {
    38. int width = wid;
    39. int height = hei;
    40. typedef struct hdrl AVI_HDRL_LIST;
    41. typedef struct movi AVI_LIST_HEAD;
    42. typedef struct avih AVI_AVIH_CHUNK;
    43. typedef struct strl AVI_STRL_LIST;
    44. typedef struct strh AVI_STRH_CHUNK;
    45. typedef struct strf AVI_STRF_CHUNK;
    46. typedef struct avi AVI_HEAD;
    47. AVI_HEAD avi_head = {
    48. {
    49. {'R', 'I', 'F', 'F'},
    50. 4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,
    51. {'A', 'V', 'I', ' '}
    52. },
    53. {
    54. {'L', 'I', 'S', 'T'},
    55. sizeof(AVI_HDRL_LIST) - 8,
    56. {'h', 'd', 'r', 'l'},
    57. {
    58. {'a', 'v', 'i', 'h'},
    59. sizeof(AVI_AVIH_CHUNK) - 8,
    60. 1000000 / perframe, width*height*bitlen*perframe / 8, 0, 0, nframes,
    61. 0, 1, width*height*bitlen / 8, width, height,
    62. {0, 0, 0, 0}
    63. },
    64. {
    65. {'L', 'I', 'S', 'T'},
    66. sizeof(AVI_STRL_LIST) - 8,
    67. {'s', 't', 'r', 'l'},
    68. {
    69. {'s', 't', 'r', 'h'},
    70. sizeof(AVI_STRH_CHUNK) - 8,
    71. {'v', 'i', 'd', 's'},
    72. vcodes,
    73. // {'J','P','E','G'},
    74. // {'Y', 'U', 'Y', 'V'},
    75. 0, 0, 0, 0,
    76. 1, //4750
    77. perframe, //20000
    78. 0, nframes, width*height*bitlen*perframe / 8, 10000, 0,
    79. {0, 0, width, height}
    80. },
    81. {
    82. {'s', 't', 'r', 'f'},
    83. sizeof(AVI_STRF_CHUNK) - 8,
    84. sizeof(AVI_STRF_CHUNK) - 8,
    85. width, height, 1,
    86. bitlen,
    87. vcodes,
    88. // {'J', 'P', 'E', 'G'},
    89. // {'Y','U','Y','V'},
    90. width * height *bitlen / 8, 0, 0, 0, 0
    91. }
    92. }
    93. },
    94. {
    95. {'L', 'I', 'S', 'T'},
    96. 4 + nframes * 8 + totalsize,
    97. {'m', 'o', 'v', 'i'}
    98. }
    99. };
    100. fseek(f_file, 0, SEEK_SET);
    101. fwrite(&avi_head, sizeof(HEAD), 1, f_file);
    102. return 0;
    103. }
    104. int main(void) {
    105. int fd1=open("/home/wzpc/1.txt",O_RDWR); //进程mmap共享,用于控制录像的停止
    106. char *p=mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd1,0);
    107. // memcpy(p,&bz,4);
    108. int fd = open("/dev/video0", O_RDWR);
    109. if (fd < 0) {
    110. perror("打开设备失败");
    111. return -1;
    112. }
    113. struct v4l2_format vfmt;
    114. vfmt.type = 1;
    115. vfmt.fmt.pix.width = wid;
    116. vfmt.fmt.pix.height = hei;
    117. vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    118. // vfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV; //重要 MJPG YUYV422
    119. // vfmt.fmt.pix.pixelformat=v4l2_fourcc('Y','U','Y','V');
    120. int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
    121. if (ret < 0) {
    122. perror("设置格式失败");
    123. }
    124. struct v4l2_requestbuffers reqbuffer;
    125. reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    126. reqbuffer.count = HCQ;
    127. reqbuffer.memory = V4L2_MEMORY_MMAP ;
    128. ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
    129. if (ret < 0) {
    130. perror("申请队列空间失败");
    131. }
    132. //我理解以下部分
    133. struct v4l2_buffer mapbuffer; //相当于在为mapbuffer 设置读写规则,还没有开始数据传输,怎样从摄像头buf传输数据保存在多少个mmap中
    134. mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    135. unsigned char *mptr[HCQ];
    136. unsigned size[HCQ];
    137. for (int t = 0; t < HCQ; t++) {
    138. mapbuffer.index = t;
    139. ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//读摄像头缓冲区数据,为映射mmap准备
    140. if(ret < 0)
    141. {
    142. perror("查询内核空间队列失败");
    143. }
    144. mptr[t] = mmap(NULL, mapbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset); //摄像头缓冲区映射到内存
    145. size[t] = mapbuffer.length;
    146. ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer); //释放一个buf缓冲区空间,摄像头可以为此buf写入数据
    147. if (ret < 0) {
    148. perror("放回失败");
    149. }
    150. }
    151. //--------------------------我的理解:以上都是设置---------------------------------------------
    152. //============================================================================================
    153. int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    154. ret = ioctl(fd, VIDIOC_STREAMON, &type); //启动流,数据开始传输
    155. if (ret < 0) {
    156. perror("开启失败");
    157. }
    158. //---------------------------------------------------------------------------
    159. FILE *file = avi_ks();
    160. struct v4l2_buffer readbuffer;
    161. readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //安装mapbuffer设置的规矩来读,数据从mmap中提取
    162. struct timeval start, end;
    163. gettimeofday(&start, NULL ); //微秒记时开始
    164. int bz=1;
    165. while ((nframes < jhframe)&&(bz)) { //开另一进程控制录像停止
    166. puts("录像中.....");
    167. ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer); //读当前队列缓冲区的数据 index循环+1,VIDIOC_DQBUF执行一次,index1
    168. if (ret < 0) {
    169. perror("提取数据失败");
    170. }
    171. int i=readbuffer.index;
    172. // printf("%d\n",mapbuffer.index); //验证index的循环变化
    173. int len = readbuffer.length; //每一个mapbuff等于一帧字节,每一帧的字节数都是相同的
    174. // int len=wid*hei*bitlen/8;
    175. unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据
    176. fwrite(tmp, 4, 1, file);
    177. fwrite(&len, 4, 1, file);
    178. fwrite(mptr[i],len, 1, file); //写入真正的JPEG数据
    179. ret = ioctl(fd, VIDIOC_QBUF, &readbuffer); //把缓冲区数据放入读队列中
    180. if (ret < 0) {
    181. perror("放回失败");
    182. }
    183. nframes++;
    184. totalsize++;
    185. memcpy(&bz,p,4);
    186. }
    187. static float timeuse1 = 0;
    188. gettimeofday(&end, NULL ); //记时结束
    189. timeuse1 = 1000 * (1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec);
    190. int timeuse = (int)(timeuse1 / 1000000); //转换为豪秒
    191. //---------------------------------------------------------
    192. ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    193. avi_end(file);
    194. int scale = timeuse;
    195. int rate = nframes * 1000; //重写实际帧率=rate/scale ,乘1000是timeuse 为毫秒,转换为秒
    196. printf("帧率:%d\n", rate / timeuse);
    197. fseek(file, 128, SEEK_SET); //128 为scale 在文件中的字节位数(偏移)
    198. fwrite(&scale, 4, 1, file);
    199. fwrite(&rate, 4, 1, file); //rate 为128+4
    200. fclose(file);
    201. for (int n = 0; n < HCQ; n++) {
    202. munmap(mptr, size[n]);
    203. }
    204. int kz1=1; //控制位复位1
    205. memcpy(p,&kz1,4);
    206. munmap(p,4);
    207. close(fd);
    208. puts("end");
    209. return 0;
    210. }

    3.停止录像进程

    1. #include <sys/mman.h>
    2. #include <stdio.h>
    3. #include <string.h>
    4. #include <fcntl.h>
    5. #include <unistd.h>
    6. int main(void){
    7. int fd=open("/home/wzpc/1.txt",O_RDWR);
    8. char *p=mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    9. puts("start:t=0\n");
    10. int t=0;
    11. memcpy(p,&t,4);
    12. return 0;
    13. }

     

     

     

     

     

  • 相关阅读:
    使用mysql语句进行分组查询
    MMdetection训练配置
    昨天同事lastday,把前端测试包扔线上去了
    最详解决:jupyter notebook不会自动打开浏览器问题
    XXL-JOB 分布式任务调度中心搭建
    js中使用原型链增加方法后,遍历对象的key-value时会遍历出方法
    python 控制包是否可导入
    Flutter组件--AppBar相关属性
    基于主动视觉机制的深度学习--一个综合池化框架
    TPS63020DSJR(LDO)NCP51200MNTXG IC REG 稳压器参数
  • 原文地址:https://blog.csdn.net/m0_59802969/article/details/134374822