【数据压缩】调用VFW库对无压缩avi的解封装

来源:互联网 发布:保险网络增员话术 编辑:程序博客网 时间:2024/04/30 21:46

一.实验原理

  AVI文件格式是微软公司推出的,Windows操作系统上最常用的流媒体文件之一。AVI文件是一种最复杂的RIFF文件,现在常用的AVI文件有两种:AVI-1和AVI-2。在AVI-2文件中通常包含2个流,一个视频流和一个音频流(被称为标准AVI格式)。
RIFF格式
  一个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()获取的帧一直有效,直到调用了这个关闭函数。

  实验涉及到的数据结构:
AVISTEAMINFO结构
用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流程图

三.实验结果

实验用到两组avi序列,一组序列为yuv数据直接封装,另一组则是用h264编码器压缩的数据。序列分辨率为1920*1080
这里写图片描述

avi截图 生成的yuv文件截图 rawavi rawyuv 264avi 264yuv

四.代码分析

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;}
0 0
原创粉丝点击