android webkit HTML5 video研究
摘要:本文主要介绍 HTML5 video 在 android2.2 中实现的主要架构和程序流程。
一、 实现 HTML5 video 主要的类
1. 主要类结构及介绍
图 1 中绿色类为 java 类,其余为 c++ 类,下面是各个类的具体介绍 :
(1) HTMLElement 类不是最上层类,其父类可追到为 Node 类。为了表述方便省去了上面的类继承结构。该类是一个通用基类,大部分 HTML 元素都需要继承该类。
(2) MediaPlayerClient 类是一个接口类, HTMLMediaElement 以私有方式继承了部分函数,主要作用是媒体播放状态改变时通过 MediaPlyer 在 MediaPlayerPrivate 中调用。
(3) HTMLMediaElement 类完成了 video 元素大部分行为和属性的定义。包括 audio 元素也是继承该类。
(4)HTMLVideoElement 类实现了很少的功能,在 android 中没有涉及该类。
(5)MediaPlayer 连接播放功能类和媒体元素类的交互类。在 HTMLMediaElement 中定义了该类的成员指针变量 m_player ,在完成播放等功能时将会调用该类。
另外在该类定义了一个 Media 引擎容器,用来保存所有注册的可提供播放支持的类,比如 MediaPlayerPrivate 。在需要时会根据媒体资源的需要选择最好的支持类。
(6) MediaPlayerPrivateInterFace 是一个纯虚函数类,定义了主要和播放相关的函数。在 MediaPlayer 中定义了该类的成员智能指针变量 m_private 。
( 7 ) MediaPlayerPrivate 该类继承了 MediaPlayerPrivateInterface 类。该类主要用于和 HTML5VideoviewProxy 类交互。定义了一个 HTML5VideoviewProxy 类的代理结构成员变量 m_glue ,用来保存 HTML5VideoviewProxy 类的实例和方法。
(8) HTML5VideoviewProxy 提供具体的播放。该类通过调用系统系统 videoview.java 类来实现播放。定义了两个静态内部类,一个用来完成具体播放调用。另一个用来完成 video poster 的下载。另外该类会调用 webview.java 的 chromeclient 类成员来实现 videoview 的显示和隐藏,默认 poster 的资源获取, progress view 的获取。所以要支持 HTML5 video ,上层 webview 应用必须重载 chromeclient 类的相关函数。
2. 主要类架构和功能分析
从上面简要分析可以看出主要类分了三类:
一类是 video 元素类 (HTMLElement, HTMLMediaElement , HTMLVideoElement)
第二类是架构类和接口 (MediaPlayerClient , MediaPlayer , MediaPlayerPrivateInterface) ;
第三类是具体实现播放功能的类 (MediaPlayerPrivate, HTML5VideoviewProxy ) 。该部分是我可以移植的,可以添加自己的播放类。
既然可以实现动态添加功能模块,那么需要遵守什么。通过源码可以看出播放类需要实现:提供静态注册函数,用来注册自己;对象创建函数 ( 调用构造函数的函数 ) ;支持的数据类型函数;支持的类型和解码器名字函数。这样 MediaPlay 在创建播放类时会根据支持情况选择。
另外,播放类还必须实现 MediaPlayerPrivateInterface 中定义的函数,这样 MediaPlay 才能通过接口调用具体播放功能;在播放类中也可以调用 MediaPlay 中的关于播放状态变化等函数,然后 MediaPlay 通过 MediaPlayerClient 接口类调用 HTMLMediaElement 中具体的实现。将播放状态返回给媒体元素。
二,视频元素处理流程
在程序完成下面两个流程后,就可以播放一个视频资源。
1. 类实例初始化流程
浏览器引擎加载完 HTML 文档后,开始解析里面的元素搭建 DOM tree, Render tree 和加载子资源。在解析 video 元素时,会创建 HTMLVideoElement 类对象和对应的 RenderVideo 对象 ( 因为大部分行为是在基类 HTMLMediaElement 中完成 , 所以下图标识的是 HTMLMediaElement ) 。对于 src 属性引擎会调用 HTMLMediaElement 的 loadResource 函数,如果没有该属性或 source 子元素,就不会有下面的流程。
图 2 表示了 HTMLMediaElement 的 loadResource 函数内部的主要调用流程。首先调用 MediaPlay 的 create 函数。然后调用 MediaPlayer 的 load 函数,在 load 函数中会注册媒体引擎,并根据 content type 选择最合适的 engine 。然后通过 engine 调用对应播放类的实例创建函数,然后调用播放类的 load 函数保存媒体资源的 url ,返回到 MediaElement 。接着判断是 poater url 是否存在,若存在保存到播放类。接下来媒体元素会调用 RenderVideo 的 updateElement 函数。在 updateElement 函数中调用 updatePlayer 并调用到 MediaPlay 的 setvisible 函数,然后调到播放类的 setvisible 并创建 java 播放类和设置 video poster 。 Rendervideo 是怎么调用到 MediaPlay 的函数呢。因为在 Element 对象和 Render 对象中都保存对方的指针,在 MediaElement 中又保存了 MediaPlyer 指针。引擎在创建 Element 后,便会调用 element 的 attach 函数创建对应的 Render 对象。
2. 播放调用序列
在 js 中调用 video.play() 方法时,程序会调用到 HTMLMediaElement 的 play 函数。最终会调用到 HTML5VideoviewProxy 的 play 函数。
三、 总结 :
对于媒体资源的回放,解码是最大的难题。既然想通过浏览器来直接支持视频播放,那么也必须考虑这个问题。所以在源码中有 Media engine vector 的设计,用来根据 contentType 选择最佳 media engine 。在目前 android2.2 源码中只有 Mediaplayerprivate 一个 media engine 。而该播放类,最终也是调用了系统 java 接口 videoview 。并传给了媒体资源的 url 。对于解码的支持完全取决于终端系统。
Android 对 video 元素支持很有限。比如 video 的 controls 、 autoplay 、 preload 等都没支持。对于 autobuffer 的值虽然有设置,但是对于播放器并没有用到。对于 HTML5 中定义的很多规则都没有实现,所以, android 对 HTML5 video 的支持很有限。
在 android 中 HTML5 video 有自己的特性。从上面的流程中可以看出,虽然架构设计了通过元素来控制播放和将播放状态从播放类返回给元素,但在实际播放按键响应和控制由 videoview 完成, js 也可以控制播放 ( 比如一开始的 play 调用 ) 。 播放界面是 videoview 的 controller 提供 , 和引擎里面定义的 RenderMediaController 没有关系,可能在 chrome 中播放出现的控件是该类画出来的。
另外,在 android 中并不支持 <audio> 元素。