Bootstrap

Chromium浏览器media子系统mojo说明

Chromium浏览器media子系统mojo说明

该文件夹包含mojo接口、客户端和实现,它们扩展了核心media来支持大多数进程外使用,包括media Player、Metrics(WatchTime)等。

目前,media不依赖于mojo,因此其他应用程序可以使用media,而无需引入mojo依赖。

Media Player

Media组件

Media Player(WebMediaPlayer)支持HTML5 <video>播放。在内部,它依赖于许多media组件来执行某些特定任务,例如media renderer(媒体渲染器)audio decoder(音频解码器)video decoder(视频解码器)content decryption module(内容解密模块)(CDM)。媒体渲染器、音频解码器或视频解码器需要CDM来处理受保护的内容。

虽然大多数HTML5媒体播放器栈和加密媒体扩展(EME)栈都位于沙盒渲染进程中(例如出于安全原因),但在某些情况下,某些媒体组件必须位于不同的进程中。

例如:

  • 基于硬件的媒体渲染器,其中所有音频/视频解码和渲染都在硬件中进行,沙盒渲染进程无法访问。
  • 基于硬件的视频解码器,沙盒渲染进程不可访问。
  • 在Android系统中,依赖于Android Java API的媒体组件,沙盒渲染进程不可访问。
  • 包含第三方代码的CDM,应在其自己的沙盒进程中运行。

我们提供了一个通用框架来支持Chromium中的大多数进程外(OOP)媒体组件。

Media Player Mojo接口

我们使用mojom接口作为每个媒体组件的传输层,以支持远程托管。这些接口称为Media Player Mojo接口

  • media::Renderer -> media::mojom::Renderer
  • media::AudioDecoder -> media::mojom::AudioDecoder
  • media::VideoDecoder -> media::mojom::VideoDecoder
  • media::ContentDecryptionModule -> media::mojom::ContentDecryptionModule
  • media::Decryptor -> media::mojom::Decryptor

开启Remote Media组件

这些接口的标准客户端和实现都已经提供。例如,对于media::RendererMojoRenderer实现media::Renderer并将调用转发到media::mojom::RendererPtrMojoRendererService实现media::mojom::Renderer,并且可以托管任何media::Renderer实现。

Remote Media组件可以轻松开启并无缝集成到当前媒体管道中。只需设置gn参数mojo_media_services以指定要启用的远程媒体组件。例如,使用以下gn参数,媒体管道将启用MojoRendererMojoCdm

mojo_media_services = ["renderer", "cdm"]

Media Mojo Interface Factory

media::mojom::InterfaceFactory具有工厂方法,如CreateRenderer()CreateCdm()等。它用于请求媒体播放器mojo接口。

在渲染进程中,每个RenderFrameImpl都有一个mojo::PendingRemote<media::mojom::InterfaceFactory>,用于从浏览器进程请求该框架的所有媒体播放器mojo接口。在浏览器进程中,每个RenderFrameHostImpl都有一个MediaInterfaceProxy,它实现了media::mojom::InterfaceFactory

MediaInterfaceProxy是处理媒体播放器mojo接口请求的中心。默认情况下,它会将所有请求转发到[MediaService]。但它也可以灵活地处理一些特殊或更复杂的用例。

例如:

  • 在桌面平台上,当启用CDM时,media::mojom::ContentDecryptionModule请求将转发到[CdmService],[CdmService]在其自己的CDM进程中运行。
  • 在Android系统中,media::mojom::Renderer请求通过在浏览器进程创建MediaPlayerRenderer,在RenderFrameHostImpl上下文进行处理,即使MediaService配置为在GPU进程中运行。
  • 在Chromecast上,media::mojom::Renderermedia::mojom::ContentDecryptionModule请求由运行在浏览器进程中的[MediaRendererService]处理。media::mojom::VideoDecoder请求由运行在GPU进程的MediaService处理。

注意:media::mojom::InterfaceFactory接口在MediaInterfaceProxyMediaService之间的通信中被重用。

Frameless Media Interface Factory

除了处理普通媒体播放请求的MediaInterfaceProxy之外,FramelessMediaInterfaceProxy处理不需要或没有框架的媒体案例请求。

受保护的媒体播放需要框架,因为媒体解码和CDM在框架内相关联。

FramelessMediaInterfaceProxy由WebCodecs、WebRTC使用,用于早期查询支持的编解码器。

MediaService

MediaService是一个提供媒体播放器mojo接口实现的mojoservice_manager::service。它的好处如下:

Flexible Process Model

不同的平台或产品,对远程媒体组件应该在哪运行的需求不同。例如,硬件解码器通常应该在GPU进程中运行。

ServiceManagerContext能够在浏览器进程或GPU进程启动一个服务。因此,通过使用MediaService,很容易支持托管远程媒体组件在多数Chromium进程种类(Browser/GPU)。这可以使用gn参数mojo_media_host来设置。

例如:

mojo_media_host = "browser" or “gpu”

MediaService使用kMediaServiceNameServiceManagerContext中注册。mojo_media_host用于确定服务注册在哪个进程中运行。

Connects Different Media Components

一些远程媒体组件依赖于其他组件来工作。例如,渲染器、音频解码器或视频解码器需要CDM才能处理加密流。通常会调用SetCdm()来连接CDM渲染器或CDM解码器。例如,如果渲染器接口和CDM接口是单独托管的,那么很难实现SetCdm()调用。它需要一个对象或实体,同时知道这两者
以便能够连接它们。MediaService在内部处理此问题,实际上充当这样一个对象或实体,所以你不必重新发明
车轮。

Customization through MojoMediaClient

MediaService提供托管OOP媒体组件所需的一切,但它不提供媒体组件本身。MediaService的客户端负责提供具体的媒体组件实现。

MojoMediaClient接口为MediaService客户端提供了具体媒体组件实现的方式。创建MediaService时,必须传入一个MojoMediaClient,以便media Service知道如何创建媒体组件。

例如,ChromeCast使用MediaService在浏览器进程中托管媒体渲染器和CDM,并通过CastMojoMediaClient(一种MojoMediaClient实现)提供“CastRenderer”和“CastCdm”。注意,这种重写机制并不是在任何地方都实现的。添加支持很简单,我们只会在需要时添加它。

Site Isolation

在Blink中,媒体元素和EME MediaKeys都属于WebLocalFrame。在Chromium中,这转换为属于RenderFrame的媒体播放器和CDM。

在渲染过程中,这一点很清楚。但是,当托管所有远程媒体在一个MediaService(服务管理器仅支持一个进程单个服务)时,框架边界可能变得模糊。这对于彼此交互的媒体组件来说尤其危险。

例如,foo.com中的Renderer与bar.net中的CDM共用1MediaService。如果设置foo.com Renderer去处理加密,就会产生错误。

为了防止这种情况发生,我们引入了一个额外的层来模拟RenderFrame边界。MediaService托管多个InterfaceFactory(每个RenderFrame一个),每个InterfaceFactory创建和管理它创建的媒体组件。

因此,在MediaInterfaceProxyMediaService之间的通信中media::mojom::InterfaceFactory被重用。

Specialized Out-of-Process media::Renderers

media::Renderer接口是一个简单的API,它非常通用,用于执行上层媒体播放命令。我们可以通过专用渲染器来扩展WebMediaPlayer的功能。

具体来说,我们可以构建一个子组件,它封装了高级场景复杂性,编写一个小适配层,将组件公开为media::Renderer,并将其嵌入现有的media::Pipeline状态机。专用渲染器通过限制文件和类的细节,通过需要很少的控制流量样板等方式降低复杂度。

由专用渲染器启用的2个复杂场景如下:一个是通过授权给Android Media Player(请参见MediaPlayerRenderer)处理Android系统的HLS播放,另一个是将“src=”媒体从Android手机转换为cast设备(请参见FlingingRenderer)。这两个示例都有子组件需要在浏览器进程中运行。因此,我们使用在renderer.mojom和render_extensions.mojom中定义的Mojo接口将MediaPlayerRendererFlingingRenderer 代理到浏览器进程
这个想法可以概括为处理任何特殊情况Foo场景专门的OOP FooRenderer

创建专用OOP FooRenderer所需的类成对出现,在其各自的过程中用于类似的目的。按照惯例FooRenderer位于目标进程中,而FooRendererClient位于渲染器进程。MojoRendererMojoRenderService代理media::Renderermedia::RendererClient,在FooRendererFooRendererClient之间调用。

无法表示为media::Renderer[Client]的命令和事件通过renderer extensions(请参见“render_extension.mojom”)进行跨进程操作。
FooRenderer[Client]Extensionmojo接口由相应的FooRenderer[Client] 实现。

FooRenderer[Client]Factory设置特定场景的样板,和所有与其他进程对话所需的mojo接口指针/请求。当调用mojom::InterfaceFactory::CreateFooRenderer()时,接口指针和请求进行跨进程。

如下所示:

在这里插入图片描述
要允许在WebMediaPlayer中创建和使用FooRenderer,FooRendererClientFactory必须生成并传递给RendererFactorySelector。还必须为RendererFactorySelector提供查询当前是否需要使用FooRenderer。当我们进入一个Foo scenario时,通过suspend()/resume()循环media::Pipeline应该足够下次调用
RendererFactorySelector::GetCurrentFactory()返回
FooRendererClientFactory。当调用RendererFactory::CreateRenderer()时,
管道将接收FooRendererClient作为不透明的media::Renderer

默认管道状态机将控制OOP FooRenderer。当我们退出Foo scenario时,再次循环管道应该会进入正确的状态。

Support Other Clients

MediaService作为service_manager::Service,可以由除媒体播放器之外的客户端在渲染过程中使用。例如,我们可以有另一个(mojo)服务来处理音频数据,并希望在媒体渲染器中播放它。由于MediaService是一个mojo服务,因此任何其他mojo服务都可以通过service_manager::mojom::Connector连接到它,并使用它所托管的remote media Renderer。

CdmService

尽管MediaService支持media::mojom::CDM,但在某些情况下(例如,桌面应用的CDM库),通常出于安全原因远程CDM需要在其自己的进程中运行,CdmService被引入来处理此问题。

它也实现了service_manager::Service”,并使用kCdmServiceName
ServiceManagerContext 中注册。目前它总是注册为在实用程序进程中运行(使用CDM沙盒类型)。

CdmService还具有对CDM库的额外支持,例如加载CDM库等。

注意:CdmService仅支持media::mojom::CDM,不支持其他媒体播放器mojo接口。

MediaRendererService

MediaRendererService支持media::mojom::Renderermedia::mojom::CDM。它被托管在与默认MediaService不同的进程中。它使用kMediaRendererServiceNameServiceManagerContext中注册。这允许运行media::mojom::VideoDecodermedia::mojom::Renderer在两个不同的进程中。目前Chromecast使用它来支持浏览器进程中的CastRenderer CDM和GPU进程中的GPU加速视频解码器。主要目标是:

  1. 允许两个页面同时保存自己的视频管道,因为CastRenderer一次只支持一个视频管道。
  2. 支持GPU加速视频解码器用于RTC路径。

Mojo CDM and Mojo Decryptor

Mojo CDM在所有媒体播放器Mojo接口中很特殊,因为所有本地/远程媒体组件处理加密缓冲区都需要它。

  1. 本地媒体组件,如DecryptingDemuxerStreamDecryptingAudioDecoderDecrypting VideoDecoder
  2. 托管在MediaService中的远程媒体组件,例如MojoRendererServiceMojoAudioDecoderService
    MojoVideoDecoderService

在JavaScript层,媒体播放器和MediaKeys通过
[setMediaKeys()](https://w3c.github.io/encrypted-media/#dom-htmlmediaelement setmediakeys)连接。这由渲染进程的SetCdm()实现。

媒体组件可以通过两种方式使用CDM。

Using a Decryptor (via CdmContext)

某些CDM提供了Decryptor实现,支持解密方法,例如Decrypt(), DecryptAndDecode()等。AesDecryptor和CDM库都支持Decryptor接口。

对于远程CDM,例如由MediaService或者CdmService中的MojoCdmService托管的CDM,如果远程CDM支持Decryptor接口,
MojoCdm就也支持Decryptor接口,这通过MojoDecryptor实现,它设置了一个新的消息管道来转发所有Decryptor调用远程CDM中的Decryptor

Using CdmContext

在某些情况下,媒体组件被设置为与特定CDM一起工作。
例如,在Android系统中,基于MediaCodec的解码器(例如MediaCodecAudioDecoder)只能通过MediaCryptoContext使用基于MediaDrm的CDM 。媒体组件和CDM必须在同一个过程中,因为这两者的相互作用通常发生在OS级别的深处。理论上,他们都可以在
渲染进程。但在实践中,通常是CDM和媒体组件由MediaService在远程(例如GPU)进程中托管。

为了能够将远程CDM连接到远程媒体组件,每个MediaService中的InterfaceFactoryImpl实例(对应一个RenderFrame)维护一个MojoCdmServiceContext,用于跟踪所有为RenderFrame创建的远程CDM。每个远程CDM分配一个唯一的CDM ID,这个CDM ID在渲染进程中发送回MojoCdm。在渲染进程,当SetCdm()被调用,CDM ID将传递给本地媒体组件(例如MojoRenderer),转发给远程媒体组件
(例如MojoRendererService)。远程媒体组件将从
MojoCdmServiceContext获取与CDM ID关联的CdmContext,以及
完成连接。

Secure Auxiliary Services

媒体组件通常需要其他服务才能工作。在渲染进程中,本地媒体组件通过MediaClient从内容层获取服务。在MediaServiceCdmService中,远程媒体组件通过secure auxiliary services获取服务。

某些服务确实需要RenderFrame或用户配置文件标识,例如文件系统。由于媒体组件都属于给定的RenderFrame,我们出于安全原因,在访问这些服务时必须维护框架标识。这些服务称为安全辅助服务。FrameServiceBase 是所有安全辅助服务的基类,以帮助管理这些服务(例如处理导航)。

创建MediaInterfaceProxy时,除了提供media::mojom::InterfaceFactoryRenderFrame配备了media::mojom::FrameInterfaceFactory,基于每帧的服务开放这些安全辅助。FrameInterfaceFactory直接提供一种方法
通过BindEmbedderReceiver()方法注册其他辅助服务。

目前,只有远程CDM需要安全的辅助服务。这是一个当前支持服务的列表

  • OutputProtection:检查输出保护状态
  • PlatformVerification:检查平台是否安全
  • CdmFileIO:用于CDM存储持久数据
  • ProvisionFetcher:用于Android MediaDrm设备配置

Security

在大多数情况下,客户端运行的最不可信的渲染进程。此外,始终假设客户端代码可能受到破坏,例如以随机顺序调用或传入垃圾参数。

由于[Flexible Process Model],有时很难知道服务端在哪个进程中运行。根据经验,假设所有服务端代码可以在特权进程(例如浏览器进程)中运行,包括常见的支持代码,如MojoVideoDecoderService,以及
具体的[Media Component],例如Android的MediaCodecVideoDecoder。要准确了解 [Media Component]在哪个进程中运行,请参见下面的 [Adoption]。

另请注意,所有 [Secure Auxiliary Services]在一个比媒体所在的进程更有特权的进程中运行。例如,所有现有服务都运行在浏览器进程中。他们必须防御受损的媒体组件。

Adoption

Android
  • MediaService in the GPU process (registered in GpuServiceFactory with
    GpuMojoMediaClient)
  • MojoCdm + MediaDrmBridge (CDM)
  • MediaDrmBridge uses mojo ProvisionFetcher service for CDM provisioning
  • MojoAudioDecoder + MediaCodecAudioDecoder
  • MojoVideoDecoder + MediaCodecVideoDecoder (in progress)
  • HLS support:
    • MojoRenderer + MediaPlayerRenderer
    • NOT using MediaService. Instead, MojoRendererService is hosted by
      RenderFrameHostImpl/MediaInterfaceProxy in the browser process
      directly.
  • Flinging media to cast devices (RemotePlayback API):
    • MojoRenderer + FlingingRenderer
    • NOT using MediaService. Instead, MojoRendererService is hosted by
      RenderFrameHostImpl/MediaInterfaceProxy in the browser process
      directly.
ChromeCast
  • MediaService in the Browser process (registered in
    CastContentBrowserClient with CastMojoMediaClient)
  • MojoRenderer + CastRenderer
  • MojoCdm + CastCdm
Desktop (ChromeOS/Linux/Mac/Windows)
  • CdmService
    • CdmService in the utility process (registered in UtilityServiceFactory with ContentCdmServiceClient)
    • MojoCdm + CdmAdapter + Library CDM implementation
    • CdmAdapter uses various secure auxiliary services
  • MediaService (in progress)
    • MediaService in the GPU process (registered in GpuServiceFactory with GpuMojoMediaClient)
    • MojoVideoDecoder + hardware video decoders such as D3D11VideoDecoder

Other Services

TODO:添加其他mojo服务的文档,例如远程处理等。

;