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

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

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

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

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

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

main.c


#include "Jpeg2AVI.h"
#include <string.h>#define JPEG_MAX_SIZE 2000000   //JPEG图像最大字节数,这个数必须大于每一张图片的字节数
#define JPEG_NUM 59  //JPEG图像数量 这个数必须小于等于实际文件数int main()
{FILE *fp_jpg;FILE *fp_avi;int filesize;unsigned char jpg_data[JPEG_MAX_SIZE];      char filename[10];int i = 0;fp_avi = fopen("sample.avi","wb");jpeg2avi_start(fp_avi);for (i = 0; i < JPEG_NUM; i++){memset(filename, 0, 10);memset(jpg_data, 0, JPEG_MAX_SIZE);sprintf(filename, "%d", i);  //int转字符fp_jpg = fopen(filename, "rb");if (fp_jpg != NULL){/*获取JPEG数据大小*/fseek(fp_jpg, 0, SEEK_END);filesize = ftell(fp_jpg);fseek(fp_jpg, 0, SEEK_SET);/*将JPEG数据读到缓冲区*/fread(jpg_data, filesize, 1, fp_jpg);/*将JPEG数据写入AVI文件*/jpeg2avi_add_frame(fp_avi, jpg_data, filesize);}fclose(fp_jpg);}jpeg2avi_end(fp_avi, 1280, 720,1);fclose(fp_avi);printf("end\n");return 0;
}

Jpeg2AVI.h

#ifndef _JPEG2AVI_H_
#define _JPEG2AVI_H_#include <stdio.h>void jpeg2avi_start(FILE *fp);
void jpeg2avi_add_frame(FILE *fp, void *data, unsigned int len);
void jpeg2avi_end(FILE *fp, int width, int height, int fps);typedef struct avi_riff_head
{unsigned char id[4];unsigned int size;unsigned char type[4];
}AVI_RIFF_HEAD, AVI_LIST_HEAD;typedef struct avi_avih_chunk
{unsigned char id[4];            //块ID,固定为avihunsigned 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;           //文件包含的流的个数,仅有视频流时为1unsigned int suggest_buff_size; //指定读取本文件建议使用的缓冲区大小,通常为存储一桢图像                                            //以及同步声音所需的数据之和,不指定时设为0unsigned int width;             //视频主窗口宽度(单位:像素)unsigned int height;            //视频主窗口高度(单位:像素)unsigned int reserved[4];       //保留段,设为0即可
}AVI_AVIH_CHUNK;typedef struct avi_rect_frame
{short left;short top;short right;short bottom;
}AVI_RECT_FRAME;typedef struct avi_strh_chunk
{unsigned char id[4];            //块ID,固定为strhunsigned int size;              //块大小,等于struct avi_strh_chunk去掉id和size的大小unsigned char stream_type[4];   //流的类型,vids表示视频流,auds表示音频流unsigned char codec[4];         //指定处理这个流需要的解码器,如JPEGunsigned int flags;             //标记,如是否允许这个流输出、调色板是否变化等,一般设为0即可unsigned short priority;        //流的优先级,视频流设为0即可unsigned short language;        //音频语言代号,视频流设为0即可unsigned int init_frames;       //为交互格式指定初始帧数(非交互格式应该指定为0)unsigned int scale;             //unsigned int rate;              //对于视频流,rate / scale = 帧率fpsunsigned int start;             //对于视频流,设为0即可unsigned int length;            //对于视频流,length即总帧数unsigned int suggest_buff_size; //读取这个流数据建议使用的缓冲区大小unsigned int quality;           //流数据的质量指标unsigned int sample_size;       //音频采样大小,视频流设为0即可AVI_RECT_FRAME rcFrame;         //这个流在视频主窗口中的显示位置,设为{0,0,width,height}即可
}AVI_STRH_CHUNK;/*对于视频流,strf块结构如下*/
typedef struct avi_strf_chunk
{unsigned char id[4];             //块ID,固定为strfunsigned int size;               //块大小,等于struct avi_strf_chunk去掉id和size的大小unsigned int size1;              //size1含义和值同size一样unsigned int width;              //视频主窗口宽度(单位:像素)unsigned int height;             //视频主窗口高度(单位:像素)unsigned short planes;           //始终为1unsigned short bitcount;         //每个像素占的位数,只能是1、4、8、16、24和32中的一个unsigned char compression[4];    //视频流编码格式,如JPEG、MJPG等unsigned int image_size;         //视频图像大小,等于width * height * bitcount / 8unsigned int x_pixels_per_meter; //显示设备的水平分辨率,设为0即可unsigned int y_pixels_per_meter; //显示设备的垂直分辨率,设为0即可unsigned int num_colors;         //含义不清楚,设为0即可unsigned int imp_colors;         //含义不清楚,设为0即可
}AVI_STRF_CHUNK;typedef struct avi_strl_list
{unsigned char id[4];    //块ID,固定为LISTunsigned int size;      //块大小,等于struct avi_strl_list去掉id和size的大小unsigned char type[4];  //块类型,固定为strlAVI_STRH_CHUNK strh;AVI_STRF_CHUNK strf;
}AVI_STRL_LIST;typedef struct avi_hdrl_list
{unsigned char id[4];    //块ID,固定为LISTunsigned int size;      //块大小,等于struct avi_hdrl_list去掉id和size的大小unsigned char type[4];  //块类型,固定为hdrlAVI_AVIH_CHUNK avih;AVI_STRL_LIST  strl;
}AVI_HDRL_LIST;#endif

Jepg2AVI.c

#include "Jpeg2AVI.h"
#include "list.h"
#include <stdlib.h>
#include <string.h>static int nframes;           //总帧数
static int totalsize;         //帧的总大小
static struct list_head list; //保存各帧图像大小的链表,用于写索引块/*链表宿主结构,用于保存真正的图像大小数据*/
struct ListNode
{int value;struct list_head head;
};static void write_index_chunk(FILE *fp)
{unsigned char index[4] = {'i', 'd', 'x', '1'};  //索引块IDunsigned int index_chunk_size = 16 * nframes;   //索引块大小unsigned int offset = 4;struct list_head *slider = NULL;struct list_head *tmpslider = NULL;fwrite(index, 4, 1, fp);fwrite(&index_chunk_size, 4, 1, fp);list_for_each_safe(slider, tmpslider, &list){unsigned char tmp[4] = {'0', '0', 'd', 'c'};  //00dc = 压缩的视频数据unsigned int keyframe = 0x10;                 //0x10表示当前帧为关键帧struct ListNode *node = list_entry(slider, struct ListNode, head);fwrite(tmp, 4, 1, fp);fwrite(&keyframe, 4, 1, fp);fwrite(&offset, 4, 1, fp);fwrite(&node->value, 4, 1, fp);offset = offset + node->value + 8;list_del(slider);free(node);}
}static void back_fill_data(FILE *fp, int width, int height, int fps)
{AVI_RIFF_HEAD riff_head ={{'R', 'I', 'F', 'F'},4 + sizeof(AVI_HDRL_LIST) + sizeof(AVI_LIST_HEAD) + nframes * 8 + totalsize,{'A', 'V', 'I', ' '}};AVI_HDRL_LIST hdrl_list ={{'L', 'I', 'S', 'T'},sizeof(AVI_HDRL_LIST) - 8,{'h', 'd', 'r', 'l'},{{'a', 'v', 'i', 'h'},sizeof(AVI_AVIH_CHUNK) - 8,1000000 / fps, 25000, 0, 0, nframes, 0, 1, 100000, 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, 23, 0, nframes, 100000, 0xFFFFFF, 0,{0, 0, width, height}},{{'s', 't', 'r', 'f'},sizeof(AVI_STRF_CHUNK) - 8,sizeof(AVI_STRF_CHUNK) - 8,width, height, 1, 24,{'J', 'P', 'E', 'G'},width * height * 3, 0, 0, 0, 0}}};AVI_LIST_HEAD movi_list_head ={{'L', 'I', 'S', 'T'},4 + nframes * 8 + totalsize,{'m', 'o', 'v', 'i'}};//定位到文件头,回填各块数据fseek(fp, 0, SEEK_SET);fwrite(&riff_head, sizeof(riff_head), 1, fp);fwrite(&hdrl_list, sizeof(hdrl_list), 1, fp);fwrite(&movi_list_head, sizeof(movi_list_head), 1, fp);
}void jpeg2avi_start(FILE *fp)
{int offset1 = sizeof(AVI_RIFF_HEAD);  //riff head大小int offset2 = sizeof(AVI_HDRL_LIST);  //hdrl list大小int offset3 = sizeof(AVI_LIST_HEAD);  //movi list head大小//AVI文件偏移量设置到movi list head后,从该位置向后依次写入JPEG数据fseek(fp, offset1 + offset2 + offset3, SEEK_SET);//初始化链表list_head_init(&list);nframes = 0;totalsize = 0;
}void jpeg2avi_add_frame(FILE *fp, void *data, unsigned int len)
{unsigned char tmp[4] = {'0', '0', 'd', 'c'};  //00dc = 压缩的视频数据struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));/*JPEG图像大小4字节对齐*/while (len % 4){len++;}fwrite(tmp, 4, 1, fp);    //写入是否是压缩的视频数据信息fwrite(&len, 4, 1, fp);   //写入4字节对齐后的JPEG图像大小fwrite(data, len, 1, fp); //写入真正的JPEG数据nframes += 1;totalsize += len;/*将4字节对齐后的JPEG图像大小保存在链表中*/if (node != NULL){node->value = len;list_add_tail(&node->head, &list);}
}void jpeg2avi_end(FILE *fp, int width, int height, int fps)
{//写索引块write_index_chunk(fp);//从文件头开始,回填各块数据back_fill_data(fp, width, height, fps);
}

list.h

#ifndef _LIST_H_
#define _LIST_H_struct list_head
{struct list_head *next;struct list_head *prev;
};void list_head_init(struct list_head *list);
void list_add_tail(struct list_head *_new, struct list_head *head);
void list_del(struct list_head *entry);#ifndef offsetof
#define offsetof(TYPE, MEMBER) \
((size_t) &((TYPE *)0)->MEMBER)
#endif#ifndef container_of
#define container_of(ptr, type, member) \
((type *)((char *)ptr - offsetof(type,member)))
#endif/*** list_entry - get the struct for this entry* @ptr:    the &struct list_head pointer.* @type:    the type of the struct this is embedded in.* @member:    the name of the list_struct within the struct.*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)/*** list_for_each_safe - iterate over a list safe against removal of list entry* @pos:    the &struct list_head to use as a loop cursor.* @n:        another &struct list_head to use as temporary storage* @head:    the head for your list.*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)#endif //_LIST_H_

list.c

#include "list.h"
#include <stdio.h>static void __list_add(struct list_head *_new, struct list_head *prev, struct list_head *next)
{next->prev = _new;_new->next = next;_new->prev = prev;prev->next = _new;
}static void __list_del(struct list_head *prev, struct list_head *next)
{next->prev = prev;prev->next = next;
}void list_head_init(struct list_head *list)
{list->next = list;list->prev = list;
}/*** list_add_tail - insert a new entry before the specified head* @_new: new entry to be added* @head: list head to add it before*/
void list_add_tail(struct list_head *_new, struct list_head *head)
{__list_add(_new, head->prev, head);
}/*** list_del - deletes entry from list.* @entry: the element to delete from the list.*/
void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = NULL;entry->prev = NULL;
}

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/166776.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【设计模式】解释器模式

文章目录 1.解释器模式定义2.解释器模式的角色3.解释器模式实战案例3.1.场景说明3.2.结构类图3.3.代码实现 4.解释器模式优缺点5.解释器模式适用场景6.解释器模式总结 主页传送门&#xff1a;&#x1f481; 传送 1.解释器模式定义 解析器模式&#xff08;Interpreter Pattern&a…

网络解析(二)

ICMP 报文有很多的类型,不同的类型有不同的代码。最常用的类型是主动请求为 8,主动请求的应答为 0。 ICMP 相当于网络世界的侦察兵。我讲了两种类型的 ICMP 报文,一种是主动探查的查询报文,一种异常报告的差错报文; ping 使用查询报文,Traceroute 使用差错报文。 IP和…

进程(1)——什么是进程?【linux】

进程&#xff08;1&#xff09;——什么是进程&#xff1f;【linux】 一. 什么是进程&#xff1f;二. 管理进程&#xff1a;2.1 怎么管理&#xff1a;2.2 PCB2.3.1 task_struct2.3.2 组织task_struct&#xff1a; 三.查看进程3.1 ps ajx3.2 ls /proc 四. 父子进程4.1 什么是父子…

数据结构——三路划分(快排优化)

刷Leetcode时遇到的问题&#xff0c;用普通的快排去跑&#xff0c;发现有问题。 普通的Hoare或者其他的快排好像都没有直接解决掉这个问题&#xff0c;当一个数重复出现的时候&#xff0c;用普通的快排效率其实并没有那么高。所以&#xff0c;这也是普通快排的缺点之一。 所以&…

基于SSM的仓库管理系统

基于SSM的仓库管理系统的设计与实现【文末源码】 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 管理员界面 员工管理 货物管理 员工界面 摘要 当考虑构建基于…

Git使用入门

一、Git简介 Git 是一个开源的分布式版本控制系统。 Git版本控制的功能为保存不同版本的代码&#xff0c;保存代码的地方叫做仓库。 每个仓库中有多个分支&#xff0c;每个分支上又有很多节点&#xff0c;每个节点代表一个版本&#xff0c;不同的分支可以进行合并&#xff0…

用节点亲和性把 Pod 分配到节点

用节点亲和性把 Pod 分配到节点 当前集群信息&#xff1a; rootk8s-master:~# kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s…

通过小程序实现会议Oa的会议展示以及个人中心

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《微信小程序 》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;…

游戏盾如何有效防护DDoS

从进入计算机时代以来&#xff0c;DDoS攻击一直是网络世界中的一大威胁&#xff0c;让无数服务陷入瘫痪。这种攻击的原理非常简单&#xff1a;攻击者使用大量的僵尸主机或蠕虫病毒&#xff0c;向目标服务器发送海量请求&#xff0c;迅速耗尽服务器的资源&#xff0c;使其无法继…

蓝桥杯 (年号字串 C++)

思路&#xff1a; 1、看成10进制转化成26进制 。 2、A表示1、B表示2。以此类推&#xff0c;Z表示26. 代码&#xff1a; #include <iostream> using namespace std; int main() {char str[10]; int sum 2019, n, i 0; while (sum > 0) {str[i] sum % 26 64;sum / …

REDIS命令

常见文件名 Redis-cli使用命令 1、启动Redis2、连接Redis3、停止Redis4、发送命令 1、redis-cli带参数运行&#xff0c;如&#xff1a;2、redis-cli不带参数运行&#xff0c;如&#xff1a;5、测试连通性key操作命令 获取所有键查询键是否存在删除键查询键类型移动键查询key的生…

vue ref和$refs获取组件实例

vue ref和$refs获取组件实例 **创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\15-ref和$refs获取组件实例 vue --ve…

用Nginx搭建一个具备缓存功能的反向代理服务

在同一台服务器上&#xff0c;使用nginx提供服务&#xff0c;然后使用openresty提供反向代理服务。 参考《Ubuntu 20.04使用源码安装nginx 1.14.0》安装nginx。 参考《用Nginx搭建一个可用的静态资源Web服务器》搭建静态资源Web服务器&#xff0c;但是/nginx/conf/nginx.conf里…

安装与脏数据绕过_安全狗

1安全狗 1.1 环境准备 安全狗safedogwzApacheV3.5.exe&#xff0c;安装步骤省略&#xff0c; pikachu环境&#xff1a;https://zhuanlan.zhihu.com/p/568493971 安装注意事项&#xff1a;安装完后php和web服务都需要重启 注意事项&#xff1a;服务名php版本保持一致 安装过…

甄知科技张礼军:数智化转型助企业破茧成蝶!

数智化浪潮滚滚向前&#xff0c;正席卷各行各业&#xff0c;带领企业从数字化时代跨入数智化时代。可什么是数智化&#xff1f;如何实现数智化转型&#xff1f;已经成为横亘在无数企业面前的大难题&#xff01; 事实上&#xff0c;数智化是数字化、AI和业务三个要素的交集&…

2023天猫双十一活动时间表 天猫淘宝双11预售几号开始付定金

双十一购物节是生活不可或缺的一部分&#xff0c;不论是满足基本需求还是享受生活乐趣&#xff0c;都需要购物。因此&#xff0c;双十一绝对是一个不容错过的绝佳机会&#xff0c;希望大家能善用这个机会&#xff0c;因为错过了就得再等一整年。 每日领红包&#xff1a;红包有…

【微信小程序】6天精准入门(第5天:利用案例与后台的数据交互)附源码

一、什么是后台交互&#xff1f; 在小程序中&#xff0c;与后台交互指的是小程序前端与后台服务器之间的数据通信和请求处理过程。通过与后台交互&#xff0c;小程序能够获取服务器端的数据、上传用户数据、发送请求等。 小程序与后台交互可以实现数据的传输、用户认证、实时消…

Parallels Client for Mac:改变您远程控制体验的革命性软件

在当今数字化的世界中&#xff0c;远程控制软件已经成为我们日常生活和工作中不可或缺的一部分。在众多远程控制软件中&#xff0c;Parallels Client for Mac以其独特的功能和出色的性能脱颖而出&#xff0c;让远程控制变得更加简单、高效和灵活。 Parallels Client for Mac是…

分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLAB实…