一.实验原理

  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编程的基础。一个句柄是指使用的一个唯一的整数值,即一个四字节长的数值,来标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序通过句柄访问相应的对象信息。

  以下为实验所用到的函数:(函数释义打*号需另外说明)

函数调用形式 函数形式 函数释义
AVIFileOpen ( &avi_file, AVIFILE, OF_READ, NULL ) STDAPI AVIFileOpen( PAVIFILE * ppfile, LPCTSTR szFile, UINT mode, CLSID pclsidHandler ); 打开一个AVI文件并返回可处理它的接口。
AVIFileGetStream ( avi_file, &avi_stream, streamtypeVIDEO, 0 ) STDAPI AVIFileGetStream( PAVIFILE pfile, PAVISTREAM * ppavi, DWORD fccType, LONG lParam ); 函数返回与AVI文件所指定对的流接口的地址,这里第二个参数avi_stream为接下来处理流的句柄,类型为PAVISTREAM。
AVIStreamReadFormat ( avi_stream, 0, &bi, &lStreamSize) STDAPI AVIStreamReadFormat( PAVISTREAM pavi, LONG lPos, LPVOID lpFormat, LONG * lpcbFormat ); *这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX结构
AVIStreamInfo ( avi_stream, &str_info, sizeof(str_info) STDAPI AVIStreamInfo( PAVISTREAM pavi, AVISTREAMINFO * psi, LONG lSize ); 函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
AVIStreamStart ( avi_stream ); STDAPI_(LONG) AVIStreamStart( PAVISTREAM pavi ); 函数用来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
AVIStreamLength ( avi_stream ); STDAPI_(LONG) AVIStreamLength( PAVISTREAM pavi ); 函数获取流的长度,同样可以通过AVIStreamInfo函数来获取长度
AVIStreamGetFrameOpen ( avi_stream, &bi_wanted ); STDAPI_(PGETFRAME) AVIStreamGetFrameOpen( PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted ); 函数用来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据
AVIStreamGetFrame ( pgf, f+begin ); STDAPI_(LPVOID) AVIStreamGetFrame( PGETFRAME pgf, LONG lPos ); 函数用以压缩一个单独的视频祯,调用成功返回值为指向一帧数据的地址值,帧数据形式为打包好的DIB
AVIStreamGetFrameClose ( pgf ); STDAPI AVIStreamGetFrameClose( PGETFRAME pget ); AVIStreamGetFrame()获取的帧一直有效,直到调用了这个关闭函数。

  实验涉及到的数据结构:

用VS中“查看定义”功能找到VFW库中结构体的定义:

        DWORD   fccType;      //若此流含的是video数据,此域值为"vids",若为audio数据,则为”auds”.DWORD   fccHandler;   //为四个字符,描述数据所用的压缩、解压缩算法。DWORD   dwFlags;        //数据流属性DWORD   dwCapsWORD    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

avi截图 生成的yuv文件截图

四.代码分析

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 filereturn;
}
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);elseprintf("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的解封装相关推荐

  1. BMP无压缩封装为AVI视频

    因为组内测试序列制作需要,需要将制作好的BMP位图,无压缩封装为AVI视频,并附有多种要求(基准和实验序列交替出现.随机组合等).因此,需要实现BMP到AVI的无压缩封装. 由于整个测试序列集有大概几 ...

  2. ( 持续更新,目前含 200+ 工具类 ) DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。

    DevUtils GitHub About ( 持续更新,目前含 200+ 工具类 ) Roadmap DevUtils 是一个 Android 工具库,主要根据不同功能模块,封装快捷使用的工具类及 ...

  3. linux下Qt调用C++库文件(.so)程序实现

    文章目录 主要内容 一.编程环境及实现方法 二.项目实现 1.创建项目 2.导入库文件 三.项目中遇到的问题 总结 主要内容 如标题所示,在linux下使用qtcreator创建项目调用C++库文件, ...

  4. linux用c++调用动态库

    1.3 用c++静态方式调用动态库libsthc.so: /*cpptest.cc*/    //linux下的c++后缀有cc,cxx, cpp #include "libsthc.h&q ...

  5. Win7下使用VFW库连接摄像头

    本文转自:http://hi.baidu.com/blogofivan/blog/item/bc28009bb8ee45036f068c6d.html VFW库在XP下很好用,但是移到Vista或者W ...

  6. Java调用动态库 缺点,Java调用动态库所需要关心的有关问题

    Java调用动态库所需要关心的问题 标签: 无标签 利用JNative实现Java调用动态库(转) http://cctv663.blog.163.com/blog/static/1011921220 ...

  7. Qt调用Matlab库C#库dll调用C++调用C#库CSharp库lib invoke matlab library .net donet netframework

    Qt调用Matlab库C#库dll调用C++调用C#库CSharp库lib invoke matlab library .net donet netframework 该文章是一篇说明c++/Qt调用 ...

  8. 两台电脑串口传输文件及调用opencv库编程显示图片和文本

    文章目录 一.两台电脑利用串口进行文件传输 (一).实验内容 (二).配置环境 (三).实验过程 (四).结果分析 二.在Ubuntu下基于Linux环境调用opencv库编程显示图片和文本 (一). ...

  9. python-外部程序的调用os库、subprocess 模块

    目录 os库 subprocess 模块 在python中调用外部程序两个常用的库:os库和subprocess库 相关内容: https://blog.csdn.net/JBY2020/articl ...

最新文章

  1. JAVA 条件语句 跟PHP没有区别!!!!!
  2. (转)跟我一起写 Makefile(一)(陈皓)
  3. 机器学习实践:TensorFlow最后一个epoch训练损失函数显著增大
  4. 哪款浏览器好用_碉堡了!火狐浏览器发布重大更新,谷歌Chrome请hold住!
  5. ONVIF Device Manager修改设备密码
  6. sql语句遇到的问题
  7. 应用上云2小时烧掉近50万,创始人:差点破产,简直噩梦
  8. php天津旅游设计网站作品
  9. LeetCode 72.编辑距离(动态规划)
  10. SQL Server2008表及字段描述信息处理示例
  11. Java基础知识之笔记总结分享(超详细)入门必备
  12. 基于机器学习中KNN算法的车牌字符识别
  13. WIN7下怎么安装iis教程
  14. 新会计准则对传统会计理论的七大变革
  15. 开启WIN10的卓越性能模式
  16. 突发!图森未来CEO侯晓迪被罢免,公司市值一夜砍半!自动驾驶未来如何?
  17. antdesign 地图_Ant Design介绍
  18. 数据仓库应用篇(一)需求文档模板和需求评审
  19. C语言学习之有一个函数: y= x(x<1) 2x-1 (1≤x<10) 3x-11 (x≥10) 写程序,输入x的值,输出y相应的值。
  20. 基金使用计划 数学建模 matlab,数学建模论文-基金使用计划

热门文章

  1. Pycharm打包可执行文件exe/mac
  2. UG后处理—刀具行程值的输出
  3. 3星|《三联生活周刊》2017年40期:中国迈入高收入国家门槛几乎是板上钉钉的事...
  4. 14.Flink1.11 安装部署及Release 文档解读
  5. 服务器电源故障维修培训,12v开关电源维修实例 - 开关电源维修教程_开关电源维修从入门到精通_开关电源故障检修方法...
  6. HM2805B高效率恒流限流 WLED 驱动IC
  7. 微信小程序使用icon图标
  8. 笔记本电脑USB接口没有反应?原来问题的根源在这儿,涨知识了!
  9. 基于QT的界面框架qcanpool使用教程(废弃)
  10. 使用类或css动画实现时钟