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
如有转发,请注明出处,谢谢!