Chromium MediaPlayer原理分析初步

最近在修改一些问题,涉及到了网页播放视频的相关东西,分析了一下流程,把它记录下来,不然可能一个星期就忘掉了.
在chromium网页切换小屏至全屏的过程中,每一个视频url对应一个WebMediaPlayer(有唯一id), 从而对应一个Browser进程中的MediaPlayerBridge. HTMLMediaElement的src变化时都会调用到HTMLMediaElement::startPlayerLoad(),进而去创建一个新的WebMediaPlayer, 然后去load. 如果该Video已经处于全屏状态,则调用ChromeClient::enterFullScreenForElement保证新创建的WebMediaPlayer也进入全屏.
全屏时, 用的java MediaPlayer公用小屏时的MediaPlayer, 只是更换了MediaPlayer的Surface. 小屏时MediaPlayer的Surface为SurfaceTexure, 全屏是为ContentVideoView中的SurfaceView
播放完毕WebMediaPlayer会被release, 进而MediaPlayerBridge也会被release. 同时如果此时是全屏播放, 那么全屏id(fullscreen_player_id_, 保存在BrowserMediaPlayerManager中)会随着MediaPlayerBridge的消失而变成无效(赋值为kInvalidMediaPlayerId, BrowserMediaPlayerManager会使用fullscreen_player_id_来查找相应的MediaPlayerBridge,如果fullscreen_player_id_无效就说明此时没有全屏的播放器)
每次进入全屏时会将新的MediaPlayer的id设置为全屏的id, 这部分逻辑在BrowserMediaPlayerManager::OnEnterFullscreen(int player_id)中处理
 
简单说就是: 每个src对应一个WebMediaPlayer,也就对应一个MediaPlayerBridge. src变化后前一个的MediaPlayer(WebMediaPlayer和MediaPlayerBridge)就会被release, 创建一个新的MediaPlayer(同上), 相应的player_id也会加1. 如果此时video标签处于全屏,那么新建的MediaPlayer也进入全屏.
 
具体代码如下
void HTMLMediaElement::startPlayerLoad()
{
    ASSERT(!m_webMediaPlayer);
    m_webMediaPlayer = frame->loader().client()->createWebMediaPlayer(*this, kurl, this);
    ...
    m_webMediaPlayer->setVolume(effectiveMediaVolume());
    m_webMediaPlayer->setPoster(posterImageURL());
    m_webMediaPlayer->setPreload(effectivePreloadType());
    m_webMediaPlayer->load(loadType(), kurl, corsMode());
    if (isFullscreen()) { // true说明video标签处于全屏
        // This handles any transition to or from fullscreen overlay mode.
        document().frame()->chromeClient().enterFullScreenForElement(this);  
        // 会调用到BrowserMediaPlayerManager::OnEnterFullscreen(int player_id),并把该player_id设置为全屏id(fullscreen_player_id_)
    }
}
 
Issue-1: 全屏activity启动后,chromeactivity会被隐藏,进而触发一些列的hidden操作, 期间会调用到RenderFrameObserver::WasHidden.
 
调用流程如下
RenderFrameObserver::WasHidden
RenderMediaPlayerManager::WasHidden
RenderMediaPlayerManager::ReleaseVideoResources // 遍历所有player,如果player->paused() || player->hasVideo(), 即该player已经暂停或是video,则release该player
WebMediaPlayerAndroid::ReleaseMediaResources()
| --- Pause(false);
|   最后调到getLocalPlayer().pause()
| --- client_->playbackStateChanged();
| --- RendererMediaPlayerManager::ReleaseResources(int player_id)
   Send(new MediaPlayerHostMsg_Release(routing_id(), player_id))
================
   BrowserMediaPlayerManager::OnReleaseResources(int player_id)
    | --- if (player->GetVideoWidth() > 0) // 如果是video
    |   | --- MediaSession::Get(web_contents())->RemovePlayer(this, player_id);
    | --- BrowserMediaPlayerManager::ReleasePlayer(MediaPlayerAndroid* player)
       | --- MediaPlayerBridge::Release()
       |   MediaPlayerBridge.release // java code
       |   getLocalPlayer().release() // 释放java层的MediaPlayer
       | --- BrowserMediaPlayerManager::ReleaseMediaResources(int player_id)  
 
 
先在的逻辑是这样的:  
Hide时只有当player暂停或是video时才释放java层的player, 也就是说如果是正在播放音频,则不释放,这符合用户习惯. 如果是视频不管是在暂停状态还是在播放状态都会被释放,这样符合用户习惯,毕竟视频在后台继续播放没有意义.(但如果是听mtv的歌曲呢, 所有还需改进)
 
Hide的过程只是释放了java层的MediaPlayer, 也就无法继续播放音视频了, 但是并没有释放c++层的WebMediaPlayerAndroid和MediaPlayeBridge. 当activity重新可见时,用户继续点击播放, 会调用WebMediaPlayerAndroid::play(), 发IPC后调用MediaPlayerBridge::Start(), 再调用MediaPlayerBridge::Prepare(), 在Prepare中建立java层的MediaPlayer来继续播放.
 
附1:BrowserMediaPlayerManager生命周期
创建: MediaWebContentsObserver::GetMediaPlayerManager(RenderFrameHost* render_frame_host)
     BrowserMediaPlayerManager与RenderFrameHost一一对应, 建立一个map
     typedef base::ScopedPtrHashMap> // uintptr_t为RenderFrameHost*
 
销毁: MediaWebContentsObserver::RenderFrameDeleted(RenderFrameHost* render_frame_host)
     RenderFrameDeleted时从map中erase
附2:

你可能感兴趣的:(chromium,Android开发)