html5 video 实现浅析,HTML5 Video 实现浅析

HTML5 Video实现浅析

——基于android4.1browser

1.     基本结构

0818b9ca8b590ca3270a3433284dd417.png

先上一张基本结构图,图中展示了webkit中与HTML5 Video实现相关的一些类。

1.1  DOM Tree

Video标签在webkit内部对应于HTMLVideoElement类,该类的功能并不多,大多数的功能都在其父类HTMLMediaElement中。

1.2  Render Tree

HTMLVideoElement和HTMLMediaElement分别对应的Render节点是RenderVideo和RenderMedia。它们也一般的Render节点不同,它们只负责layout来确定位置信息,而并不需要绘制(虽然有paint函数,但实际并没有调用到)。

1.3  Bridge

MediaPlayer类指的是WebKit中定义的MediaPlayer类(而不是系统定义的),它只是一个桥接类,相关功能交由MediaPlayerPrivateInterface类实现,并将某些反馈信息回调给HTMLMediaElement。

1.4  Player

按照webkit的一贯风格,MediaPlayerPrivateInterface只是一个基类,具体需要不同的平台来提供具体实现。Android平台下为MediaPlayerPrivate类,它也是一个基类,分别由MediaPlayerVideoPrivate和MediaPlayerAudioPrivate来实现。很显然,它们分别对应Video和Audio的实现。

上述是webkit内的类,下面再看看webkitporting之后外部的类结构:

0818b9ca8b590ca3270a3433284dd417.png

可以看到,这些类大多数都是轻量级类,它们主要负责功能转发。最终实现类是android中的MediaPlayer类(请与前述MediaPlayer区别开来)。至于该MediaPlayer再怎么实现,已不在本文的讨论范围内。值得注意的是,HTML5VideoViewProxy类继承自Handler,它转发到VideoPlayer的消息使用了android的异步调用机制。

2.     操作控制

同过上述基本结构的介绍,我们可以想象一个播放器操作,如play,seek等,它们从HTMLMediaElement发出,最终一步一步到达android的Mediaplayer。下面是一个seek过程的调用堆栈:

#0  WebKit::HTML5VideoViewProxy::seek

#1 WebCore::MediaPlayerPrivate::seek

#2 WebCore::MediaPlayer::seek

#3  WebCore::HTMLMediaElement::seek

#4 WebCore::MediaControlTimelineElement::defaultEventHandler

当然基本流程是这样,有时候也不是一成不变的,比如play就有多种方式,多种流程来实现。具体问题具体分析,打一个断点一切流程都一目了然。

0818b9ca8b590ca3270a3433284dd417.png

上图展示的是一个播放器状态转移情况,只有这么几种状态,还是很好理解的,这里就不赘述了。

3.     渲染流程

上述业务操作逻辑比较简单,我们更感兴趣的是video的显示过程,也就是渲染流程。前面说过,RenderVideo虽然有paint函数,但并没有派上用场。那是怎样完成渲染的呢?还需要从webkit的渲染结构说起。

0818b9ca8b590ca3270a3433284dd417.png

这张图片所展示的内容大家应该都很熟悉,是webkit中的基础结构。但这还没有完,在硬件加速渲染下,还需要更多的结构来支持:

0818b9ca8b590ca3270a3433284dd417.png

GraphicsLayer也是一个基类,在android下的实现为GraphicsLayerAndroid,该类包含一个LayerAndroid类,才是硬件加速用到的Layer。当然LayerAndroid也是一个基类,Video对应的是VideoLayerAndroid类。可以简单这样理解,一个Video标签在硬件加速部分对应于一个VideoLayerAndroid类,用于显示Video内容。

那么我们看看VideoLayerAndroid是怎么创建的,如下堆栈:

#0 VideoLayerAndroid

#1 MediaPlayerPrivate

#2 MediaPlayerVideoPrivate

#3 WebCore::MediaPlayerPrivate::create

#4 WebCore::MediaPlayerPrivate::create

#5  WebCore::MediaPlayer::loadWithNextMediaEngine

#6 WebCore::MediaPlayer::load

#7 WebCore::HTMLMediaElement::loadResource

可以看到,load操作引起了一系列初始化,其中VideoLayerAndroid由MediaPlayerPrivate来初始化。这个过程与GraphicsLayerAndroid没有关系,也就是说VideoLayerAndroid并没有与webkit的主体结构联系起来。事实上另有玄机:

#3 WebCore::GraphicsLayerAndroid::setContentsToMedia

#4  WebCore::RenderLayerBacking::updateGraphicsLayerConfiguration

#5 WebCore::RenderLayerCompositor::updateLayerCompositingState

#6  WebCore::RenderLayer::contentChanged (

#7  WebCore::RenderVideo::updatePlayer

#8 WebCore::HTMLMediaElement::mediaPlayerSizeChanged

#9  WebCore::MediaPlayer::loadWithNextMediaEngine

上述流程最终调用到GraphicsLayerAndroid::setContentsToMedia,会将VideoLayerAndroid设入GraphicsLayerAndroid,这样创建的VideoLayerAndroid就与webkit的主体结构联系在一起,成为硬件加速LayerAndroid树的一部分。

VideoLayerAndroid中有一个drawGL函数,就是该Layer的绘制函数。其中有一个非常重要的成员m_surfaceTexture, 它是一个SurfaceTexture类型。正是通过调用SurfaceTexture::updateTexImage来绘制的GL纹理。那么m_surfaceTexture成员是怎么得到的呢?看下面堆栈:

#0 WebCore::VideoLayerAndroid::setSurfaceTexture

#1 android::SendSurfaceTexture

#2 WebKit::HTML5VideoViewProxyDelegate::nativeSendSurfaceTexture

#3  WebKit::VideoPlayer::setBaseLayer

可见SurfaceTexture是在外部创建(事实上有HTML5VideoInline类创建),并传递给VideoLayerAndroid。另外setSurfaceTexture函数还会将VideoLayerAndroid的信息设置给VideoLayerManager类。

现在从webkit硬件加速的角度看,绘制需要的对象已经准备就绪。主要是video所对应的硬件加速层VideoLayerAndroid,以及绘制GL纹理所需要的类SurfaceTexture(通过updateTexImage函数来绘制)。

SurfaceTexture既然在webkit外部创建,那它在外部是怎样被使用的呢?可以看HTML5VideoInline::decideDisplayMode函数。先用SurfaceTexture构造一个Surface,然后将该Surface设置给MediaPlayer。MediaPlayer做的事情就是,从站点上下载video数据,并进行解码,解码后的结果输出给Surface,再经过SurfaceTexture:: updateTexImage来最终成为GL纹理。看下图:

0818b9ca8b590ca3270a3433284dd417.png

该图大体上描述了整个HTML5 Video的控制,渲染的主要类的关系。该图中大部分内容前文已经介绍过了,那么我们关注一点,解码的结果怎样从surface传送到surfacetexture,因为只有内容在surfacetexture才能被绘制成GL纹理。

其实这个过程曾经深入研究过android surface系统的同学应该很熟悉。我在这里简单介绍一下。Surfacetexture创建的时候也会创建一个BufferQueue,这才是一个重要的类,它维护了一个共享内存的队列。Surface通过binder机制与BufferQueue通信。Surface每次向BufferQueue申请一块空的内存,解码结果就保存在这块内存中。之后将装满的内存还给BufferQueue。SurfaceTexture:: updateTexImage的时候,会向BufferQueue请求一块满的内存,并将其内容渲染成GL纹理,随后将空内存还给BufferQueue。可以说Surface与SurfaceTexture形成了一个经典的生产者—消费者模式。

这样HTML5 Video的大体渲染框架就搭建完成了。

4.     控制条

播放时,与Video相配套的还有一个控制条,它是完全由webkit来实现的。控制条所涉及的元素都定义在MediaControlElement.h文件中。包括开始按钮,时间条,全屏按钮等元素。这些元素同时也负责接收并处理触摸事件。例如MediaControlTimelineElement对应于时间条,它的defaultEventHandler函数处理时间条相关的事件,比如时间改变,会导致调用seek函数。这个过程在前面已经介绍过了。

上述的所有元素都被添加到MediaControlRootElement上,而MediaControlRootElement又被添加到ShadowRoot上。熟悉前端技术的同学可能对ShadowRoot比较熟悉了,没错,控制条是在一棵Shadow DOM子树中,游离于主DOM树之外的。

控制条并不是一个HTML DOM节点,当然也就没有对应的Render节点。它的绘制在RenderSkinMediaButton类的draw函数中完成。

从该函数中看,控制条中相关元素(如play按钮)对应的是一个RenderBox类,然后根据其RenderStyle中的ControlPart熟悉来确定究竟是什么按钮,应该进行怎样绘制。

5.   全屏模式

控制条全屏按钮可以使视频进入全屏模式,全屏模式的绘制方式与普通模式不太相同,全屏模式专门有一个类HTML5VideoFullScreen来支持,它是HTML5VideoView的子类。

按全屏按钮后流程如下:

#0  WebKit::HTML5VideoViewProxy::enterFullscreenForVideoLayer

#1  WebKit::HTML5VideoViewProxyDelegate::enterFullscreenForVideoLayer

#2  WebCore::MediaPlayerVideoPrivate::enterFullscreenMode

#3  WebCore::MediaPlayerVideoPrivate::enterFullscreenMode

#4  android::ChromeClientAndroid::enterFullscreenForNode

#5  android::ChromeClientAndroid::enterFullscreenForNode

#6  WebCore::HTMLMediaElement::enterFullscreen

#7  WebCore::HTMLMediaElement::enterFullscreen

#8  WebCore::MediaControlFullscreenButtonElement::defaultEventHandler

#0  VideoSurfaceView

#1  HTML5VideoFullScreen

#2  WebKit::VideoPlayer::enterFullScreenVideo

#3  WebKit::HTML5VideoViewProxy::handleMessage

#4  gaia::Handler::dispatchMessage

中间经过了WebCore线程到UI线程的转换,可见在HTML5VideoFullScreen 中创建了一个VideoSurfaceView对象。VideoSurfaceView是一个SurfaceView,在SurfaceView创建时会触发如下事件:

#0  WebKit::HTML5VideoFullScreen::attachMediaController

#1  WebKit::HTML5VideoFullScreen::setMediaController

#2  WebKit::HTML5VideoFullScreen::prepareForFullScreen

#3  WebKit::HTML5VideoFullScreen::VideoSurfaceHolderCallback::surfaceCreated

在attachMediaController 函数中将VideoSurfaceView设置给MediaController,这样MediaController解析的视频直接输出给VideoSurfaceView来显示(在HTML5VideoFullScreen 是用MediaController来播放视频,MediaController相当于一个带操作条的MediaPlayer),这样就没有了上述普通模式那样复杂的显示过程。全屏模式的显示过程与webkit是没有一毛钱关系的,完全由android ui控件来显示。那么VideoSurfaceView怎样与浏览器的UI联系起来呢? 通过onShowCustomView回调。将控件回调给浏览器UI,与UI联系起来。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值