Chromium Video 是如何播放的

本文基于Android平台简单梳理了Chromium在打开一个视频资源时背后所发生的逻辑与源码调用流程

1. Video标签初始化视频源

当浏览器打开一个视频资源链接时,会默认为视频资源创建video标签,帮将视频源链接设置给video标签的src属性,当video标签解析到视频源链接后,会触发以下流程进行视频资源的加载

################ 消息边界
++++++++++++++++ 区域边界
$$$$$$$$$$$$$$$$ 线程边界
~~~~~~~~~~~~~~~~ 进程边界
---------------- 代码块功能模块分隔

html_media_element.SourceWasAdded
  |-->InvokeResourceSelectionAlgorithm
    |-->ScheduleNextSourceChild
      |-->load_timer_.StartOnShot-------|
                                        |
html_media_element.LoadTimerFired<------|
  |-->LoadInternal
    |-->SelectMediaResource
      |-->LoadNextSourceChild
        |-->LoadResource

2. 创建播放器WebMediaPlayer

video标签加载视频资源时会创建相应的播放器用于播放视频,当播放器创建完成后,通过播放器加载视频资源

html_media_element.LoadResource
  |-->StartPlayerLoad
    |-->frame->Client()->CreateWebMediaPlayer------------|
       [frame: local_frame  Client:local_frame_client]   |
    |-->local_frame_client_impl.CreateWebMediaPlayer<----|
      |-->core_initializer.CreateWebMediaPlayer--------|
            extends                                    |
      |-->modules_initializer.CreateWebMediaPlayer<----|
        |-->web_local_frame_client.CreateMediaPlayer-----|
            extends                                      |
        |-->render_frame_impl.CreateMediaPlayer<---------|
          |-->media_factory.CreateMediaPlayer [ DeferLoadCB=RenderFrameImpl:DeferMediaLoad ]
            |-->web_media_player_builder.Build
              |-->new WebMediaPlayerImpl
                |-->new PipelineImpl [ create_renderer_cb_=WebMediaPlayerImpl:CreateRenderer ]
  |-->web_media_player_impl.Load [通过播放器加载资源]

创建播放器时,也会一并初始化PipelineImpl,传入renderer视频渲染器的构造回调函数

3. 播放器初始化

播放器创建完成后,首先将播放器相关的数据源对象初始化

web_media_player_impl.Load
  |-->defer_load_cb_.Run(cb=WebMediaPlayerImpl::DoLoad)------|
     media_factory构造WebMediaPlayerImpl时传入                                         |
render_frame_impl:DoLoad<------------------------------------|
  |-->GetContentClient().renderer().DeferMediaLoad
    |-->no_state_prefetch_utils.DeferMediaLoad-------|
                                                     | 
web_media_player_impl.DoLoad<------------------------|
  |-->multi_buffer_data_source.Initialize(cb=WebMediaPlayerImpl::MultiBufferDataSourceInitialized)-----|
                                                                                                       |
web_media_player_impl.MultiBufferDataSourceInitialized<------------------------------------------------|
  |-->DataSourceInitialized

数据源初始化成功后,启动Pipeline流程,web_media_player_impl.StartPipeline

web_media_player_impl.DataSourceInitialized
  |-->StartPipeline
    |-->post runnable SetOnNewProcessedFrameCallback [ 设置守帧回调 ]
    |-->demuxer_manager.CreateDemuxer(cb=WebMediaPlayerImpl::OnDemuxerCreated)--------|
                   [ 创建视频解封装器 ]                                                 |
web_media_player_impl.OnDemuxerCreated<-----------------------------------------------|
  |-->pipeline_controller.Start
    |-->pipeline_impl.Start
      |-->create_renderer_cb_.Run [ WebMediaPlayerImpl:CreateRenderer ]
      |-->按需执行初始化任务--------------------------------------------------------|
        |-->1.PipelineImpl::RendererWrapper::InitializeDemuxer                   |                               
        |-->2.PipelineImpl::RendererWrapper::ReportMetadata                      |                                 
        |-->3.PipelineImpl::RendererWrapper::CreateRenderer                      |
        |-->4.PipelineImpl::RendererWrapper::InitializeRenderer                  |                                
        |-->final callback PipelineImpl::RendererWrapper::CompleteSeek<----------|
                                                                    |
PipelineImpl::RendererWrapper::CompleteSeek<------------------------|

pipeline_impl.Start首先通过create_renderer_cb_创建媒体渲染器,创建完成后逐步执行代码流程中所述的初始化流程

create_renderer_cb_ 创建媒体渲染器此处就是我们的视频播放器,在Android平台上视频渲染器分为2种:

1. 主场景为codec解码器方式,即Render进程负责调度视频的解封装以及声画同步逻辑,而Android平台只提供基本的硬解单元

2. 另外一种则是完全复用Android平台的视频播放器,就是我们平时开发所使用的MediaPlayer

这里我们只详细分析第一种播放器初始化流程,从create_renderer_cb_.Run开始

pipeline_impl.Start
  |-->create_renderer_cb_.Run--------------------------------------------------------|
                                                                                     |
web_media_player_impl.CreateRenderer<------------------------------------------------|
  |-->renderer_factory_selector_->GetCurrentFactory()->CreateRenderer [ media_factory.CreateRendererFactorySelector ]
    |-->renderer_impl_factory.CreateRenderer
      |-->new AudioRendererImpl [ CreateAudioDecodersCB=RendererImplFactory:CreateAudioDecoders ]
      |-->new VideoRendererImpl [ CreateVideoDecodersCB=RendererImplFactory:CreateVideoDecoders ]
      |-->new RendererImpl

create_renderer_cb_创建完成后,逐步执行以下初始化任务

1.InitializeDemuxer 

2.ReportMetadata 

3.CreateRenderer 

4.InitializeRenderer 

这里我们先关注最后一步初始化媒体渲染器,首先先创建render进程的视频解码器,这里是因为render是sandbox进程没有权限访问Android平台的解码器资源,因此render进程无法访问到解码器资源需要通过跨进程的方式使用Android平台的硬解单元

PipelineImpl::RendererWrapper::InitializeRenderer
  |-->renderer_impl.Initialize
    |-->InitializeAudioRenderer
      |-->audio_renderer_impl.Initialize(cb=RendererImpl::OnAudioRendererInitializeDone)-------|
                                                                                               |
renderer_impl.OnAudioRendererInitializeDone<---------------------------------------------------|
  |-->InitializeVideoRenderer
    |-->video_renderer_impl.Initialize(init_cb_=RendererImpl::OnVideoRendererInitializeDone)
      |-->decoder_stream.Initialize(init_cb_=VideoRendererImpl::OnVideoDecoderStreamInitialized)
        |-->BeginDecoderSelection
          |-->decoder_selector.BeginDecoderSelection
                   |-select_decoder_cb=DecoderStream::OnDecoderSelected
                   |-output_cb=DecoderStream::OnDecodeOutputReady
            |-->SelectDecoderInternal
              |-->CreateDecoders
                |-->create_decoders_cb_.Run
                      |
                |-->RendererImplFactory:CreateVideoDecoders
                  |-->mojo_decoder_factory.CreateVideoDecoders
                    |-->new MojoVideoDecoder [ mojo 视频解码器 ]

render进程硬解对象创建好后开始创建browser进程的硬解对象,browser进程的硬解对象才是真正的硬解单元

decoder_selector.SelectDecoderInternal
  |-->CreateDecoders
  |-->GetAndInitializeNextDecoder
    |-->decoder_stream_traits.InitializeDecoder [ init_cb=DecoderSelector::OnDecoderInitializeDone ]
      |-->mojo_video_decoder.Initialize [ init_cb=DecoderStreamTraits::OnDecoderInitialized ]
        |-->InitAndBindRemoteDecoder [ cb=MojoVideoDecoder::InitializeRemoteDecoder ]
          |-->GpuVideoAcceleratorFactories [ cb=MojoVideoDecoder::OnChannelTokenReady ]--------------------|
                                           [ complete_cb= MojoVideoDecoder::InitializeRemoteDecoder ]      |
mojo_video_decoder.OnChannelTokenReady<--------------------------------------------------------------------|
  |-->InitAndConstructRemoteDecoder [ complete_cb= MojoVideoDecoder::InitializeRemoteDecoder ]
    |-->mojom::VideoDecoder.Construct----------------------------------------------------------------------|
    |-->complete_cb.Run                                                                                    |
           | 顺序执行                                                                                                                                                                                                                           |
    |-->mojo_video_decoder.InitializeRemoteDecoder                                                         |
                                                                                                           |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Browser~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      |
                                                                                                           |
mojo_video_decoder_service.Construct<----------------------------------------------------------------------|
  |-->mojo_media_client.CreateVideoDecoder
         | extends
  |-->gpu_mojo_media_client.CreateVideoDecoder
    |-->CreatePlatformVideoDecoder
         | extends
    |-->gpu_mojo_media_client_android.CreatePlatformVideoDecoder
      |-codec_allocator.GetInstance
        |-->new CodecAllocator [ factory_cb=MediaCodecBridgeImpl::CreateVideoDecoder ]
      |-media_codec_video_decoder.Create
        |-->new MediaCodecVideoDecoder
          |-->android_video_surface_chooser_impl.SetClientCallbacks [ cb=MediaCodecVideoDecoder::OnSurfaceChosen ]
        |-->new AsyncDestroyVideoDecoder(MediaCodecVideoDecoder)
        
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Renderer~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

mojo_video_decoder.InitializeRemoteDecoder
  |-->mojom::VideoDecoder.Initialize [ init_cb_=MojoVideoDecoder::OnInitializeDone ]-----------------------|
                                                                                                           |  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Browser~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      |
                                                                                                           |
mojo_video_decoder_service.Initialize [ cb=MojoVideoDecoder::OnInitializeDone ]<---------------------------|
  |-->media_codec_video_decoder.Initialize [ init_cb=MojoVideoDecoderService::OnDecoderInitialized ]
                                           [ output_cb=MojoVideoDecoderService::OnDecoderOutput ]
    |-->set decoder_config
    |-->init_cb.Run
      |
    |-->mojo_video_decoder_service.OnDecoderInitialized
      |-->init_cb_.Run-----------------------------------------------------------------------------------|
                                                                                                         | 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Renderer~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    |
                                                                                                         |
mojo_video_decoder.OnInitializeDone<---------------------------------------------------------------------|

4. 解码器初始化

当mojo_video_decoder_service完成browser、render解码器初步的创建后,会触发一系列的回调,最终到PipelineImpl::RendererWrapper::CompleteSeek,具体调用流程如下

mojo_video_decoder.OnInitializeDone
  |-->init_cb_.Run [ DecoderStreamTraits::OnDecoderInitialized ]------------------------|
      (decoder_stream_traits.InitializeDecoder bind the callback)                       |
decoder_stream_traits.OnDecoderInitialized<---------------------------------------------|
  |-->cb.Run [ DecoderSelector::OnDecoderInitializeDone ]-------------------------------|
      (decoder_selector.GetAndInitializeNextDecoder bind the callback)                  | 
decoder_selector.OnDecoderInitializeDone<-----------------------------------------------|
  |-->RunSelectDecoderCB
    |-->select_decoder_cb_.Run----------------------------------------------------------|
        (decoder_stream.BeginDecoderSelection bind the callback)                        |
decoder_stream.OnDecoderSelected<-------------------------------------------------------|
  |-->init_cb_.Run----------------------------------------------------------------------|
      (video_renderer_impl.Initialize bind the callback)                                |
video_renderer_impl.OnVideoDecoderStreamInitialized<------------------------------------|
  |-->FinishInitialization
    |-->init_cb_.Run--------------------------------------------------------------------|
        (renderer_impl.InitializeVideoRenderer bind the callback)                       |
renderer_impl.OnVideoRendererInitializeDone<--------------------------------------------|
  |-->FinishInitialization
    |-->init_cb_.Run--------------------------------------------------------------------|
        (PipelineImpl::RendererWrapper::InitializeRenderer bind done_cb)                |
        (doen_cb=SerialRunner.RunNextInSeries)                                          |
serial_runner.RunNextInSeries<----------------------------------------------------------|
  |-->bound_fns is empty
  |-->done_cb_.Run ---------------------------------------------------------------------|
    | PipelineImpl::RendererWrapper::Start bind the callback                            |
PipelineImpl::RendererWrapper::CompleteSeek<--------------------------------------------|

PipelineImpl::RendererWrapper::CompleteSeek方法里通知到PipelineImpl解码器对象已准备好,此时PipelineImpl会通知视频渲染对象尝试从数据源中读取数据,读取数据的方式则是从视频解封装器读取相应的音频流和视频流

视频渲染对象将读取到的数据发送到browser端的解码器对象进行解码,由于browser端的解码器对象刚准备好,还没有进行解码器的初始化,所以在收到第一次数据buffer时会进行解码器对象的prepare操作,具体调用流程如下

PipelineImpl::RendererWrapper::CompleteSeek
  |-->renderer_impl.StartPlayingFrom
    |-->video_renderer_impl.StartPlayingFrom
      |-->AttemptRead_Locked
        |-->decoder_stream->Read [ read_cb_=VideoRendererImpl::FrameReady ]
          |-->ReadFromDemuxerStream
            |-->demuxer_stream.Read [ cb=DecoderStream:OnBuffersReady ]--------|
                                                                               |
decoder_stream.OnBuffersReady<-------------------------------------------------|
  |-->Decode
    |-->mojo_video_decoder.Decode [ decode_cb=DecoderStream::OnDecodeDone ]
      |-->mojo::VideoDecoder.Decode [ cb=MojoVideoDecoder::OnDecodeDone ]----------|
                                                                                   |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Browser~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~          |
                                                                                   |
mojo_video_decoder_service.Decode [ callback=MojoVideoDecoder::OnDecodeDone ]<-----|
  |-->mojo_decoder_buffer_reader.ReadDecodeBuffer [ callback=MojoVideoDecoderService::OnReaderRead ]------|
                                                  [ bind callback MojoVideoDecoder::OnDecodeDone]         |
mojo_video_decoder_service.OnReaderRead<------------------------------------------------------------------|
  |-->media_codec_video_decoder.Decode [ callback=MojoVideoDecoderService::OnDecoderDecoded ]
                                       [ bind callback MojoVideoDecoder::OnDecodeDone]
    |-->pending_decodes_ 先记录读取到的数据Buffer
    |-->StartLazyInit

MediaCodecVideoDecoder.StartLazyInit开始初始化Android平台的硬件解码单元

media_codec_video_decoder.StartLazyInit
  |-->video_frame_factory.Initialize [ callback=MediaCodecVideoDecoder::OnVideoFrameFactoryInitialized ]-------|
                                     [ 生成硬件解码器的texture,用于构建surface用于解码数据的输出 ]                   |
media_codec_video_decoder.OnVideoFrameFactoryInitialized<------------------------------------------------------|
  |-->request_overlay_info_cb_.Run [ callback=MediaCodecVideoDecoder::OnOverlayInfoChanged ]
      | mojo_video_decoder_service.Construct set the request_overlay_info_cb
      | MojoVideoDecoderService::OnDecoderRequestedOverlayInfo
  |-->mojo_video_decoder_service.OnDecoderRequestedOverlayInfo [ callback=MediaCodecVideoDecoder::OnOverlayInfoChanged ]
    |-->provide_overlay_info_cb_ = callback
    |-->mojom::VideoDecoderClient.RequestOverlayInfo----------------------------|
                                                                                |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Render~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        |
                                                                                |
mojo_video_decoder.RequestOverlayInfo<------------------------------------------|
  |-->request_overlay_info_cb_.Run [ callback=MojoVideoDecoder::OnOverlayInfoChanged ]
      | web_media_player_impl.CreateRenderer set the request_overlay_info_cb
      | WebMediaPlayerImpl::OnOverlayInfoRequested
  |-->web_media_player_impl.OnOverlayInfoRequested [ callback=MojoVideoDecoder::OnOverlayInfoChanged ]
    |-->provide_overlay_info_cb_ = callback
    |-->MaybeSendOverlayInfoToDecoder
      |-->provide_overlay_info_cb_.Run----------|
                                                |
mojo_video_decoder.OnOverlayInfoChanged<--------|
  |-->mojo::VideoDecoder.OnOverlayInfoChanged--------------------------------|
                                                                             |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Browser~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    |
                                                                             |
mojo_video_decoder_service.OnOverlayInfoChanged<-----------------------------|
  |-->provide_overlay_info_cb_.Run
    | MediaCodecVideoDecoder::OnOverlayInfoChanged
  |-->media_codec_video_decoder.OnOverlayInfoChanged
    |-->surface_chooser_helper.UpdateChooserState
      |-->android_video_surface_chooser_impl.UpdateState
        |-->Choose
          |-->SwitchToTextureOwner
            |-->use_texture_owner_cb_.Run-------------------------------------|
                [ MediaCodecVideoDecoder constructor set the callback ]       |
                [ MediaCodecVideoDecoder::OnSurfaceChosen ]                   |
                                                                              |
media_codec_video_decoder.OnSurfaceChosen<------------------------------------|
  |-->CreateCodec
    |-->codec_allocator.CreateMediaCodecAsync [ callback=MediaCodecVideoDecoder::OnCodecConfiguredInternal ]
      |-->CreateMediaCodecInternal [ return value as CodecAllocator::OnCodecCreated ]
                                   [ bind param callback MediaCodecVideoDecoder::OnCodecConfiguredInternal ]
        |-->factory_cb.Run
            | MediaCodecBridgeImpl::CreateVideoDecoder
        |-->media_codec_bridge_impl.CreateVideoDecoder
          |-->Java_MediaCodecBridgeBuilder_createVideoDecoder
          |-->new MediaCodecBridgeImpl(java object)
        |-->codec_allocator.OnCodecCreated
          |-->codec_created_cb.Run
            | MediaCodecVideoDecoder::OnCodecConfiguredInternal
          |-->media_codec_video_decoder.OnCodecConfiguredInternal

至此Android平台的硬件解码单元已真正创建成功

5. 解码视频帧上屏

硬件解码单元(Android java mediacodec 对象)创建完成后,通过回调通知到media_codec_video_decoder使用此解码单元对从Render接收到的视频流数据进行解码,media_codec_video_decoder.Decode方法触发时会先将数据流记录到pending_decodes_,之后无论是创建解码器还是使用解码器解码数据都是从pending_decodes_提取数据进行解码,解码视频后则将视频帧数据封装为VideoFrame帧数据送到Render的video_compositor进行视频内容的真正上屏,具体流程如下

media_codec_video_decoder.OnCodecConfiguredInternal
  |-->OnCodecConfigured
    |-->StartTimerOrPumpCodec
      |-->PumpCodec
        |-->QueueInput [ 向解码器输入视频流 ]
        |-->DequeueOutput [ 从解码器或者解码数据 ]
          |-->video_frame_factory.CreateVideoFrame [ callback=MediaCodecVideoDecoder::ForwardVideoFrame ]------|
              [ 通过解码数据生成视频帧 ]                                                                          |
media_codec_video_decoder.ForwardVideoFrame<-------------------------------------------------------------------|
  |-->output_cb_.Run------------------------------------------------------|
      | media_video_decoder_service.Initialize set the callback           |
      | MojoVideoDecoderService::OnDecoderOutput                          |
  |-->mojo_video_decoder_service.OnDecoderOutput<-------------------------|
    |-->mojom::VideoDecoderClient.OnVideoFrameDecoded-----------------------------------------------------|
                                                                                                          |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Render~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~      |
                                                                                                          |
mojo_video_decoder.OnVideoFrameDecoded<-------------------------------------------------------------------|
  |-->output_cb_.Run
      | decoder_stream.BeginDecoderSelection set to decoder_selector output_cb=DecoderStream::OnDecodeOutputReady
      | decoder_selector.output_cb_ = DecoderStream::OnDecodeOutputReady                         |
      | decoder_selector.GetAndInitializeNextDecoder set output_cb to decoder_stream_traits      |
      | decoder_selector.InitializeDecoder set output_cb to mojo_video_decoder                   | 
      | mojo_video_decoder.Initialize record the output_cb_--------------------------------------|
  |-->decoder_stream.OnDecodeOutputReady
    |-->SatisfyRead
      |-->read_cb_.Run
          | video_renderer_impl.AttemptRead_Locked bind the callback VideoRendererImpl::FrameReady
      |-->video_renderer_impl.FrameReady
        |-->PaintFirstFrame
          |-->VideoRendererSink.PaintSingleFrame
              | extends
          |-->video_frame_compositor.PaintSingleFrame
            |-->ProcessNewFrame
              |-->new_processed_frame_cb_.Run
                  | web_media_player_impl.StartPipeline set the callback=WebMediaPlayerImpl::OnFirstFrame
              |-->web_media_player_impl.OnFirstFrame
                |-->通知首帧
            |-->client_[cc::VideoFrameProvider::Client].DidReceiveFrame
                | extends
            |-->web_video_frame_submitter.DidReceiveFrame
                | extends
            |-->video_frame_submitter.DidReceiveFrame
              |-->SubmitSingleFrame
                |-->SubmitFrame
                  |-->viz::mojom::blink::CompositorFrameSink.SubmitCompositorFrame-----------------------|
         |-->painted_first_frame_=false [下次 不再触发首帧]                                                 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Browser~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    |
                                                                                                         |
compositor_frame_sink_impl.SubmitCompositorFrame<--------------------------------------------------------|
  |-->SubmitCompositorFrameInternal
    |-->compositor_frame_sink_support.MaybeSubmitCompositorFrame
        |-->SetNeedsBeginFrame(true)
          |-->触发VSync绘制上屏

video_compositor收到browser端传入的视频帧数据时,将该帧数据通过video_frame_submitter将数据通过compositor_frame_sink将数据送到browser端处理数据compositor_frame_sink_impl上,最终通过Android平台的VSync一系列绘制流程,将视频帧数据绘制到设备屏幕上

video_frame_compositor.client_就是上述中video_frame_submitter,它是在PipelineImpl::RendererWrapper::Start 4 步初始化流程中的第 2 步 ReportMetadata 设置的,具体调用流程如下

PipelineImpl::RendererWrapper::ReportMetadata
  |-->pipeline_impl::OnMetadata
    |-->client_[Pipeline::Client].OnMetadata
        | web_media_player_impl.Start set the client, the client is WebMediaPlayerImpl
    |-->web_media_player_impl.OnMetadata
      |-->ActivateSurfaceLayerForVideo
        |-->video_frame_compositor.EnableSubmission
          |-->client_=submitter_
              | client_[cc::VideoFrameProvider::Client]
              | submitter_[WebVideoFrameSubmitter]
              | WebVideoFrameSubmitter extends cc::VideoFrameProvider::Client
              | video_frame_submitter extends WebVideoFrameSubmitter

至此Chromium打开一个视频资源时内部的源码流程基本已梳理完成,主流程上的分支细节后续系列文章会一一补充完善

原文链接:https://juejin.cn/post/7326100957766385698 作者:小小不点

(0)
上一篇 2024年1月22日 上午10:36
下一篇 2024年1月22日 上午10:46

相关推荐

发表回复

登录后才能评论