在过去的几年里,视频已经成为web网页上最主流的趋势之一,这主要是由Adobe Flash Player来推动的。2007年Flash Player 9中引入了H.264和全屏支持技术,通过在web页面上的沉浸式高清视频体验,实实在在地改变了局面。最近,在移动设备上实现的Flash Player已经为Adobe公司的Flash Player项目组带来了新的思路,这些新的思路将帮助项目组解决如何在Flash中播放视频以及如何持续增强用户体验等问题。Stage video便是这些投入所得的成果。
Flash Player中渲染视频的传统方法是使用Video对象。 Video对象被视为与stage中的其它对象一样,这给了开发者一个前所未有的用来发挥创意的机会。 例如,视频可以渲染在旋转立方体的每一面,或者多个视频可以彼此融合在一起。 Video对象被等同为其它任何的DisplayObject。 图1阐明了这一点。
图1. 可以使用Video对象将多个视频融合在一起。
为了支持这一创意的发挥,Flash Player必须为每个视频帧进行大量的处理。 依靠底层基础设备的性能,这种增加的处理过程可能会降低视频的帧速率,或者还可能会增加Flash Player在CPU中的负载。
渲染视频的一种新方式
为了减少在Video对象中渲染视频对性能所造成的影响,Adobe引入了stage video作为渲染视频的新途径。 这种方法充分利用了底层的视频硬件设施。 而结果是大大地降低了CPU的负载,这便意味着在低性能设备中能表现出更高的帧率以及更少的内存使用率。 使用stage video的话,StageVideo对象并不会位于Flash Player的显示列表中,而是隐藏在stage的背后。 图2说明了这种设计方法。
图2. 隐藏在Flash stage背后的StageVideo对象。
对于电视、机顶盒以及移动设备来说,stage video的性能优势尤为明显。 这些设备没有如台式电脑那般强大的CPU,但是却具有非常强大的视频解码功能,能利用少量的CPU使用率来渲染高品质视频。 然而,即使是在台式电脑上,stage video也将显著地改变Flash Player中的视频性能。
作为一名开发人员,您必须知道,stage video在Flash Player中只是与视频GPU加速相关的第二步增强方式。第一步在于视频编码,以便充分利用目标平台上可用的硬件加速设备。因此,要获得可能的最优视频 体验,您需要从这两个步骤下手。H.264视频编解码器是stage video最佳伴侣;使用H.264视频编解码器将确保您从视频解码到视频渲染这段过程中得到全面的GPU加速。通过这种方法,将再也不需要通过 read-back(从GPU传送数据到CPU)在显示列表中合成视频帧了。YUV 4:2:0格式的视频帧通过一个GPU流处理器(DirectX9或OpenGL) ,会被转换为RGB图像,并传送到屏幕上。因此,您将会看到更高的像素保真度,并能降低CPU和内存的使用率。
限制
应用stage video,视频会被渲染于一个flash.media.StageVideo对象,而不是Video对象。 该StageVideo对象始终显示在屏幕上一个对齐窗口的矩形区域内。 其他图层可能会位于StageVideo对象的上方,但是却不可能将这些图层对象置于视频的背后。 由于StageVideo并不是位于传统的显示列表中,而是通过GPU被合成,所以在使用StageVideo对象的时候,下述功能将不可用:
在实际情况下,上述任何限制都不会影响到最常用的用例,便是其作为视频播放器应用程序的用例。 在可以接受这些限制条件的情况下,强烈鼓励开发者使用StageVideo对象。 Google TV平台以及为TV平台开发的所有AIR程序都支持Stage video,而且它即将包含在所有支持Flash的平台中。
如果您想了解更多关于TV平台和Google TV平台上AIR程序使用StageVideo的相关知识,请查阅下面的文章:ring videoDelive and content for the Flash Platform on TV 。
要求
为了确保stage video可用,任何时候您均须设置wmode="direct"。 这是极力推荐的视频播放模式。 该模式在Windows上使用Direct3D,而在Mac OS和Linux系统中使用OpenGL,以此直接通过GPU进行视频帧的合成处理。这种模式下的限制是,Flash Player运行在它自己的上下文环境中,而不可能在player的顶部有重叠的HTML内容。 如果您要使用任何其它的模式,如wmode=“window”、wmode=“opaque”,或者wmode=“transparent”,这将大大降 低stage video的可用机会(请查看注解)。 因此,为了保持一致性,强烈建议在任何时候都使用wmode="direct"。
注: 某些浏览器,比如Safari 4(或更高版本)或Internet Explorer 9,即使用如CoreAnimation(MacOS 10.6)或IE9 GPU APIs(Windows Vista/7)等类库的浏览器,允许Flash Player通过浏览器上下文环境中的GPU进行合成处理,就像使用wmode="direct"模式那样。因此,这使得在不考虑wmode参数值的情况 下允许使用stage video。但是再一次强调,为了跨浏览器的一致性,请尽量尝试使用wmode="direct"。
现在,您已经学会了stage video的相关概念及其使用限制,那么就来看看以ActionScript方式实现的话会是什么样子。
Stage Video API
以Flash Player 10.2开始,有一个名为StageVideo的新类,它代表硬件视频平面中的一个视频显示实例。 StageVideo对象由Flash Player创建,并且它无法自行实例化。 可以从Stage对象中可用的stageVideos数组中访问StageVideo对象:
var v:Vector. = stage.stageVideos;
var sv:StageVideo;
if ( v.length >= 1 ) {
sv = v[0];
}
当stageVideos属性被访问的时候,根据可用的平台和硬件设备的不同,stage.stageVideos数组的长度会有所不同。 StageVideo对象的最大数量是8。 因此,如果您想在应用程序中使用多个StageVideo对象,这在台式电脑上是完全有可行的。 而在移动平台上,只可以使用一个StageVideo对象,因此您必须考虑到这一点。 此外,数组的长度有时会是零。 如果要正确地实现stage video,您就应当时刻监听StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY事件,而不是 手动地探询stageVideos对象数组的长度。 这将告知您关于stage video的性能。
任何时候,您都可以监听这样的事件,并且等待事件的分派以做出合适的反应:
stage .addEventListener ( StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, onStageVideoState) ;
不管stage video的可用性随时间发生了怎样的变化,当注册了事件处理器之后,它将被分派一次。如果您想了解有关此操作的更多细节,请查阅“Gaining or losing stage video”章节。
在onStageVideoHandler中,依赖于StageVideoAvailability事件对象中可得到的状态属性:
private function onStageVideoState(event:StageVideoAvailabilityEvent):void {
var available:Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
}
不管stage video的可用性随时间发生了怎样的变化,当注册了事件处理器之后,它将被分派一次。如果您想了解有关此操作的更多细节,请查阅“Gaining or losing stage video”章节。
在onStageVideoHandler中,依赖于StageVideoAvailability事件对象中可得到的状态属性:
private function onStageVideoState(event:StageVideoAvailabilityEvent):void {
var available:Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
}
该状态属性可以具有以下的值:
通常情况下,一旦被告知了可用性的信息,您就可以决定该做些什么事情了。 如果stage video是可用的,那么便可从stage.stageVideos对象数组中获取到一个StageVideo对象。 如果stage video不可用,您将依赖于一个已经创建的传统的Video对象,并将它用作退却:
private function toggleStageVideo(on:Boolean):void {
//如果StageVideo可用,将NetStream附到StageVideo
if (on) {
if ( sv == null ) {
// 检索第一个StageVideo
object sv = stage.stageVideos[0];
sv.addEventListener(StageVideoEvent.RENDER_STATE, stageVideoStateChange);
}
sv.attachNetStream(ns);
} else {
video.attachNetStream(ns);
stage.addChildAt(video, 0);
} ns.play(FILE_NAME);
}
您可能已经注意到,我们在StageVideo对象上还监听了一个名为StageVideoEvent.RENDER_STATE的新事件。 请注意,通过VideoEvent.RENDER_STATE的相似事件,这个新事件在传统Video对象上也同样适用的,并且它告知我们视频是如何渲染 的:
private function stageVideoStateChange(event:StageVideoEvent):void {
var status:String = event.status;
resize();
}
在Flash Player 10.1中,我们没有办法知道视频帧是否通过GPU来进行解码或合成。 但是加入到StageVideo和Video对象中的这个新事件修复了这一局限性,这在Flash Player 10.2中开始启用。 状态属性可以有以下值,这些值可作为StageVideoEvent或VideoEvent类中的常量:
当NetStream连接到StageVideo对象时,StageVideoEvent就会被分派,或者是Video 对象的VideoEvent被分派。 例如,如果被分派的是StageVideoEvent.RENDER_STATUS_SOFTWARE,即通知您该视频正通过软件进行解码,您可以尝试切 换到另一个视频流——比如H.264或者不同尺寸的视频流——来让该视频在GPU上更好地解码。如果硬件解码突然无法使用,您可以选择一个非GPU加速的 编码解码器来强制通过软件进行解码。 这样的事件同样可用在调试或记录日志中跟踪用户体验。
正如您所看到的,您也可以使用这个事件来改变视频的大小,因为该事件也会告知什么时候可以从Video或StageVideo对象中获取视频的尺寸,并根据约束条件计算得到视频的最终宽度和高度。
private function resize ():void {
rc = computeVideoRect(sv.videoWidth, sv.videoHeight);
sv.viewPort = rc;
}
请记住,StageVideo不是一个DisplayObject,因此它并不会实现您在Flash中用来定位和缩放DisplayObject的所有预期属性。 上面的代码使用了viewPort属性来指定屏幕中视频的尺寸,而不是使用那些预期的如宽度和高度的属性。
StageVideo对象公开了下列属性:
请注意,StageVideo实例是按顺序渲染的。 stageVideos数组中的第一个StageVideo对象会最先渲染;下一个对象将渲染在前一个的上方。 要改变这一点,您可以使用depth属性来手动地改变顺序:
sv.depth = 0 ; sv2.depth = 1 ;
您可能已经注意到,StageVideo对象公开了一个colorSpaces属性,该属性返回由当前硬件进行处理的色彩空间的相关信息。 现在您会发现这将是多么地有用。
使用色彩空间
colorSpaces属性能够在StageVideo对象中被访问,并返回一个字符串数组:
var colorSpace: Vector.< String> = stageVideo.colorSpaces;
色彩空间名称列举在flash.media.VideoColorSpace类中:
VideoColorSpace.BT601 = "BT.601";
VideoColorSpace.UNKNOWN = "unknown";
VideoColorSpace.BT709 = "BT.709";
VideoColorSpace.SMPTE_240M = "SMPTE-240M";
VideoColorSpace.USFCC:String = "USFCC";
请注意,播放器会尝试将StageVideo的色彩空间与视频流的色彩空间相匹配。 在某些机器的配置中,如果这一匹配不能满足,Flash Player就会尝试查找最接近的匹配。
请记住,有些视频容器可能嵌入了与视频流的原始色彩空间相关的信息。 因此,Flash Player必须考虑到这一点。 一种常见的情况是H.264,其中的视频流通常被编码成“BT.709”——而色彩空间属性可能返回“BT.601”,其意思是说底层的 OS/graphics硬件设备没有能力在“BT.709”色彩空间中渲染视频平面。 如果发生这种情况,您可以重新使用软件合成(通过使用一个传统Video对象),或者是接受颜色的不匹配。 如果返回“unknown”时,平台将不能查询当前实际使用的色彩空间。
获取或释放stage video
当SWF文件被实例化的时候,Stage video可能无法使用,但它可能会在一段时间之后变得可用。 您可能会问,这是为什么呢? 正如我前面解释过的,当整合SWF文件时,您选择的wmode值会决定stage video跨浏览器的一致性。 在某些情况下,您可能无法设置wmode="direct"-——比如这可能是在HTML页面中的整合问题所导致的。 此外,在全屏模式下,由于Flash Player并不会在浏览器上下文中运行,因此stage video可以不考虑wmode的值而始终可用。 但是请注意,当离开全屏模式时,stage video可能又会变得不可用,然后将您的SWF文件重新放置于浏览器上下文环境中,此时stage video受到wmode参数的影响。
请注意: 由于该问题的存在,在利用StageVideo API的视频播放器中,您应当始终使有一个传统Video对象作为备用。
因此,您需要通过某种方式来构造视频播放器,以作出合适的反应。 幸运的是,这是相当容易的事情。 如果stage video变得可用,只需将NetStream对象附加到刚刚获取的St通常,您需要按照如下方式来修改toggleStageVideo函数:
private function toggleStageVideo(on:Boolean):void {
// 如果StageVideo可用,将NetStream附加到StageVideo
if (on) {
stageVideoInUse = true;
if ( sv == null ) {
sv = stage.stageVideos[0];
sv.addEventListener(StageVideoEvent.RENDER_STATE, stageVideoStateChange);
}
sv.attachNetStream(ns);
if (classicVideoInUse) {
// 如果使用StageVideo,仅仅只需将Video对象从显示列表中删除
// 以避免覆盖StageVideo对象
//(该对象总是在后台运行)
stage.removeChild ( video );
classicVideoInUse = false;
}
} else {
// 否则,将它附加到Video对象
if (stageVideoInUse)
stageVideoInUse = false;
classicVideoInUse = true;
video.attachNetStream(ns);
stage.addChildAt(video, 0);
}
if ( !played ) {
played = true;
ns.play(FILE_NAME);
}
}
这段代码可以妥善地处理当stage video变得可用或不可用时退却到一个传统Video对象的情况。 当把NetStream重新附加到StageVideo对象时,由于此时必须分配硬件资源,所以在附加NetStream和在屏幕上看到像素这两个时刻之 间,您可能会看到一点延迟。
如果您想了解更多关于如何利用StageVideo API的信息,请参阅本文的附件。 附件中包含了一个简单的视频播放器,用来对各种不同场景进行说明。
场景
正如您在整篇文章中看到的,在Flash Player中播放视频时,可能会遇到不同的场景。 下面列出了您在使用Video或StageVideo对象来播放视频时可能遇到的不同场景路径
延伸与意义
请务必尝试用面向开发者的Adobe Flash Player 10.2 beta 版本,在介绍新特征和增强功能的Adobe Labs中,现在包含了一个新的视频硬件加速模型,该模型使得视频播放性能得到了显著的增强。
为了充分体验这一新推出的StageVideo API,Adobe已经引入了两处深受关注的小改进:
StageVideo API毫无疑问将会改变视频播放的性能。 在某些场景下,stage video可以将处理器的使用率降低高达85个百分点。 即便对stage video的使用有些要求,但还请务必尽可能地使用它。 这将允许开发者充分利用视频渲染流水线的全面硬件加速,而这将带来一流的视频播放性能。