Flash神奇的视频利器StageVideo
在过去的几年里,视频已经成为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阐明了这一点。
为了支持这一创意的发挥,Flash Player必须为每个视频帧进行大量的处理。 依靠底层基础设备的性能,这种增加的处理过程可能会降低视频的帧速率,或者还可能会增加Flash Player在CPU中的负载。
渲染视频的一种新方式
为了减少在Video对象中渲染视频对性能所造成的影响,Adobe引入了stage video作为渲染视频的新途径。 这种方法充分利用了底层的视频硬件设施。 而结果是大大地降低了CPU的负载,这便意味着在低性能设备中能表现出更高的帧率以及更少的内存使用率。 使用stage video的话,StageVideo对象并不会位于Flash Player的显示列表中,而是隐藏在stage的背后。 图2说明了这种设计方法。
对于电视、机顶盒以及移动设备来说,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对象不能被旋转。 只可能做到正交旋转(以90度的增量旋转)。
- StageVideo对象可能不能适用colorTransform或3D转换变形。 它没有一个适用的矩阵变换功能来对视频进行倾斜处理。
- StageVideo对象无法适用alpha 通道、混合模式、滤光器、蒙版或者scale9Grid这些功能。
- 其视频数据不能被复制到BitmapData对象(BitmapData.draw)中。
- 视频不能以位图格式缓存。
- 视频数据不能嵌入在SWF文件中。 StageVideo只能应用于源自NetStream对象的影片。
- 依赖于底层硬件设备,一些色彩空间可能不被支持。 在这种情况下,Flash Player将选择一个替代的色彩空间。 新的StageVideo ActionScript API提供了一种方式用来查询正在使用的色彩空间。
- 依赖于平台,在视频平面中允许显示的视频数量是有限的。 在大多数移动系统中,在全局范围任何时间内只允许播放一个视频。 这意味着,如果您有几个SWF文件的实例同时显示的话,只有第一个SWF文件能以硬件加速的方式显示。
- 为了保证台式电脑和TV设备中Flash Player的一致性,请将wmode设置为direct。
- 请避免让wmode="transparent"的SWF文件彼此相互层叠。 某些平台不支持wmode="transparent"模式,如Google TV。 这意味着当wmode ="window"时,无论<embed> 标签参数是何值,所有的SWF实例都能被平台支持。
在实际情况下,上述任何限制都不会影响到最常用的用例,便是其作为视频播放器应用程序的用例。 在可以接受这些限制条件的情况下,强烈鼓励开发者使用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.<StageVideo> = 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); }
该状态属性可以具有以下的值:
StageVideoAbility.AVAILABLE
:Stage video可用;在stageVideos对象数组中至少有一个StageVideo
处于等待状态。StageVideoEvent.UNAVAILABLE
:Stage video不可用;stageVideos对象数组为空。
通常情况下,一旦被告知了可用性的信息,您就可以决定该做些什么事情了。 如果stageVideos是可用的,那么便可从stage.stageVideos对象数组中获取到一个StageVideo对象。 如果stageVideos不可用,您将依赖于一个已经创建的传统的Video对象,并将它用作退却:
private function toggleStageVideo(on:Boolean):void { // if StageVideo is available, attach the NetStream to StageVideo if (on) { if ( sv == null ) { // retrieve the first 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
类中的常量:
StageVideoEvent.RENDER_STATUS_ACCELERATED
:视频将通过GPU进行译码和合成。StageVideoEvent.RENDER_STATUS_SOFTWARE
:视频通过软件进行解码,并通过GPU(如果由StageVideo分派)或者是软件(如果由Video进行分派)进行合成。StageVideoEvent.RENDER_STATUS_UNAVAILABLE
:视频硬件已停止解码和合成视频。
当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对象公开了下列属性:
colorSpaces:Vector.
:在底层硬件设备上可用的颜色空间。depth:int:StageVideo
对象的深度。这个属性允许您在多个StageVideo对象之间处理z-ordering。pan:Point
:平移(类似于X和Y);必须指定一个Point对象。 默认情况下,pan的值为(0,0)。videoHeight:int
: 视频流的原始高度;为只读属性。videoWidth int
:视频流的原始宽度;为只读属性。viewport:Rectangle
:可见的表面(类似于宽度和高度);必须指定一个Rectangle对象。zoom:Point
:缩放因子;必须指定一个Point对象。 默认情况下,zoom的值是(1,1)。
请注意,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 { // if StageVideo is available, attach the NetStream to StageVideo if (on) { stageVideoInUse = true; if ( sv == null ) { sv = stage.stageVideos[0]; sv.addEventListener(StageVideoEvent.RENDER_STATE, stageVideoStateChange); } sv.attachNetStream(ns); if (classicVideoInUse) { // If using StageVideo, just remove the Video object from // the display list to avoid covering the StageVideo object // (always in the background) stage.removeChild ( video ); classicVideoInUse = false; } } else { // Otherwise attach it to a Video object 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对象来播放视频时可能遇到的不同场景路径
- Video对象发挥着非加速编解码器的作用:将会使用CPU来进行解码和合成。
- Video对象发挥着GPU加速编解码器(H.264)的作用:将使用GPU来解码,但CPU仍可用于合成。
- StageVideo对象发挥着非加速编解码器的作用:CPU将用于解码,但GPU将用于合成。
- StageVideo对象发挥着GPU加速编解码器(H.264)的作用:GPU将用于解码和合成。CPU在整个流水线中将不会使用到。这就是您所希望达到的最佳性能的"direct path"场景。
延伸与意义
请务必尝试用面向开发者的Adobe Flash Player 10.2 beta版本,在介绍新特征和增强功能的Adobe Labs中,现在包含了一个新的视频硬件加速模型,该模型使得视频播放性能得到了显著的增强。
为了充分体验这一新推出的StageVideo API,Adobe已经引入了两处深受关注的小改进:
- 支持多个显示器的全屏模式:全屏内容在第二台显示器上将保持全屏模式,从而允许用户在另一台显示器上工作的同时观看全屏内容。
- 全屏检测能力:新的allowFullScreen属性现在已经适用于Stage对象,它使得开发人员能够检测当前容器/托管网页是否允许播放器全屏显示。
StageVideo API毫无疑问将会改变视频播放的性能。 在某些场景下,stage video可以将处理器的使用率降低高达85个百分点。 即便对stage video的使用有些要求,但还请务必尽可能地使用它。 这将允许开发者充分利用视频渲染流水线的全面硬件加速,而这将带来一流的视频播放性能。