• (转)c 多张图片生成avi视频


    https://www.cnblogs.com/songhe364826110/p/7619949.html

    修改了几个参数,可以生成视频了。下载主要是为了学习avi视频格式。最后编一个摄像头生成视频的程序。

    本程序把标准avi 视频格式的各种数据结构自定义在文件头(JpegAVI.h)中,所以就不用去下载借用ffmpeg,mplayer等的头文件,库文件了。现在发现,其实编写程序不难,难的是理解定义的各种数据结构和怎样按avi协议去用这些定义的结构。

    现在有点明白了为什么学编程要数学好,数学不好就想不出这些数据结构和怎样构建这些文件体系。就象现在虽然有些c的编程知识,可什么也干不了,解决不了实际问题。想创新是不可能的,只有复制别人的成果。

    图片文件名为数字,从0开始,不含后缀。

    main.c

    1. #include "Jpeg2AVI.h"
    2. #include <string.h>
    3. #define JPEG_MAX_SIZE 2000000 //JPEG图像最大字节数,这个数必须大于每一张图片的字节数
    4. #define JPEG_NUM 59 //JPEG图像数量 这个数必须小于等于实际文件数
    5. int main()
    6. {
    7. FILE *fp_jpg;
    8. FILE *fp_avi;
    9. int filesize;
    10. unsigned char jpg_data[JPEG_MAX_SIZE];
    11. char filename[10];
    12. int i = 0;
    13. fp_avi = fopen("sample.avi","wb");
    14. jpeg2avi_start(fp_avi);
    15. for (i = 0; i < JPEG_NUM; i++)
    16. {
    17. memset(filename, 0, 10);
    18. memset(jpg_data, 0, JPEG_MAX_SIZE);
    19. sprintf(filename, "%d", i); //int转字符
    20. fp_jpg = fopen(filename, "rb");
    21. if (fp_jpg != NULL)
    22. {
    23. /*获取JPEG数据大小*/
    24. fseek(fp_jpg, 0, SEEK_END);
    25. filesize = ftell(fp_jpg);
    26. fseek(fp_jpg, 0, SEEK_SET);
    27. /*将JPEG数据读到缓冲区*/
    28. fread(jpg_data, filesize, 1, fp_jpg);
    29. /*将JPEG数据写入AVI文件*/
    30. jpeg2avi_add_frame(fp_avi, jpg_data, filesize);
    31. }
    32. fclose(fp_jpg);
    33. }
    34. jpeg2avi_end(fp_avi, 1280, 720,1);
    35. fclose(fp_avi);
    36. printf("end\n");
    37. return 0;
    38. }

    Jpeg2AVI.h

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

    Jepg2AVI.c

    1. #include "Jpeg2AVI.h"
    2. #include "list.h"
    3. #include <stdlib.h>
    4. #include <string.h>
    5. static int nframes; //总帧数
    6. static int totalsize; //帧的总大小
    7. static struct list_head list; //保存各帧图像大小的链表,用于写索引块
    8. /*链表宿主结构,用于保存真正的图像大小数据*/
    9. struct ListNode
    10. {
    11. int value;
    12. struct list_head head;
    13. };
    14. static void write_index_chunk(FILE *fp)
    15. {
    16. unsigned char index[4] = {'i', 'd', 'x', '1'}; //索引块ID
    17. unsigned int index_chunk_size = 16 * nframes; //索引块大小
    18. unsigned int offset = 4;
    19. struct list_head *slider = NULL;
    20. struct list_head *tmpslider = NULL;
    21. fwrite(index, 4, 1, fp);
    22. fwrite(&index_chunk_size, 4, 1, fp);
    23. list_for_each_safe(slider, tmpslider, &list)
    24. {
    25. unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据
    26. unsigned int keyframe = 0x10; //0x10表示当前帧为关键帧
    27. struct ListNode *node = list_entry(slider, struct ListNode, head);
    28. fwrite(tmp, 4, 1, fp);
    29. fwrite(&keyframe, 4, 1, fp);
    30. fwrite(&offset, 4, 1, fp);
    31. fwrite(&node->value, 4, 1, fp);
    32. offset = offset + node->value + 8;
    33. list_del(slider);
    34. free(node);
    35. }
    36. }
    37. static void back_fill_data(FILE *fp, int width, int height, int fps)
    38. {
    39. AVI_RIFF_HEAD riff_head =
    40. {
    41. {'R', 'I', 'F', 'F'},
    42. 4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,
    43. {'A', 'V', 'I', ' '}
    44. };
    45. AVI_HDRL_LIST hdrl_list =
    46. {
    47. {'L', 'I', 'S', 'T'},
    48. sizeof(AVI_HDRL_LIST) - 8,
    49. {'h', 'd', 'r', 'l'},
    50. {
    51. {'a', 'v', 'i', 'h'},
    52. sizeof(AVI_AVIH_CHUNK) - 8,
    53. 1000000 / fps, 25000, 0, 0, nframes, 0, 1, 100000, width, height,
    54. {0, 0, 0, 0}
    55. },
    56. {
    57. {'L', 'I', 'S', 'T'},
    58. sizeof(AVI_STRL_LIST) - 8,
    59. {'s', 't', 'r', 'l'},
    60. {
    61. {'s', 't', 'r', 'h'},
    62. sizeof(AVI_STRH_CHUNK) - 8,
    63. {'v', 'i', 'd', 's'},
    64. {'J', 'P', 'E', 'G'},
    65. 0, 0, 0, 0, 1, 23, 0, nframes, 100000, 0xFFFFFF, 0,
    66. {0, 0, width, height}
    67. },
    68. {
    69. {'s', 't', 'r', 'f'},
    70. sizeof(AVI_STRF_CHUNK) - 8,
    71. sizeof(AVI_STRF_CHUNK) - 8,
    72. width, height, 1, 24,
    73. {'J', 'P', 'E', 'G'},
    74. width * height * 3, 0, 0, 0, 0
    75. }
    76. }
    77. };
    78. AVI_LIST_HEAD movi_list_head =
    79. {
    80. {'L', 'I', 'S', 'T'},
    81. 4 + nframes * 8 + totalsize,
    82. {'m', 'o', 'v', 'i'}
    83. };
    84. //定位到文件头,回填各块数据
    85. fseek(fp, 0, SEEK_SET);
    86. fwrite(&riff_head, sizeof(riff_head), 1, fp);
    87. fwrite(&hdrl_list, sizeof(hdrl_list), 1, fp);
    88. fwrite(&movi_list_head, sizeof(movi_list_head), 1, fp);
    89. }
    90. void jpeg2avi_start(FILE *fp)
    91. {
    92. int offset1 = sizeof(AVI_RIFF_HEAD); //riff head大小
    93. int offset2 = sizeof(AVI_HDRL_LIST); //hdrl list大小
    94. int offset3 = sizeof(AVI_LIST_HEAD); //movi list head大小
    95. //AVI文件偏移量设置到movi list head后,从该位置向后依次写入JPEG数据
    96. fseek(fp, offset1 + offset2 + offset3, SEEK_SET);
    97. //初始化链表
    98. list_head_init(&list);
    99. nframes = 0;
    100. totalsize = 0;
    101. }
    102. void jpeg2avi_add_frame(FILE *fp, void *data, unsigned int len)
    103. {
    104. unsigned char tmp[4] = {'0', '0', 'd', 'c'}; //00dc = 压缩的视频数据
    105. struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
    106. /*JPEG图像大小4字节对齐*/
    107. while (len % 4)
    108. {
    109. len++;
    110. }
    111. fwrite(tmp, 4, 1, fp); //写入是否是压缩的视频数据信息
    112. fwrite(&len, 4, 1, fp); //写入4字节对齐后的JPEG图像大小
    113. fwrite(data, len, 1, fp); //写入真正的JPEG数据
    114. nframes += 1;
    115. totalsize += len;
    116. /*4字节对齐后的JPEG图像大小保存在链表中*/
    117. if (node != NULL)
    118. {
    119. node->value = len;
    120. list_add_tail(&node->head, &list);
    121. }
    122. }
    123. void jpeg2avi_end(FILE *fp, int width, int height, int fps)
    124. {
    125. //写索引块
    126. write_index_chunk(fp);
    127. //从文件头开始,回填各块数据
    128. back_fill_data(fp, width, height, fps);
    129. }

    list.h

    1. #ifndef _LIST_H_
    2. #define _LIST_H_
    3. struct list_head
    4. {
    5. struct list_head *next;
    6. struct list_head *prev;
    7. };
    8. void list_head_init(struct list_head *list);
    9. void list_add_tail(struct list_head *_new, struct list_head *head);
    10. void list_del(struct list_head *entry);
    11. #ifndef offsetof
    12. #define offsetof(TYPE, MEMBER) \
    13. ((size_t) &((TYPE *)0)->MEMBER)
    14. #endif
    15. #ifndef container_of
    16. #define container_of(ptr, type, member) \
    17. ((type *)((char *)ptr - offsetof(type,member)))
    18. #endif
    19. /**
    20. * list_entry - get the struct for this entry
    21. * @ptr: the &struct list_head pointer.
    22. * @type: the type of the struct this is embedded in.
    23. * @member: the name of the list_struct within the struct.
    24. */
    25. #define list_entry(ptr, type, member) \
    26. container_of(ptr, type, member)
    27. /**
    28. * list_for_each_safe - iterate over a list safe against removal of list entry
    29. * @pos: the &struct list_head to use as a loop cursor.
    30. * @n: another &struct list_head to use as temporary storage
    31. * @head: the head for your list.
    32. */
    33. #define list_for_each_safe(pos, n, head) \
    34. for (pos = (head)->next, n = pos->next; pos != (head); \
    35. pos = n, n = pos->next)
    36. #endif //_LIST_H_

    list.c

    1. #include "list.h"
    2. #include <stdio.h>
    3. static void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next)
    4. {
    5. next->prev = _new;
    6. _new->next = next;
    7. _new->prev = prev;
    8. prev->next = _new;
    9. }
    10. static void __list_del(struct list_head *prev, struct list_head *next)
    11. {
    12. next->prev = prev;
    13. prev->next = next;
    14. }
    15. void list_head_init(struct list_head *list)
    16. {
    17. list->next = list;
    18. list->prev = list;
    19. }
    20. /**
    21. * list_add_tail - insert a new entry before the specified head
    22. * @_new: new entry to be added
    23. * @head: list head to add it before
    24. */
    25. void list_add_tail(struct list_head *_new, struct list_head *head)
    26. {
    27. __list_add(_new, head->prev, head);
    28. }
    29. /**
    30. * list_del - deletes entry from list.
    31. * @entry: the element to delete from the list.
    32. */
    33. void list_del(struct list_head *entry)
    34. {
    35. __list_del(entry->prev, entry->next);
    36. entry->next = NULL;
    37. entry->prev = NULL;
    38. }

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    php接口api数据签名及验签
    C++ string replace操作
    Github每日精选(第33期):Screenshot-to-code训练 AI 将设计模型转换为 HTML 和 CSS
    网络安全(黑客)自学
    本机号码一键登录原理与应用(荣耀典藏版)
    【python】如何注释
    MySQL的数据类型详解
    下一代Docker来了,会让部署更加丝滑吗?
    Mysql 45讲学习笔记(十二)MySQL会“抖”一下
    Primer笔记——typedef指针类型别名时的const陷阱
  • 原文地址:https://blog.csdn.net/m0_59802969/article/details/133949394