• vc++实现avi文件的操作 用于视频解析及录制(转)


    vc++实现avi文件的操作

    为了对avi进行读写,微软提供了一套API,总共50个函数,他们的用途主要有两类,一个是avi文件的操作,一类是数据流streams的操作。
      1、打开和关闭文件
      AVIFileOpen ,AVIFileAddRef, AVIFileRelease
      2、从文件中读取文件信息
      通过AVIFileInfo可以获取avi文件的一些信息,这个函数返回一个AVIFILEINFO结构,通过AVIFileReadData可以用来获取AVIFileInfo函数得不到的信息。这些信息也许不包含在文件的头部,比如拥有file的公司和个人的名称。
      3、写入文件信息
      可以通过AVIFileWriteData函数来写入文件的一些额外信息。
      4、打开和关闭一个流
      打开一个数据流就跟打开文件一样,你可以通过 AVIFileGetStream函数来打开一个数据流,这个函数创建了一个流的接口,然后在该接口中保存了一个句柄。
      如果你想操作文件的某一个单独的流,你可以采用AVIStreamOpenFromFile函数,这个函数综合了AVIFileOpen和AVIFileGetStream函数。
      如果你想操作文件中的多个数据流,你就要首先AVIFileOpen,然后AVIFileGetStream。
      可以通过AVIStreamAddRef来增加stream接口的引用。
      通过AVIStreamRelease函数来关闭数据流。这个函数用来减少streams的引用计数,当计数减少为0时,删除。
      5、从流中读取数据和信息
      AVIStreamInfo函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
      如果数据流还有一些其它的额外的信息,你可以通过AVIStreamReadData函数来获取。应用程序分配一个内存,传递给这个函数,然后这个函数会通过这个内存返回数据流的信息,额外的信息可能包括数据流的压缩和解压缩的方法,你可以通过AVIStreamDataSize宏来回去需要申请内存块的大小。
      可以通过AVIStreamReadFormat函数获取数据流的格式信息。这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。你可以通过给AVIStreamReadFormat传递一个空buffer就可以获取buffer的大小。也可以通过AVIStreamFormatSize宏。
      可以通过AVIStreamRead函数来返回多媒体的数据。这个函数将数据复制到应用程序提供的内存中,对于视频流,这个函数返回图像祯,对于音频流,这个函数返回音频的sample数据。可以通过给AVIStreamRead传递一个NULL的buffer来获取需要的buffer的大小。也可以通过AVIStreamSampleSize宏来获取buffer的大小。
      有些AVI数据流句柄可能需要在启动数据流的前要做一下准备工作,此时,我们可以调用AVIStreamBeginStreaming函数来告知AVI数据流handle来申请分配它需要的一些资源。在完毕后,调用AVIStreamEndStreamming函数来释放资源。
      6、操作压缩的视频数据
      如果你要演示一祯或者几祯压缩视频图像时,你可以调用AVIStreamRead函数,将获取的数据传递给DrawDib函数来显示图像。这些函数可以显示压缩和未压缩的图像。
      AVIFile也提供了一个函数AVIStreamGetFrameOpen,来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据。也可以通过AVIStreamGetFrame函数来解压缩一个单独的视频祯。这个函数可以解压缩某一祯图像,然后将数据以一个BIMAPINFOHEADER结构返回。当你调用完AVIStreamGetFrame函数后,要调用AVIStreamGetFrameClose函数释放上一个函数申请的资源。
      7、根据已存在的数据流创建文件
      创建一个包含多个数据流的文件的方法就是整合多个数据流,将其写入一个新文件。这些数据流可以是内存中的数据,也可以是存在于另一个文件中。
      我们可以用AVISave这个函数来build一个文件。这个函数可以创建一个文件,并且将指定的多个数据流按照指定的顺序写入文件,你也可以通过AVISaveV函数来创建一个新的文件,这个函数的功能和AVISave的功能一样,主要区别是AVISaveV采用的数据流数组,而AVISave是单个的数据流,多次保存。
      我们可以调用AVISaveOptions函数来显示一个对话框,可以让用户来选择压缩方式。
      我们可以在调用AVISave和AVISaveV函数时指定一个回调函数,用来显示avi文件的生成进度,可以让用户随时地取消生成avi文件。
      我们可以调用GetSaveFileNamePreview函数来显示保存的对话框让用户选择保存的文件名。
      通过AVIMakeFileFromStreams函数我们可以创建一个虚拟的文件句柄,其他的avi函数可以通过这个虚拟的文件句柄来操作文件中的数据流,操作完毕要记得调用AVIFileRelease释放。

    8、向文件写入一个数据流
      我们可以通过AVIFileCreateStream函数来在一个新文件或者已经存在的文件中创建一个数据流。这个函数根据AVISTREAMINFO结构定义了新的数据流,并为新的数据流创建一个接口,返回接口的指针。
      在写入新的数据前,一定要指定流的格式信息,通过AVIStreamSetFormat函数,当设置一个视频流的时候,一定要使用BIMAPINFO结构来设置,音频就用WAVEFORMAT。
      然后我们就可以通过AVIStreamWrite函数将我们的多媒体数据写入数据流了。这个函数将应用程序提供的内存数据复制到指定的流。缺省的avi handler将数据写入流的最后。
      如果你有其他额外的信息需要写入流,你可以调用AVIFileWriteData或者AVIStreamWriteData,最后记得在完成数据写入后,要调用AVIStreamRelease。
      9、数据流中的祯的位置
      寻找起始祯:
      可以通过AVIStreamStart函数来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,这个函数的AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
      可以通过AVIStreamLength函数来获取流的长度。这个函数返回流中的sample的数目。也可以通过AVIStreamInfo函数来获取这些信息,可以通过AVIStreamLengthTime宏来获取流的长度,毫秒。
      在视频流中,一个sample对应着一祯图像,所以,有时这些sample中没有视频数据,如果你调用AVIStreamRead函数来数据,可能返回NULL,也可以通过AVIStreamFindSample通过指定FIND_ANY标志来查找指定的sample。
      查找关键祯
      通过AVIStreamFindSample函数查找符合要寻找的sample,然后可以通过下面的宏判断是否关键祯。
      在time和sample间互相切换。
      AVIStreamSampleToTime这个函数可以将smaple转换成毫秒。对于视频,这个值代表的是这个祯开始播放的时间。
      在了解了上面的知识后,我们对avi的文件结构以及如何操作avi文件心里就明白了,下面我们可以开始我们的编程了。我们要做两件事情:
      1、如何将一组静态的bmp位图合成一个avi的视频文件;
      2、如何将一个未压缩的avi文件解析成一幅幅位图。
      示例程序界面如下:

      下面的函数演示了如何将一个文件夹下面的所有bmp文件都保存为一个avi文件,函数的第一个参数是要生成的AVI的文件名,第二个参数是存放bmp文件的文件夹名,这个函数会枚举该文件夹下的所有bmp文件,合成一个AVI文件。

    void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString strBmpDir)
    {
     // TODO: 在此添加控件通知处理程序代码
     AVIFileInit();
     PAVIFILE avi;
     int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
     int n = GetLastError();
     if (res!=AVIERR_OK)
     {
      //an error occures
      if (avi!=NULL)
       AVIFileRelease(avi);
      return ;
     }
     AVIFILEINFO avi_info;
     AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
     PAVISTREAM pStream;
     res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
       0 /*first stream*/);
     if (res!=AVIERR_OK)
     {
      if (pStream!=NULL)
       AVIStreamRelease(pStream);
       AVIFileExit();
      return ;
     }
     //do some task with the stream
     int iNumFrames;
     int iFirstFrame;
     iFirstFrame=AVIStreamStart(pStream);
     if (iFirstFrame==-1)
     {
      //Error getteing the frame inside the stream
      if (pStream!=NULL)
       AVIStreamRelease(pStream);
      AVIFileExit();
      return ;
     }
     iNumFrames=AVIStreamLength(pStream);
     if (iNumFrames==-1)
     {
      //Error getteing the number of frames inside the stream
      if (pStream!=NULL)
       AVIStreamRelease(pStream);
      AVIFileExit();
      return ;
     }
     //getting bitmap from frame
     BITMAPINFOHEADER bih;
     ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
     bih.biBitCount=24; //24 bit per pixel
     bih.biClrImportant=0;
     bih.biClrUsed = 0;
     bih.biCompression = BI_RGB;
     bih.biPlanes = 1;
     bih.biSize = 40;
     bih.biXPelsPerMeter = 0;
     bih.biYPelsPerMeter = 0;
     //calculate total size of RGBQUAD scanlines (DWORD aligned)
     bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
     PGETFRAME pFrame;
     pFrame=AVIStreamGetFrameOpen(pStream, NULL );
     AVISTREAMINFO streaminfo;
     AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
     //Get the first frame
     BITMAPINFOHEADER bih2;
     long lsize = sizeof(bih2);
     int index=0;
     for (int i=iFirstFrame; i<iNumFrames; i++)
     {
      index= i-iFirstFrame;
      BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //
      AVIStreamReadFormat(pStream,index,&bih2,&lsize);
      BITMAPFILEHEADER stFileHdr;
      BYTE* Bits=new BYTE[bih2.biSizeImage];
      AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
      //RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
      bih2.biClrUsed =0;
      stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
      stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
      stFileHdr.bfType=0x4d42;
      CString FileName;
      FileName.Format("Frame-%05d.bmp", index);
      CString strtemp = strBmpDir;
      strtemp += "\\";
      strtemp += FileName;
      FILE* fp=_tfopen(strtemp ,_T("wb"));
      fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
      fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
      int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
      int e = GetLastError();
      fclose(fp);
      /////
      delete Bits;
      //CreateFromPackedDIBPointer(pDIB, index);
     }
     AVIStreamGetFrameClose(pFrame);
     //close the stream after finishing the task
     if (pStream!=NULL)
      AVIStreamRelease(pStream);
     AVIFileExit();
    }

      下面的这个函数演示了如何将AVI文件中的每一桢图像单独取出来,保存为bmp文件。函数的头一个参数是avi文件名,第二个参数是存放bmp文件的文件夹。

    //生成avi
    void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
    {
     CFileFind finder;
     strBmpDir += _T("\\*.*");
     AVIFileInit();
     AVISTREAMINFO strhdr;
     PAVIFILE pfile;
     PAVISTREAM ps;
     int nFrames =0;
     HRESULT hr;
     BOOL bFind = finder.FindFile(strBmpDir);
     while(bFind)
     {
      bFind = finder.FindNextFile();
      if(!finder.IsDots() && !finder.IsDirectory())
      {
       CString str = finder.GetFilePath();
       FILE *fp = fopen(str,"rb");
       BITMAPFILEHEADER bmpFileHdr;
       BITMAPINFOHEADER bmpInfoHdr;
       fseek( fp,0,SEEK_SET);
       fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
       fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
       BYTE *tmp_buf = NULL;
       if(nFrames ==0 )
       {
        AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
        _fmemset(&strhdr, 0, sizeof(strhdr));
        strhdr.fccType = streamtypeVIDEO;// stream type
        strhdr.fccHandler = 0;
        strhdr.dwScale = 1;
        strhdr.dwRate = 15; // 15 fps
        strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
        SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
        // And create the stream;
        hr = AVIFileCreateStream(pfile,&ps,&strhdr);
        // hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
       }
       tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
       fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
       hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
       hr = AVIStreamWrite(ps, // stream pointer
          nFrames , // time of this frame
          1, // number to write
          (LPBYTE) tmp_buf,
          bmpInfoHdr.biSizeImage , // size of this frame
          AVIIF_KEYFRAME, // flags....
          NULL,
          NULL);
       nFrames ++;
       fclose(fp);
      }
     }
     AVIStreamClose(ps);
     if(pfile != NULL)
      AVIFileRelease(pfile);
     AVIFileExit();
    }

    结束语:
      以上代码在 vc 6.0 和windows xp平台调试通过。这两个函数你可以直接在你的程序中使用,更详细的代码可以参见随着本文附上的示例源码。这里我要指出的是,这个AVI文件和bmp互相转换过程中,avi中的视频数据都是存放的是没有压缩的数据,如果你要分解AVI文件是经过压缩编码,比如,DVSD,MPEG4编码,首先你要采用相应的解码器对视频数据解码,然后将解码过的数据保存为bmp文件。好了,关于avi文件的介绍就到这里结束了。

  • 相关阅读:
    互联网改变的产业 汽车 x 互联网 = 汽车革命
    我有一个实现HMI触摸屏的资源【4418开发平台】降d成本
    iTOP-4412开发板裸机开发环境文档分享
    2019年最受欢迎iTOP-4418开发板_新产品研发必备利器
    安卓触控一体机为什么得到大家认可?远比Windows系统一体机大受欢迎
    新手入门嵌入式学习单片机?stm32?树莓派?4412开发板资料大汇报-基础了解
    iTOP-4418开发板-Qt系统下运行摄像头测试程序
    iTOP-4418/6818开发板-QtE4.7WIFI_MT6620热点
    iTOP-4412开发板-使用PartitionManager分区之后tf卡无法识别
    迅为iTOP-4418/6818开发板-MiniLinux-GPS使用文档
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/1850082.html
Copyright © 2020-2023  润新知