chromium在60版本之後,在media部分做了些許改變,此文檔就當筆記,以免以後忘記。
使用優酷,騰訊,愛奇藝等國内網站全屏播放視訊時,此時當有第三方APP打斷,如接聽電話,微信,短信等,當浏覽器切回到後端再傳回的時候,android上使用67版本會必現crash。
以下是APP相關log:
/sprdroid9.0_trunk/vendor/sprd/platform/packages/apps/SprdBrowser/src/com/android/browser/BrowserActivity.java
protected void onStop()
/sprdroid9.0_trunk/vendor/sprd/platform/packages/apps/SprdBrowser/src/com/android/browser/Controller.java
pauseWebViewTimer
onBrowserActivityPause
WebViewTimersControl: onBrowserActivityResume
以下是暫停的流程:
bool RendererWebMediaPlayerDelegate::OnMessageReceived MediaPlayerDelegateMsg_Pause
void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int player_id)
void WebMediaPlayerImpl::OnPause()
void WebMediaPlayerImpl::Pause()
bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg)
bool RenderWidget::OnMessageReceived(const IPC::Message& message) IPC_MESSAGE_HANDLER(ViewMsg_WasHidden, OnWasHidden)
void RenderWidget::OnWasHidden()
void RenderFrameImpl::WasHidden()
void RendererWebMediaPlayerDelegate::WasHidden()
void WebMediaPlayerImpl::OnFrameHidden()
void WebMediaPlayerImpl::UpdatePlayState()
void WebMediaPlayerImpl::SetSuspendState(bool is_suspended)
void PipelineController::Suspend()
void PipelineController::Dispatch()
181
void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb)
void PipelineImpl::RendererWrapper::Suspend()
void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status)
void PipelineImpl::OnSuspendDone() is_suspended_ = true;
bool RenderViewImpl::OnMessageReceived(const IPC::Message& message)
IPC_MESSAGE_UNHANDLED(handled = RenderWidget::OnMessageReceived(message))
bool RendererWebMediaPlayerDelegate::OnMessageReceived MediaPlayerDelegateMsg_Play
void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay
void WebMediaPlayerImpl::OnPlay()
void WebMediaPlayerImpl::Play()
void WebMediaPlayerImpl::UpdatePlayState()
void WebMediaPlayerImpl::SetSuspendState(bool is_suspended)
void PipelineController::Resume()
void PipelineController::Dispatch()
void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, renderer null
void PipelineImpl::RendererWrapper::InitializeRenderer shared_state_.renderer=0x0
bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg)
由于問題是必現問題,是以經過2周的分析,發現chromium在60版本之後,當全屏暫停之後重新開始播放的時候的createrender的流程與一開始啟動浏覽器播放視訊的createrender不同,在59版本上面,無論是否暫停使用的都是default_renderer_factory.cc
但是在chromium_67.0.3364.0版本上面,啟動和暫停的factory是不相同的,啟動播放的是:
src\media\renderers\default_renderer_factory.cc
暫停後再次建立使用的是:
src\media\mojo\clients\mojo_renderer_factory.cc CourierRendererFactory
而在CourierRendererFactory中暫停後在android中使用的話就會一直傳回nullptr.
std::unique_ptr<Renderer> CourierRendererFactory::CreateRenderer(
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
const scoped_refptr<base::TaskRunner>& worker_task_runner,
AudioRendererSink* audio_renderer_sink,
VideoRendererSink* video_renderer_sink,
const RequestOverlayInfoCB& request_overlay_info_cb,
const gfx::ColorSpace& target_color_space) {
DCHECK(IsRemotingActive());
#if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
return std::make_unique<CourierRenderer>(
media_task_runner, controller_->GetWeakPtr(), video_renderer_sink);
#else
return nullptr;
#endif
}
void PipelineImpl::RendererWrapper::InitializeRenderer(
const PipelineStatusCB& done_cb) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
switch (demuxer_->GetType()) {
case MediaResource::Type::STREAM:
if (demuxer_->GetAllStreams().empty()) {
DVLOG(1) << "Error: demuxer does not have an audio or a video stream.";
done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
return;
}
break;
case MediaResource::Type::URL:
// NOTE: Empty GURL are not valid.
if (!demuxer_->GetMediaUrlParams().media_url.is_valid()) {
DVLOG(1) << "Error: demuxer does not have a valid URL.";
done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
return;
}
break;
}
if (cdm_context_) {
shared_state_.renderer->SetCdm(cdm_context_,
base::BindRepeating(&IgnoreCdmAttached));
}
shared_state_.renderer->Initialize(demuxer_, this, done_cb);
}
void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
base::TimeDelta timestamp) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
// Tracking down http://crbug.com/827990
CHECK(renderer);
// Suppress resuming if we're not suspended.
if (state_ != kSuspended) {
DCHECK(state_ == kStopping || state_ == kStopped)
<< "Receive resume in unexpected state: " << state_;
OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
return;
}
DCHECK(!pending_callbacks_.get());
SetState(kResuming);
{
base::AutoLock auto_lock(shared_state_lock_);
DCHECK(!shared_state_.renderer);
shared_state_.renderer = std::move(renderer);
}
renderer_ended_ = false;
text_renderer_ended_ = false;
base::TimeDelta start_timestamp =
std::max(timestamp, demuxer_->GetStartTime());
// Queue the asynchronous actions required to start playback.
SerialRunner::Queue fns;
fns.Push(base::BindRepeating(&Demuxer::Seek, base::Unretained(demuxer_),
start_timestamp));
fns.Push(base::BindRepeating(&RendererWrapper::InitializeRenderer,
weak_factory_.GetWeakPtr()));
pending_callbacks_ = SerialRunner::Run(
fns, base::BindRepeating(&RendererWrapper::CompleteSeek,
weak_factory_.GetWeakPtr(), start_timestamp));
}
在播放過程中,暫停之後會調用DestroyRenderer,之後resume到的shared_state_.renderer值為空。在resume添加了調用棧,最終發現是類型為RendererFactoryCB的 renderer_factory_cb_為空,進而分析到PipelineController對象被析構掉了,最後得到當浏覽器切回到前端時,之前的render被DestroyRenderer掉之後,resume的時候shared_state_.renderer的值在CreateRenderer的時候一直傳回空,是以修改了media/media_options.gni的配置enable_media_remoting_rpc = enable_media_remoting && is_android;解決此問題。
但是我還是有一個疑問,如果60版本之後android上enable_media_remoting_rpc這個宏是關閉的情況下,68版本又是好用的,是以我覺得還是跟gn args的配置有關系。
本文中使用的gn args如下;
target_os = "android"
target_cpu = "arm" # (default)
is_debug = false # (default)
symbol_level = 2
is_official_build=true
is_chrome_branded=false
use_official_google_api_keys=false
exclude_unwind_tables=false
enable_resource_whitelist_generation=true
ffmpeg_branding="Chrome"
proprietary_codecs=true
enable_remoting=true
ignore_elf32_limitations=true
如有轉發,請注明出處,謝謝!