如要转贴请注明转至blog.csdn.net/suntaoznz。谢谢!
视频压缩管理器(VCM)视频压缩管理器提供了一个访问接口,通过该接口可以使用系统已经安装了的压缩器去压缩处理实时视频数据。应用程序可以使用安装的压缩器去执行下面的任务:
l 压缩和解压视频数据
l 发送一个renderer压缩视频数据并画它显示它。
l 使用应用程序默认的renderers去压缩,解压或者画视频数据。
l 使用renderers 去处理文本和自定义数据。
2.1关于视频压缩管理器
一般,处理视频图形的压缩器被存放在AVI文件中。通过VCM来访问安装了的压缩器,这些内容涉及到下面的主题:
l VCM 和VFW的结构
l 从你的程序压缩和解压图形数据
l 从你的程序中使用VCM renderers 去画视频数据
l VCM 函数和数据结构体
在你阅读这些内容前,最好对Microsoft Win32 图形处理了解一下。因为很多内容,比如位图结构 BITMAPINFO和BITMAPINFOHEADER会大量地被VCM使用。
注意:音频压缩管理器(ACM)提供系统级的音频压缩、解压支持,关于音频压缩服务,可以去看音频压缩管理器。
2.1.1 VCM 结构
VCM界于应用程序和压缩解压缩驱动程序之间。压缩解压缩驱动程序对数据帧进行压缩和解压缩处理。
当应用程序去调用VCM的时候,VCM把这个调用动作封装到一个消息中。然后通过使用ICSendMessage函数,把这个消息发送到适合的压缩器或解压器。VCM接收到压缩器或解压器的返回值后,就把控制返回给应用程序。
如果为一个消息定义一个宏,这个宏可以对ICSendMessage函数调用进行扩冲,因为它可以为这个消息提供更适合的参数。假如为一个消息定义一个宏,那么你的程序将使用宏而不愿意使用消息。在后面的介绍中,这些宏将放在消息后的园括号中。
2.1.2压缩,解压缩以及Renderers的注册表项目
系统使用注册表中的“相”来定位VCM的驱动程序。 这些“相”用两个四字符码(two four-character codes)的形式来表示,中间使用一个点来分割(比如VIDC.DIVX)。第一个4字符码(four-character code)由系统定义,它的内容是下列中之一:
四字符码
说 明
"VIDC"
压缩器和解压缩器的ID
"VIDS"
视频流Renderers的ID
"TXTS"
文本流(text-stream)Renderers的ID
"AUDS"
音频流控制器
自定义的renderers可以定义它自己的四字符码。
第二个四字符码由驱动程序定义。比较典型的是,第二个四字符码用于描述这个驱动程序可以处理的数据类型。
当打开一个VCM驱动程序的时候,应用程序就指定了这个驱动程序和驱动程序可以处理的数据类型。一般来说,这个信息来源于数据流的头部。系统将尝试去打开指定的数据,如果打开失败了,系统就在注册表中查找可以处理该数据的其他驱动程序。
当在查找驱动程序的时候,系统会尝试用与这四字符码匹配的其他驱动程序“项”来处理这个数据类型。例如,一个应用程序指定了一个MSSQ的压缩器,系统在注册表寻找VIDC.MSSQ这个项。如果没有找到,它就打开每个驱动程序去看是否可以处理该数据。在这个例子中,系统在注册表中如果不能找到VIDC.MSSQ“项”,它将打开所有带“VIDC”“相”指定的驱动程序去处理这些数据。
2.1.3 VCM的服务
通常,应用程序使用VCM去处理下面的任务:
l 定位,打开,或安装一个压缩器或者解压器。
l 设置或者获得压缩器、解压缩器的配置信息。
l 使用函数去压缩,解压缩或者显示这些数据。
DrawDib库的函数和宏可以完成完成这些任务,同时它可以提供更方便的方法去使用VCM。关于DrawDib库更多的信息可以,去查看MSDN中DrawDib的内容。
下面将描述使用VCM完成如下的任务:
压缩器和解压缩器基础
你可以使用ICLocate和ICOpen函数来打开和定位一个压缩器。你可以是使用ICLocate来定位一个指定类型的压缩器,并通过使用其他VCM函数来获得这个压缩器句柄。要打开一个压缩器,你可以使用ICOpen函数。你的应用程序使用该函数返回的句柄来标示一个打开的压缩器。该压缩器的句柄,在其他VCM函数中会用到。
应用程序使用ICDecompressOpen和ICDrwaOpen宏来打开和定位一个解压器,这些宏都使用ICLocate操作。
当你的应用程序使用完压缩器和解压器后,必须关闭你打开的解压器和压缩器,同时还要释放到所有用于压缩和解压的资源。你的程序可以使用ICClose函数去关闭压缩器和解压器。
同样,你的程序通过使用ICInfo函数来列举系统中所有的压缩器和解码器。
注意:在AVI文件的流的头部包含了关于流类型和流指定的处理器的信息。在流的头部内,fccType和fccHandler数据成员指定流的类型和流指定的处理器信息。
用户选择压缩器
当要压缩数据的时候,你的应用程序可以使用ICCompressorChoose函数去创建一个对话框,让用户在对话框中去选择压缩器。你可以给这个函数指定一个标志,来允许用户指定关键帧频率(Key-frame frequency)和运动数据速度(movie-data rate),或者去显示一个预览的窗体。
在ICCompressorChoose函数中,用户选择的压缩器将被自动开,并且压缩器的句柄将保存在COMPVARS数据结构体的hic数据成员中。
如果你使用了ICCompressorChoose函数,那么使用ICCompressorFree函数去关闭压缩器和释放COMPVARS对象关联的所有资源。
安装和移除压缩器和解压器
应用程序可以使用系统中已经安装的压缩器后解压器来运行在Microsoft的操作系统上。应用程序同样可以为一般用户或者特殊用户安装压缩器和解压器。大多数的应用程序不需要去安装或者移除压缩起或者解压器,因为它们通常是通过一个安装程序来安装。有的时候,有的函数可以被当作压缩器或者解压器。
应用程序通过使用ICInstall函数来安装解压器或者压缩器(或者被当作压缩器或者解压器的函数)。这个函数将在注册表中建立一个“项”去确定压缩器或者解压器。你的应用程序或者其他应用程序可以在查找注册表来确定是否有适合自己使用的解码器和压缩器。使用ICInstall安装压缩器和解压器的所有驱动程序
应用程序可以使用ICLocate和ICOpen函数来定位和打开系统中安装的解压器和压缩器。当应用程序使用完成后,使用ICClose函数来关闭它们。
应用程序使用ICRemove函数,可以在注册表中移除一个已经安装了的压缩器或者解压器的“项”。 该函数不能移除当前已经加载到内存中的压缩器或者解压器。应用程序通过安装,打开,关闭,移除操作可以限定压缩器或解压器的使用。
作为其他选择,应用程序可以使用ICOpenFunction函数,来把一个函数当做压缩器或者解码器来使用,而不需要去注册表进行安装。这个函数要求应用程序获得去进行压缩和解压函数的地址。当应用程序使用完这个函数后,必须使用ICClose来关闭该函数。因为该函数没有在注册表中进行注册,所以不用对注册表进行操作。
被当做压缩器或者解压器使用的函数的内部结构,与用于安装驱动程序DriverProc指针函数的结构一样。关于DriverPro指针函数的更多信息,你查看MSDN的 Installable Drivers。
注意: 应用程序安装了一个可以当作压缩器或解码器的函数,在应用程序关闭前,必须要移出这个函数,好让其他程序不要去尝试使用该函数。当移除这个函数的时候,应用程序把它当作采用四字符码安装的驱动一样。
压缩器和解压器的配置
你的程序可以自动配置压缩器或者解压器,或者允许用户去配置这些信息。用户可以使用配置对话框来设置压缩器或者解压器的配置信息。你可以发送ICM_CONFIGURE消息给VCM(或者使用ICQueryConfigure宏)来判断压缩器或者解码器是否支持显示配置对话框。如果支持,发送ICM_CONFIGURE消息(ICConfigure宏)来显示它。
你的程序发送ICM_GETSTATE和ICM_SETSTATE消息(ICGetStateSize,ICGetState和ICSetState宏)去获得和设置压缩器、解码器的状态。如果你的程序创建或者改变了状态,必须在恢复它们状态前去获得压缩器或者解码器数据的状态。如果你的应用程序从压缩器或者编码器获得了它们的状态,并将在后面使用它们来恢复以前的状态。要保证你获得的信息为当前的信息。
获得压缩器和编码器的信息
要得到压缩器或者解码器的信息,你可以使用ICGetInfo函数来实现。这个函数把解码器和压缩器的信息写入一个ICINFO的数据结构中。你的程序必须为ICINFO数据结构分配内存,并且在ICGetInfo中通过一个指针指向它。ICINFO数据结构体中将提供很多关于压缩器或者解码器性能的信息。
要获得压缩器或者解码器默认的关键帧频率(key-frame rate)和默认的质量值,可以发送ICM_GETDEFAULTKEYFRAMERATE和ICM_GETDEFAULTQUALITY消息,或者使用ICGetDefaultKeyFrameRate和ICGetDafaultQuality宏。
可以使用ICGetDisplayFormat函数来测量压缩器或解码器最好的显示格式。发送ICM_ABOUT消息可以检测压缩器或解码器是否能显示“关于”对话框。(或使用ICQueryAbout宏)。你同样可以显示压缩器、解码器的这个“关于”对话框,发送ICM_ABOUT消息并改变wParam的参数值(或者使用ICAbout宏)。
单张图像压缩
你可以使用ICImageCompress函数去压缩单张图像。该函数返回一个压缩的DIB位图的句柄。这个压缩的DIB位图使用CF_DIB格式。
序列压缩 (Sequence Compression)
你的应用程序可以使用ICSeqCompressFrame,ICSeqCompressFrameStart和ICSeqCompressFrameEnd函数来压缩一个帧的序列。这些函数使用保存在COMPVARS数据结构中的数据。应用程序使用ICCompressorChoose函数允许用户选择一个压缩器,打开它,并在COMPVARS中设置压缩参数;同时,应用程序也可以手动去设置这个数据结构中的参数。
在应用程序使用开始压缩一个帧序列前,必须使用ICSeqCompressFrameStart去分配必要的资源。在资源分配好后,应用程序就可以是使用ICSeqCompressFrame去压缩每个帧了。帧率(frame rate)和关键帧频率(key-frame frequency)使用COMPVARS数据结构中指定的数去进行压缩。ICSeqCompressFrame的返回值,指向被压缩的数据。
当程序完成压缩时,可以使用ICSeqCompressFrameEnd函数去释放系统资源(ICSeqCompressFrameStart分配的)。应用程序可以使用ICCompressorFree函数去释放为COMPVARS数据结构分配的系统资源。
图像数据压缩(Image-Data Compression)
你的程序可以使用一个连串的ICCompress函数和宏去压缩数据。这个函数和宏可以帮你完成下面的任务:
l 决定使用压缩格式
l 准备压缩器
l 压缩数据
l 结束压缩工作
使用ICCompress函数和宏可以增加你程序对压缩处理的控制。这组函数和宏处理帧时,是采用单独的帧处理,而不是把所有帧当成一个序列来处理。这样处理更加灵活。比如,你的程序可以使用ICCompress函数,把一些帧当作关键帧来(key-frames)处理。
压缩器接收统一格式的数据,然后压缩这些数据,并且还会返回这个指定格式的数据压缩版本号。比较典型的输入格式DIBs使用BITMAPINFO数据类型。比较典型的输出格式压缩DIBs,也使用BITMAPINFO数据结构。
Note To minimize image and audio degradation during playback, avoid compressing an AVI file more than once. Combine uncompressed pieces of video in your editing system and then compress the final product.
压缩器和压缩格式选择
如果你要按一指定输出格式压缩数据,你可以发送ICM_COMPRESS_QUERY消息(ICCompressQuery宏)去查询你的系统是否支持该格式。
如果输出格式对你的程序可能并不重要。你只想找到一个压缩器可以处理输入的格式就行了。要判断压缩器是否可以处理这种输入格式,你可以发送ICM_COMPRESS_QUERY消息,并指定lParam参数为NULL。这个消息不会返回输出格式到你的程序。你可以发送ICM_COMPRESS_GET_FORMAT消息(ICCompressGetFormatSize宏),来知道采用该压缩格式的数据需要分配的缓存大小。
要想获得压缩器在压缩时需要的最大缓存区,可以发送ICM_COMPRESS_GET_SIZE消息(ICCompressGetSize宏)。你可以使用ICSendMessage函数返回的字节(byte)数来为图像压缩分配缓存大小。
压缩初始化
你的程序选择了压缩器后,你可以使用ICM_COMPRESS_BEGIN消息来初始压缩器。(ICCompressBegin宏)。这个消息需要压缩器的句柄和输入、输出的格式。
数据压缩
你可以使用ICCompress函数去压缩一个帧。你的程序必须重复使用该函数,直到序列中的所有帧都被压缩完成。每压缩一帧图像,你的程序必须在ICCompress函数中增加压缩帧的数字。压缩器靠这个值来检测压缩情况。如果要重新压缩一帧图像,你就使用上次相同的值。如果你要压缩一个静态图形(still-frame),就让该值为0。
使用ICCOMPRESS_KEYFRAME消息可以让这些帧被压缩为一个关键帧(Key-frame)。
当完成一个帧的压缩后,VCM就把控制权交给你的应用程序,VCM就把压缩数据保存在一个数据结构体中,可通过lpbiOutput和lpData参数来引用。如果你要移动这些数据,可以在lpbiOutput参数指定的BITMAPINFO的数据结构的biSizeImage成员中,得到数据的大小。
注意 你的程序必须分配数据结构和缓存去存储没压缩的和已压缩的数据。如果压缩器支持临时压缩,你的程序还必须分配数据结构和缓存去保留以前帧的信息。
结束压缩
你的程序完成数据压缩后,可使用ICCompressEnd宏去通知压缩器它工作完成了。如果你想要使用这个函数重新压缩,你的程序必须重新初始化压缩器。同样是通过发送消息ICM_COMPRESS_BEGIN(ICCompressBegin)。
单帧图像解压
你可使用ICImageDecompress函数解压缩一个单帧图像。这个函数返回一个解压缩DIB的句柄。这个解压DIB保存在一个CF_DIB格式中。
图像数据解压
你的程序使用一连串的ICDecompressEx函数去控制解压器。这些函数可以帮助你完成下面的任务:
l 选择一个解压器.
l 解压器准备
l 数据解压
l 结束解压工作
你的程序处理数据解压的过程和数据压缩的过程类似,只不过现在的输入格式是一个压缩了的格式,输出格式是一个可以显示的格式。对于要解码的输入格式信息通常在流的头部来获得。在获得了输入的格式后,你的程序就可以使用ICLocate或者ICOpen函数去查找可以处理它的解码器。
ICDecompressEx函数和宏是对ICDecompress函数的扩从,它提供了更多的处理能力。ICDeCompressEx,ICDecompressExBegin,ICDecompressExEnd,ICDecompressExQuery在功能上替代了ICDecompress,ICDecopmressBegin,ICDecompressEnd,ICDecompressQuery函数。使用ICDecompressEx函数和宏去替换ICDecompress函数。
解压器和解压格式选择
如果你想按一种指定的输出格式来解压数据,你该使用ICDecompressExQuery函数去查询解码器看是否可以支持这中输入和输出的格式。
如果对于你的应用程序,输出格式并不重要,你只需要去查找可以处理这中输入格式的解码器就可以了。使用ICDecompressExQuery去要判断一个解码器是否可以处理这种输入格式,但是要把lpbiDst参数设为NULL。使用ICM_DECOMPRESS_GET_FORMAT消息(ICDecompressGetFormatSize宏)可以知道为采用该解压格式需要分配的缓存大小。发送ICM_DECOMPRESS_GET_FORMAT(ICDecompressGetFormat宏)可以获得格式数据。解码器在一个BITMAPINFO数据结构返回它的格式。这个格式一般保存解压期间的大部分信息。在解码器成功解压数据前,你的应用程序应该保存这些信息。
发送ICM_DECOMPRESS_GET_FORMAT消息可以获得解码器需要的默认字节数。
如果你要定义自己的格式(通过使用ICDecompresssExQuery),必须要获得这个位图的调色板。ICDecompressExQuery不通过调色板定义(大部分程序使用标准格式,而不需要获取调色板)。你的程序通过发送ICM_DECOMPRESS_GET_PALETTE消息可以获得调色板。(ICDecompressGetPalette宏)
解压器初始化
你程序选择了解码器后,你可使用ICDecompressExBegin函数去初始化这个解码器。这个函数需要这个解码器的句柄和输入、输出的格式。
数据解压
你可以使用ICDecompressEx函数去解压一个帧。你的应用程序必须重复使用这个函数,自到所有的帧都解压了。
在回放过程中,如果你的视频图像滞后(比如图像比声音延后),你的程序可以使用ICDECOMPRESS_HURRYUP标志去加速解码。使用加速解码,在解码过程中可能不会对整个帧都解码,而时取其中关键的一部分数据进行解压。所以当使用这个标志的时候,你的程序最好不要尝试把解压的数据画出来。
你的程序解压完数据后,发送ICM_DECOMPRESSEX_END消息(ICDecompressExEnd宏)去通知解码器,它工作完成了。如果你还要重新使用这个函数进行压缩,你必须要使用ICDecompressExBegin去重新初始化解码器。
监视压缩器和解码器的进度
对于解码器或者压缩器的一个长时间的操作,可以通过回调函数来让你的程序监视整个操作的进度。你可以使用ICSetStatusProc函数把回调函数的地址发送给压缩器或解码器。当它们接收到这个地址后,它们发送状态消息给函数。这些消息就标识了这个操作动作开始,停止,yielding,正在处理。
硬件绘画能力
一些Renderers可以把它们解压的视频帧直接画到硬件上去。这些Renderers在对ICGetInfo函数的响应中返回VIDCF_DRAW标志。当使用这类Render的时候,你的程序不用去处理数据解压。它为Renderer提供用于绘画的解压数据。
如果你程序使用一个代绘画能立的Renderer,就必须处理下面的任务:
l 选择一个renderer.
l 指定图像格式
l 初始画Renderer
l 把数据画出来
l 控制绘画参数
选择Renderer
ICDrawOpen宏打开一个可以按指定格式绘画的Renderer。如果成功,它返回一个Rendder的句柄,否则返回0。这个宏使用ICLocate函数去打开这个Renderer。
指定图形格式
因为你的程序不需要去画解压缩的数据,所以它不需要知道输出格式。然而,要必须保证Renderer可以使用输入格式来绘画。(通过ICM_DRAW_QUERY 或ICDrawQuery宏来实现)。这个消息不能判断Renderer是否能画一个位图。如果你的程序必须要判断Renderer是否能绘画位图,可以在ICDrawBegin函数使用这个消息。
你的程序使用ICDrawSuggestFormat函数可以得到Renderer的输入格式。这个函数用于去划分Render的解压能力和绘画能力。大多数程序使用这个函数,而不需要去知道它的输出格式。
Renderer 初始化
ICDrawBegin函数用于初始化一个Renderer,并告诉它绘画的地点。这个函数可以完成如下任务:
l 判断Renderer是否支持一个特定的输入格式
l 指定绘画操作是全屏还是仅在一个窗口上进行
l 指定使用源尺寸去显示图像
l 定义回放的图像速度
为Render提供一些缓存器去存放压缩数据,将让操作更有效率。你的程序可以发送ICM_GETBUFFERSWANDTED消息(ICGetBuffersWanted宏)去决定Render需要的缓存大小。你的程序在绘画前就应该预加载缓存并把它们发送给Render。
把数据画出来
你可以为绘画使用ICDraw函数去解压缩这些数据。这个Renderer,如果在没有发送ICM_DRAW_START消息(ICDrawStart宏)之前是不会开始进行绘画的。当你的程序调用这个函数时,这个Renderer就开始绘画了,函数的dwRate参数除以dwScale参数的值用于指定绘画帧的速度。当你的程序使用ICDrawBegin函数去初始化Renderer时,就回提供这些参数。绘画将持续到你的程序发送ICM_DRAW_STOP消息(ICDrawStop)的时候,才停止。
注意:如果在绘画前,Render缓存了一些数据,你的程序就不能使用ICDrawStart宏,除非通过ICGetBuffersWanted宏,把这些帧的数量发送给Renderer。
ICDraw函数的lTime参数指定画一帧图像所要的时间。Render通过ICDrawBegin指定的时间比例区划分这个lTime的整数,从而获得实际的时间。ICDRaw函数的时间和ICDrawStart成比例。ICDrawStart设定时钟为0。比如,你的程序指定时间比例为1000,lTime为75。那么Render绘画的为75毫秒。
控制绘画参赛
发送ICM_DRAW_GETTIME消息(ICDrawGetTime宏)可以监视Renderer的时钟,同时发送ICM_DRAW_SETTING消息(ICDrawSetTime宏)可以设置Renderer的时钟。
当一个Renderer正在绘画,这时要改变当前的位置,可以发送ICM_DRAW_WINDOW消息(ICDrawWindow宏)去重新配置窗体。一般,只要窗体改变了就使用该消息。
如果回放窗体得到了一个真实的调色板消息,你必须发送ICM_DRAW_REALIZE消息(ICDrawRealize宏)让Renderer去再次实现调色板。程序可以改变调色板,通过发送消息ICM_DRAW_CHANGEPALETTE(ICDrawChangePalette宏)来实现。要获得当前调色板可以发送ICM_DRAE_GET_PALETTE消息。
一些Renderer必须通过特定的指令才能去显示数据帧,发送如下的消息/宏可以让这些Renderer去画数据帧:ICM_DRAW_RENDERBUFFER 消息 ICDrawRenderBuff宏