HTMLDocumentParser.cpp 解析HTML源文件遇到video标签时,
会调用自动生成的HTMLElementFactory中的方法createHTMLElement()
这个方法根据tag name 在ConstructorFunctionMap中找到videoConstructor。
并调用HTMLElementFactory::videoContructor.
videoContructor中调用了HTMLVideoElement::create.
由于HTMLVideoElement继承自HTMLMediaElement,create()在调用HTMLVideoElement的构造函数时,同时调用
HTMLMediaElement。video 标签播放的主要流程在HTMLMediaElement中。
HTMLMediaElement.cpp这个类是webkit 用以表示video标签的类HTMLVideoElement的基类。
MediaControlElement.cpp这个类是webkit 自带的控件类。
MediaControlRootElement.cpp这个类是webkit自带的控件类的父类,即所有的控件类都是这个类的child.
由于android4.0.1中全屏播放时使用的是android的控件类,所以我们不考虑webkit自带的控件类。
以下是android4.0.1的video 标签播放涉及到的主要类的类图。
类图及顺序图下载地址
先说明这些类之间的关系。
HTMLMediaElement除了继承自HTMLElement外,还继承了MediaPlayerClient,这个类定义了一些回调接口,这些接口提供给WebCore::MediaPlayer使用。
WebCore::MediaPlayer通过这些接口来通知HTMLMediaElement播放器状态的改变,如network state,ready state,volume state,mute state等。
HTMLMediaElement在创建WebCore::MediaPlayer时,会把指向自身的指针作为参数传给MediaPlayer的构造函数。这样MediaPlayer就可以回调HTMLMediaElement中实现的MediaPlayerClient的接口。
HTMLMediaElement中播放相关的工作是调用WebCore::MediaPlayer完成的。
WebCore::MediaPlayer中包含一个指向MediaPlayerPrivateInterface的指针,具体工作是调用这个接口实现的。
这个接口的具体实现类是MediaPlayerPrivate,
MediaPlayerPrivate有两个子类MediaPlayerVideoPrivate和MediaPlayerAudioPrivate。
播放视频时调用的MediaPlayerPrivateInterface接口的具体实现是MediaPlayerVideoPrivate。
播放音频时调用的MediaPlayerPrivateInterface接口的具体实现是MediaPlayerAudioPrivate(对于Audio标签)。
MediaPlayerPrivate含有一个指向WebCore::MediaPlayer的指针,
通过这个指针调用WebCore::MediaPlayer的接口通知播放器状态的改变,这些状态就是上面提到的WebCore::MediaPlayer
需要通知给HTMLMediaElement的那些播放器状态,如network state,ready state,volume state,mute state等。
WebCore::MediaPlayer在创建MediaPlayerPrivate时,同样会把指向自身的指针作为参数传给MediaPlayerPrivate的构造函数。这样MediaPlayerPrivate就可以回调WebCore::MediaPlayer的接口来通知播放器状态的改变。
MediaPlayerPrivate以及MediaPlayerVideoPrivate,MediaPlayerAudioPrivate的具体实现在MediaPlayerPrivateAndroid.cpp中。
MediaPlayerPrivate是 HTML5VideoViewProxy.java的jni层。
MediaPlayerPrivate及其两个子类的具体实现是通过jni调用HTML5VideoViewProxy.java中的操作完成的。
HTML5VideoViewProxy.java有一个内置类VideoPlayer,封装了所有的播放器操作,
在HTML5VideoViewProxy::VideoPlayer内部包含了HTML5VideoView类,
HTML5VideoView类包含MediaPlayer类,这个类就是android 提供的java层播放器。
所以实际的播放动作都是通过调用MediaPlayer接口完成的。
HTML5VideoView有两个子类HTML5VideoFullScreen和HTML5VideoInline分别对应全屏和小屏的播放状态。
说明一下HTML5VideoViewProxy.java的结构。
内置类VideoPlayer封装了传向HTML5VideoView的播放器接口调用。
play(),seek(),pause()都是由MediaPlayerPrivate通过jni调用的,用于向HTML5VideoViewProxy发送消息。
dispatchOnEnded(),dispatchOnPaused(),sendTimeupdate()都是HTML5VideoViewProxy用来向MediaPlayerPrivate发送消息的。
HTML5VideoViewProxy与它的jni层MediaPlayerPrivate之间存在双向的信息交流。
所以HTML5VideoViewProxy中有两个处理消息的Handler.
handleMessage()中创建的handler用于处理MediaPlayerPrivate发给HTML5VideoViewProxy的消息。消息的具体处理是在HTML5VideoViewProxy::VideoPlayer的接口实现中。
createWebCoreHandler()中创建的mWebCoreHandler用于处理HTML5VideoViewProxy发给MediaPlayerProxy(即MWebCore)的消息。消息的具体处理在MediaPlayerPrivate的native 方法中。
下面的顺序图是从HTMLMediaElement开始的video标签的autoplay过程。
顺序图下载地址
重点记录下HTMLMediaElement和WebCOre::MediaPlayer,MediaPlayerPrivate,Android::HTML5VideoViewProxy之间是怎样建立关联的。
HTMLMediaElement和WebCore::MediaPlayer之间的关系建立比较直接。
HTMLMediaElement继承了WebCore::MediaPlayerClient,WebCore::MediaPlayer的构造函数需要WebCore::MediaPlayerClient*作参数。
同时HTMLMediaElement含有WebCore::MediaPlayer*成员变量,所以HTMLMediaElement在构建类型为WebCore::MediaPlayer*的成员变量时会将自身作为参数传进去。
WebCore::MediaPlayer与MediaPlayerPrivateInterface的具体平台实现类之间关系的建立。
上面顺序图步骤10---18是WebCore::MediaPlayer与MediaPlayerPrivateInterface的android平台实现类MediaPlayerPrivate建立联系的具体过程。
MediaPlayerPrivateInterface的具体实现类是平台相关的,所以WebCore采用工厂模式实现具体平台的对接。
MediaPlayer.cpp中封装了不同平台的差异。
在MediaPlayer.cpp中的WebCore全局方法installedMediaEngines()中会调用当前平台的registerMediaEngine(),在android中调用的就是位于MediaPlayerPrivateAndroid.cpp
中的MediaPlayerPrivate::registerMediaEngine(),这个方法有一个函数指针作为参数。
这个函数指针的具体实现是MediaPlayer.cpp中的WebCore全局方法addMediaEngine()这个方法的作用就是为具体的平台相关的MediaPlayerPrivateInterface实现类
创建工厂类。
addMediaEngine()的参数是六个函数指针,这些函数指针是创建MediaPlayerFactory需要的参数。其中第一个函数指针对应的就是MediaPlayerPrivateInterface具体实现类的创建函数。
MediaPlayerPrivate::registerMediaEngine()在调用WebCore全局方法addMediaEngine()时将自身的create函数作为第一个参数传给了addMediaEngine()。
addMediaEngine又以MediaPlayerPrivate::registerMediaEngine()传进来的参数为参数构造了可以创建MediaPlayerPrivate的MediaPlayerFactory.
所以MediaPlayer::loadWithNextMediaEngine()在调用 m_private.set(engine->constructor(this))时,触发的是MediaPlayerPrivate::create()函数,同时将指向自身的指针
作为参数传给了MediaPlayerPrivate.
这样WebCore::MediaPlayer和MediaPlayerPrivateInterface的android平台实现类MediaPlayerPrivate之间的关系就建立起来的。
下面简单记录下play的启动过程。
MediaPlayerPrivate::load()函数会通知MediaPlayer当前状态变为了ReadyState.MediaPlayer进一步通知HTMLMediaElement当前状态变为了ReadyState.
HTMLMediaElement在得知这一状态变化后调用setReadyState()将一个Play事件加入队列。当Play事件被执行时就会调用play().play()又进一步调用MediaPlayer::play().
MediaPlayer::play()调用MediaPlayerVideoPrivate::play(),这个函数用通过JNI调用到android::HTML5VideoViewProxy::play().