作为浏览器,访问网络资源,除了文本网页,再就是音视频资源了。随着互联网的发展,视频资源除了提供让用户学习、娱乐的资源外,在当今时代,还演示着传播实时咨讯的角色。
趁着清明节,用一到两个blog,来说说chromium对于视频播放这块逻辑。
对于WebKit(chromium维护的版本更名为blink),解析到Video标签、Audio 标签时,会分别创建HTMLVideoElement和HTMLAudioElement。这两个类有共同的父类HTMLMediaElement。
这篇blog,先来介绍下对于视频播放MediaPlayer相关逻辑。下个blog来说说对于Video标签的MediaControls。
关于MediaPlayer。实际上是个概念。在WebKit中,有MediaPlayer这个对象,且在content api中有对应的平台实现,WebMediaPlayerAndroid。而对于Android系统来讲,有个播放器内核。即MediaPlayer的具体实现。在android系统源码的Java层,有MediaPlayer.java来暴露相关的接口。
一. MediaPlayer(blink中)的创建。
JS invoke(网页JS调用)
HTMLMediaElement::load()
HTMLMediaElement::prepareForLoad()
HTMLMediaElement::createMediaPlayer()
在函数:HTMLMediaElement::createMediaPlayer()中,有代码:
m_player = MediaPlayer::create(this);
这里的MediaPlayer。是blink中的概念MediaPlayer 。这个类在文件platform/graphics/media/MediaPlayer.h中。
关于MediaPlayer类,在blink中还有个子类:WebMediaPlayerClientImpl。
==========================================================
二. WebMediaPlayer的创建
说到上面的逻辑,但是我们还没有看到对系统MediaPlayer的调用。接着我们来看看对系统MediaPlayer调用这块。
还是再从HTMLMediaElement讲起。
HTMLMediaElement::load()
HTMLMediaElement::loadInternal()
HTMLMediaElement::selectMediaResource()
HTMLMediaElement::loadResource
HTMLMediaElement::startPlayerLoad()
在方法:HTMLMediaElement::startPlayerLoad的函数体中有代码:
m_player->load(loadType(), requestURL, corsMode());
这个load的实现是在WebMediaPlayerClientImpl.cpp中。
void WebMediaPlayerClientImpl::load(WebMediaPlayer::LoadType loadType, const WTF::String& url, WebMediaPlayer::CORSMode corsMode)
{
ASSERT(!m_webMediaPlayer);
// FIXME: Remove this cast
LocalFrame* frame = mediaElement().document().frame();
WebURL poster = m_client->mediaPlayerPosterURL();
KURL kurl(ParsedURLString, url);
m_webMediaPlayer = createWebMediaPlayer(this, kurl, frame, HTMLMediaElementEncryptedMedia::contentDecryptionModule(mediaElement()));
if (!m_webMediaPlayer)
return;
#if ENABLE(WEB_AUDIO)
// Make sure if we create/re-create the WebMediaPlayer that we update our wrapper.
m_audioSourceProvider.wrap(m_webMediaPlayer->audioSourceProvider());
#endif
m_webMediaPlayer->setVolume(mediaElement().effectiveMediaVolume());
m_webMediaPlayer->setPoster(poster);
m_webMediaPlayer->load(loadType, kurl, corsMode);
if (mediaElement().isFullscreen())
m_webMediaPlayer->enterFullscreen();
}
static PassOwnPtr<WebMediaPlayer> createWebMediaPlayer(WebMediaPlayerClient* client, const WebURL& url, LocalFrame* frame, WebContentDecryptionModule* initialCdm)
{
WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(frame);
if (!webFrame || !webFrame->client())
return nullptr;
return adoptPtr(webFrame->client()->createMediaPlayer(webFrame, url, client, initialCdm));
}
WebFrameClient的对象是content api中的RenderFrameImpl。
接着看调用逻辑
RenderFrameImpl::createMediaPlayer
RenderFrameImpl::CreateAndroidWebMediaPlayer
我们看看WebMediaPlayerAndroid类,是blink::WebMediaPlayer的子类。
三。系统MediaPlayer创建调用逻辑
1. content api主要对象逻辑梳理
我们在二中提到:
WebMediaPlayerClientImpl::load 函数。二中,我们主要看了WebMediaPlayer的创建过程,即代码:
m_webMediaPlayer = createWebMediaPlayer(this, kurl, frame, HTMLMediaElementEncryptedMedia::contentDecryptionModule(mediaElement()));
的执行。这里,我们再看看后面m_webMediaPlayer->setVolume(mediaElement().effectiveMediaVolume());的执行。
这个load的具体实现,就是content api中的WebMediaPlayer_android.cc中函数:WebMediaPlayerAndroid::setVolume的执行。
void WebMediaPlayerAndroid::setVolume(double volume) {
DCHECK(main_thread_checker_.CalledOnValidThread());
LOG(INFO)<<"vnbo0401 WebMediaPlayerAndroid::setVolume volume is %f "<<volume;
player_manager_->SetVolume(player_id_, volume);
}
WebMediaPlayerAndroid对象中维护着一个RenderMediaPlayerManager的对象。
我们看看RendererMediaPlayerManager::SetVolume函数。
void RendererMediaPlayerManager::SetVolume(int player_id, double volume) {
Send(new MediaPlayerHostMsg_SetVolume(routing_id(), player_id, volume));
}
IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_SetVolume,
GetMediaPlayerManager(render_frame_host),
BrowserMediaPlayerManager::OnSetVolume)
在MediaWebContentsObserver对象中,管理着所有的BrowserMediaPlayerManager对象。
BrowserMediaPlayerManager* MediaWebContentsObserver::GetMediaPlayerManager(
RenderFrameHost* render_frame_host) {
LOG(INFO)<<"vnbo0401 MediaWebContentsObserver::GetMediaPlayerManager";
uintptr_t key = reinterpret_cast<uintptr_t>(render_frame_host);
if (!media_player_managers_.contains(key)) {
LOG(INFO)<<"vnbo0401 MediaWebContentsObserver::GetMediaPlayerManager 1 new BrowserMediaPlayerManager";
media_player_managers_.set(
key,
make_scoped_ptr(BrowserMediaPlayerManager::Create(render_frame_host)));
}
return media_player_managers_.get(key);
}
通过这里,我们可以看出不同的BrowserMediaPlayerManager对象是通过RenderFrameHost对象来查找的。
总结:上面这段逻辑主要说了,RenderMediaPlayerManager对象、BrowserMediaPlayerManager对象、WebMediaPlayer对象之间的联系。
blink内核中的WebMediaPlayer对象,含有成员变量RenderMediaPlayerManager,而RenderMediaPlayerManager通过IPC联系MediaWebContentsObserver;
MediaWebContentsObserver对象管理BrowserMediaPlayerManager对象。
2. chromium media api相关对象创建与逻辑梳理
视频播放模块是android系统中主要模块之一,也就是我们平时所讲的MediaPlayer(狭义的MediaPlayer,文中之前提到的MediaPlayer是广义的MediaPlayer)。这里api会调用系统MediaPlayer。我们来梳理下这块逻辑
之前的逻辑梳理中,我们只看到了BrowserMediaPlayerManager对象的创建。BrowserMediaPlayerManager类是media api中MediaPlayerManager类的子类。
BrowserMediaPlayerManager主要职责就是管理media api中的MediaPlayerAndroid对象。
WebMediaPlayerClientImpl::load
WebMediaPlayerAndroid::load
WebMediaPlayerAndroid::InitializePlayer
RendererMediaPlayerManager::Initialize
××××××IPC××××××
BrowserMediaPlayerManager::OnInitialize
在函数中OnInitialize中调用同类中函数:BrowserMediaPlayerManager::CreateMediaPlayer。这里创建的对象是media api中MediaPlayerBridge对象。MediaPlayerBridge类是media api中MediaPlayerAndroid的子类。
MediaPlayerBridge对象的职责是负责chromium kernel与Android系统的MediaPlayer打交道。
MediaPlayerBridge通过JNI调用MediaPlayerBridge的java对象联系。MediaPlayerBridge.java调用了系统的MediaPlayer.java
另外,media api中重写了MediaPlayerListener.java。 MediaPlayerListener通过JNI与C++对象MediaPlayerListener联系。C++对象MediaPlayerListener,是在MediaPlayerAndroid中构造函数中创建。
MediaPlayerAndroid::MediaPlayerAndroid(
int player_id,
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const GURL& frame_url)
: request_media_resources_cb_(request_media_resources_cb),
player_id_(player_id),
manager_(manager),
frame_url_(frame_url),
weak_factory_(this) {
LOG(INFO)<<"vnbo0401 MediaPlayerAndroid::MediaPlayerAndroid";
listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
weak_factory_.GetWeakPtr()));
}
现在,我们就梳理从video元素开始执行load函数之后,进行的MediaPlayer的创建的所有逻辑。通过这块逻辑,我们也可以看出blink内核与content api等打交道,采用XXXclient、 WebXXX的设计方式,实现接口的暴漏。