kinect视频的保存与回放(一)


目的:保存kinect的视频数据,并回放,以便于动作的剪裁
进展:保存为无压缩的avi格式文件,回放框架初步搭建起来
方案的选择:
  • 简单的方法是使用openCV            
    这种方案,编程实现简单。但是,必须在新的机器上安装配置openCV库,这是其操作实现复杂的一方面,另一方面是在进行avi视频生成时,要求机器安装编码器。
    暂时不考虑这个方案。
  • 创建文件存储原始数据(无压缩)
 保存了原始数据后发现,每分钟的数据量是1~2G,这是在无压缩保存方式下不可避免的。
1)按文件流的操作方式写入文件,速度太慢。因为是边播放边保存,频繁的IO操作,所以会出现播放不流畅的现象。
2)采用文件的write方法把内存块一次性写入一帧,可以解决1)中的问题。但是问题是数据量太大,原因就是没有压缩
3)在速度方面,如果还有更高要求可以考虑:大文件或大量数据处理方式——内存映射文件,像操作内存一样操作文件,仅仅是在速度上快(不可能达到内存的速度,因为省去了很多IO操作),并且数据量越大它的优越性越明显。
    内存映射文件直接将文件的地址映射到进程的地址空间中,那么操作文件就相当于在内存中操作一样,省去了读和写I/O的时间;实际情况访问速度不可能达到访问内存的速度,只是省去了读写I/O转换的时间,数据量越大对比越明显。 
  • 解析avi格式,用微软的库去操作
 
VFW(Video for Windows)是Microsoft推出的关于数字视频的一个软件开发包,VFW的核心是AVI文件标准。

如果不压缩保存avi,数据量还是很大 ,大约每秒100兆左右。考虑到时间的问题和内存的问题(边播放边压缩和存储),在可以容忍的条件下,先搁置一下压缩的问题,把路子走通。

1) AVIFileInit();//初始化;

2) AVIFileOpen(); //打开一个AVI文件并获文件的句柄;

3) AVIFileInfo(); //获取文件的相关信息,如图像的Width和Height等;

4) AVIFileGetStream(); //建立一个指向需要访问的数据流的指针;

5) AVIStreamInfo(); //获取存储数据流信息的AVISTREAMINFO结构;

6) AVIStreamRead(); //读取数据流中的原始数据, 对AVI文件进行所需的编辑处理;

7) AVIStreamRelease(); //释放指向视频流的指针;

8) AVIFileRelease();//释放流

9) AVIFileExit(); //释放AVI文件,断开流与文件的关联

把按帧把内存数据依次写入文件的过程中,遇到不少错误,也收获了些许经验;比如是否应该把avi对象放入已有的类中进行类内操作,还是设置成全局变量。经过一番折腾,终于看到avi文件出现了;文件太大是意料之中的,但是存储播放视频发现图像上下倒置。是内存数据存放的问题?还是写入的时候,读取方式从后到前啊?最后查明原因是后者,存储顺序是这样的:先从图像最下面一行开始,从左向右,依次存储,最后存储正数第一行。明白问题所在了,方法也就有了。下面是数据倒置和帧写入文件的部分代码。

        BYTE *pBuffer = new BYTE[LockedRect.size];

  for(int i = 0; i < 480; i++)  {

        //优化效率 knife-1

  memcpy(pBuffer + i * 640 * 4,(BYTE*)LockedRect.pBits + (480 - i - 1) * 640 * 4,640 * 4);

  }

  hr = AVIStreamWrite(ps,frame,1,(LPBYTE)pBuffer,LockedRect.size,AVIIF_KEYFRAME, NULL, NULL);

        delete []pBuffer;

在回放的时候还要把数据再倒置一遍,使其正确绘出,这是意料之中的事。这样一来,我所做的事情是这样的:保存和读取各倒置了一次数据,达到的目的就是保存的avi视频文件在其他播放器(QQ影音,media player)中图像不至于上下颠倒。

尝试压缩失败
在对视频数据进行xvid压缩后,存入avi文件出错!
我是先用AVIFileCreateStream()创建了原始流,接着用 AVIMakeCompressedStream()创建了一个压缩流,然后通过 ICSeqCompressFrame()对数据进行压缩,在调用AVIStreamSetFormat()设置压缩流的格式时采用的是压缩流的位图格式 而非原图像格式,其大小也是压缩后的实际大小,最后采用AVIStreamWrite()写入流数据时也是采用压缩后的图像数据和压缩后图像数据的实际大小。

另记:
CreateFile 创建文件,返回文件句柄,是一个用途非常广泛的函数,在这里的用法并没有什么特殊 的地方,但有几点需要注意:一是访问模式参数dwDesiredAccess。该参数设置了对文件内核对象的访问类型,其允许设置的权限可以为读权限 GENERIC_READ、写权限GENERIC_WRITE、读写权限GENERIC_READ | GENERIC_WRITE和设备查询权限0。在使用映射文件时,只能打开那些具有可读访问权限的文件,即只能应用GENERIC_READ和 GENERIC_READ | GENERIC_WRITE这两种组合;另一点需要注意的是共享模式参数dwShareMode。该参数定义了对文件内核对象的共享方式,其可能的设置为 FILE_SHARE_READ、FILE_SHARE_WRITE和0,并可对其组合使用。其中,设置为0时不允许共享对 象;FILE_SHARE_READ和FILE_SHARE_WRITE分别为在要求只读、只写访问的情况下才允许对象的共享。

  由于通过内存映射文件可以在多个进程间共享数据,因此在进行这种应用时应当考虑dwShareMode参数设置对运行结果的影响。

CreateFileMapping()函数创建一个文件映射内核对象,通过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由 CreateFile()函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统 不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统能够确定对页面采取何种保护属性,需要通过参数flProtect来 设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,可以读取、读写 文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE 则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只需要确保 CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的参数 dwMaximumSizeHigh和dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64位,因此所支持的 最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。

MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为 CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与 CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的 保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件 的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度 的整数倍,对于Windows操作系统,分配粒度固定为6 4KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:
  PBYTE pBuffer=(PBYTE)MapViewOfFile(
   hMapFile,
   FILE_MAP_READ|FILE_MAP_WRITE,
   0,    //OffsetHigh of the file offset where the view begins.
   0,    //OffsetLow,
   100);   //粒度个数 如果是0,the mapping extends from the specified offset to the end of the file mapping.
SYSTEM_INFO,Win32 API函数GetSystemInfo所使用的结构体。
说明:
SYSTEM_INFO结构体包含了当前计算机的信息。这个信息包括计算机的体系结构、中央处理器的类型、系统中中央处理器的数量、页面的大小以及其他信息。

你可能感兴趣的:(windows,File,Microsoft,存储,byte,avi)