【数据压缩】调用VFW库对无压缩avi的解封装
来源:互联网 发布:保险网络增员话术 编辑:程序博客网 时间:2024/04/30 21:46
一.实验原理
AVI文件格式是微软公司推出的,Windows操作系统上最常用的流媒体文件之一。AVI文件是一种最复杂的RIFF文件,现在常用的AVI文件有两种:AVI-1和AVI-2。在AVI-2文件中通常包含2个流,一个视频流和一个音频流(被称为标准AVI格式)。
一个AVI RIFF文件由3大部分组成:
- RIFF文件头
- hdrl列表
-avih子块
-strl子列表 - movi列表
文件有多少个流,hdrl列表中就有多少个strl子列表,strl子列表在hdrl中的次序就是流的序号。strl子列表由strh字块、strf字块、strd子块(可选)、strn子块(可选)构成。
movi列表中储存的则是流实际的数据,其中种类有:##db,##dc,##pc,##wb。其中“##”代表数据所属的流的序号。db代表未压缩的视频帧(DIB的简写),dc代表已压缩的视频帧(DIB compressed的简写);wb代表音频数据;pc代表调色板变化。
应注意的是,AVI RIFF文件格式只规定了文件的组成方式,即各种数据如何在文件中排列对等,对于文件中的数据并没有做任何编码格式的约束。即可理解为AVI格式只是个容器,其中可以放置多种编码格式的数据,可以是MPEG-4、h.264编码的,数据本身也可以未经压缩。
本次实验所用素材:对YUV文件,利用FFMPEG直接封装成的AVI文件。
利用VFW从AVI文件中提取RGB数据
VFW是微软公司为开发Windows平台下视频应用程序提供的软件工具包。我们利用VFW提供的应用程序编程接口(API)可以很方便地实现视频捕获、视频编辑、视频播放等功能。
- 句柄(Handle)
句柄是整个windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个四字节长的数值,来标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序通过句柄访问相应的对象信息。
以下为实验所用到的函数:(函数释义打*号需另外说明)
实验涉及到的数据结构:
用VS中“查看定义”功能找到VFW库中结构体的定义:
DWORD fccType; //若此流含的是video数据,此域值为"vids",若为audio数据,则为”auds”. DWORD fccHandler; //为四个字符,描述数据所用的压缩、解压缩算法。 DWORD dwFlags; //数据流属性 DWORD dwCaps WORD wPriority; //此数据流的播放优先级 WORD wLanguage; //音频的语言代号 DWORD dwInitialFrames; //用于interlaced文件,定义在文件中在AVI系列初始帧之前所含的数据帧的个数。 DWORD dwScale; //与dwRate一起定义回放速率。 DWORD dwRate; // DWORD dwStart; //序列的起始时间 DWORD dwLength; //系列的长度 DWORD dwSuggestedBufferSize; //回放所需的Buff大小 DWORD dwQuality; //数据质量标志字 DWORD dwSampleSize;
在调用了AVIStreamInfo后,在Debug过程中监视AVISTEAMINFO的内存窗格:
可观察到fccType对应的值为vids,由于实验所用AVI为YUV420直接封装得来,其fccHandler值为”I420”。
- 下图结果为,采用H264压缩得到的AVI中的fccHandler值
PS:若需处理h264压缩的数据流,需在电脑上安装H264依赖库(x264vfw.exe)。
二.实验流程
三.实验结果
实验用到两组avi序列,一组序列为yuv数据直接封装,另一组则是用h264编码器压缩的数据。序列分辨率为1920*1080
四.代码分析
main.cpp:
void main(int argc, char **argv){ Init(argv[1], argv[2]); return;}
void Init(char *aviFileName,char *yuvFileName){ PAVIFILE avi_file = NULL;//用于操作文件的句柄 PAVISTREAM avi_stream = NULL;//用于操作数据流的句柄 AVIFILEINFO avi_info;//用于读取AVI的文件信息 //尚未发现读取MainAVIHeader的方法,待修改 AVISTREAMINFO stream_info;//流数据结构体 BITMAPINFOHEADER info_h;//用于读取流封装格式的bmp信息头结构体 LONG longAviInfo = sizeof(avi_info); int begin, len, seqw, seqh; AVIFileInit();//初始化AVI系统库 if (AVIFileOpen(&avi_file, aviFileName, OF_READ, NULL)) {//打开avi文件 printf("Failed AVIFileOpen:%s\n", aviFileName); exit(0); } if (AVIFileInfo(avi_file, &avi_info, longAviInfo)==0) { printf("read File Info success!\n"); printf("contain %d streams in this file\n", avi_info.dwStreams); //读取AVI文件信息,并输出文件包含了多少个数据流 } if (AVIFileGetStream(avi_file, &avi_stream, streamtypeVIDEO, 0)) {//打开数据流 printf("Failed AVIFileGetStream\n"); exit(1); } LONG lpcbFormat = sizeof(info_h); if (AVIStreamReadFormat(avi_stream, 0, &info_h, &lpcbFormat)) {//读数据流格式 printf("Failed AVIStreamReadFormat\n"); exit(1); } /* AVIStream函数通过指定的内存返回数据流的格式信息。对于视频流,包含了一个BITMAPINFOHEADER结构,对于视频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。 */ if (AVIStreamInfo(avi_stream, &stream_info, sizeof(stream_info))) { printf("Failed AVIStreamInfo\n"); exit(1); } begin = AVIStreamStart(avi_stream);//寻找起始帧 if (begin == -1) { printf("failed get start sample number\n"); if (avi_stream == NULL) AVIStreamRelease(avi_stream); AVIFileExit(); exit(1); } len = AVIStreamLength(avi_stream);//获取流的长度 if (len == -1) { printf("failed get the length of frames\n"); if (avi_stream == NULL) AVIStreamRelease(avi_stream); AVIFileExit(); exit(1); } seqw = info_h.biWidth;//获取帧宽度 seqh = info_h.biHeight;//获取帧高度 printf("%dx%d,%d frames @ %d Hz\n", seqw, seqh, len, stream_info.dwRate / stream_info.dwScale); GetRgbFromStream(avi_stream,info_h,yuvFileName,begin,len,seqw,seqh); AVIFileRelease(avi_file);//close the file return;}
void GetRgbFromStream(PAVISTREAM avi_stream, BITMAPINFOHEADER info_h, char *yuvFileName, int begin, int len, int seqw, int seqh){ bool flip = FALSE; BITMAPINFOHEADER tmpinfo_h; tmpinfo_h = info_h; tmpinfo_h.biCompression = BI_RGB; PGETFRAME pgf; BITMAPINFO *binf; u_int8_t *pk_data, *smp_data; u_int8_t *yBuf, *vBuf, *uBuf; FILE *yuvFile; yBuf = (u_int8_t *)malloc(seqw *seqh); uBuf = (u_int8_t *)malloc(seqw *seqh / 4); vBuf = (u_int8_t *)malloc(seqw *seqh / 4); pgf = AVIStreamGetFrameOpen(avi_stream, &tmpinfo_h); if (pgf == NULL) { printf(" the system cannot find a decompressor that can decompress the stream to the given format, or to any RGB format\n"); exit(1); } yuvFile = fopen(yuvFileName, "wb+"); if (yuvFile == NULL) { printf("Failed fopen:%s\n", yuvFileName); \ } for (int f = 0; f < len; f++)//根据流长度,界定循环变量 { pk_data = (u_int8_t*)AVIStreamGetFrame(pgf, f + begin); if (pk_data) printf("Frame %d of %d OK\n", f, len); else printf("Frame %d of %d FAILED\n", f, len); binf = (BITMAPINFO*)pk_data; smp_data = (u_int8_t*)binf->bmiColors; //不存在调色板,跳过BITMAPINFO结构体中的bimHeader直接定位到储存的数据中 if (binf->bmiHeader.biWidth != seqw || binf->bmiHeader.biHeight != seqh) { printf("Mismatch in size\n"); exit(1); } if (binf->bmiHeader.biCompression != BI_RGB) { printf("Can only process RGB AVIs\n"); exit(1); } RGB2YUV(seqw, seqh, smp_data, yBuf, uBuf, vBuf, flip); int wr; wr = fwrite(yBuf, 1, seqw*seqh, yuvFile); if (wr != seqw*seqh) printf("Error in wr y\n"); wr = fwrite(uBuf, 1, seqw*seqh / 4, yuvFile); if (wr != seqw*seqh / 4) printf("Error in wr u\n"); wr = fwrite(vBuf, 1, seqw*seqh / 4, yuvFile); if (wr != seqw*seqh / 4) printf("Error in wr v\n"); } AVIStreamGetFrameClose(pgf); free(yBuf); free(uBuf); free(vBuf); fclose(yuvFile); return;}
- 【数据压缩】调用VFW库对无压缩avi的解封装
- 利用vfw库将一系列bmp图转换为压缩的avi视频
- 用vfw播放xvid的avi文件
- AVI的压缩
- avi的压缩
- avi的压缩
- 视频文件的解封装
- windows下使用vfw方式生成AVI视频的实现
- ZLib的数据压缩和解压缩
- VFW 生成AVI 音视频文件
- AVI常用的压缩算法
- Delphi : ZLib的数据压缩和解压缩
- VFW Microsoft推出的关于数字视频软件开发包 AVI文件标准
- VFW(Video For Windows)--AVI开发
- VC6.0 如何用VFW录制avi
- VC6.0 如何用VFW录制avi
- BMP to AVI 及其压缩的实现
- 对ED3Movie数据压缩格式的修正
- Codevs 1337 银行里的迷宫
- keras简单介绍与使用
- Java内部类
- 二叉树遍历
- 简单的客户端,服务端通信
- 【数据压缩】调用VFW库对无压缩avi的解封装
- 线程创建
- Linux 链接脚本分析
- 新三板挂牌条件是什么?
- JZOJ 3731. 【NOIP2014模拟7.10】庐州月
- C#定时程序
- ASP页面添加打印按钮
- python中类的基本使用
- 【bzoj1002】[FJOI2007]轮状病毒