media/mojo
このフォルダにはMOJOインターフェース、クライアントそして実装が含まれている。これはコア「メディア」ターゲットを拡張しプロセス外での動作をサポートする。Media Playerやメトリクス(WatchTime)などが含まれる。
メディアプレイヤー
メディアコンポーネント
メディアプレイヤー(WebMediaPlayer)はChromiumでHTML5の<video>再生をサポートする。機能は多くのメディアコンポーネントに分けられている(すわなちメディアレンダラー、音声デコーダー、映像デコーダーそしてコンテント暗号解除モジュール(CDM))。CDMは保護されたコンテントで必要である。詳しくは文書メディア?を参照。
大部分のHTML5メディアプレイヤースタックと暗号メディア拡張(EME)スタックはサンドボックス化されたレンダラープロセス内にある(セキュリティのため)ため、いくつかのメディアコンポーネントは他のプロセスで実行されなければならない。例えば以下:
- ハードウェアベースのメディアレンダラー。この場合音声や映像のデコードとレンダリングはハードウェアで行われるためサンドボックス内では実行できない。
- ハードウェアベースの映像デコーダ。この場合はハードウェアのデコードライブラリはサイドボックス内では利用できない。
- Androidでは、メディアコンポーネントはAndroidJavaAPIに依存するため、サンドボックス内では利用できない。
- サードパーティのコードが含まれるCDMは専用のサンドボックスプロセスが必要である。
よってプロセス外実行のメディアコンポーネントを提供する。
メディアプレイヤーMOJOインターフェース
メディアコンポーネントをリモートでホストするためにmojomインターフェースを使う。これらのインターフェースはメディアプレイヤーMOJOインターフェースと呼ばれる。それに対応するC++クラスと類似している。
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
リモートメディアコンポーネントを有効にする
標準のクライアントとインターフェース実装はあらかじめ用意されている。例えばmedia::Renderer
ではMojoRender
が実装し、呼び出しをmedia::mojom::RendererPtr
に転送する。MojoRendererService
はmedia::mojom::Renderer
を実装し、media::Rederer
実装をホストできる。
リモートメディアコンポーネントは簡単に有効にでき、現在のメディアパイプラインにシームレスに統合できる。やり方は単にgn引数のmojo_media_serviceで有効にしたいリモートメディアコンポーネントを指定すればよい。以下の例ではメディパイプラインはMojoRendererとMojoCdmを有効にする。
enable_mojo_media = true mojo_media_services = ["renderer", "cdm"]
注:enable_mojo_mediaを最初に設定すること。
メディアMOJOインタフェースファクトリ
media::mojom::InterfaceFactory
はCreateRenderer()
,CreateCdm()
のようなファクトリメソッドをもつ。メディアプレイヤーMOJOインタフェースを要求するために使われる。
レンダープロセスでは、それぞれのRenderFrameImpl
はmedia::mojom::InterfaceFactoryPtr
をもち、当該プロセスのためのメディアプレイヤーMOJOインタフェースをブラウザプロセスから要求する。ブラウザプロセスでは、それぞれのRenderFrameHostImpl
はMediaInterfaceProxy
を所有し、これはmedia::mojom::IntrefaceFactory
を実装する。
MediaInterfaceProxy
はメディアプレイヤーMOJOインタフェース要求の中心ハブである。デフォルトではすべてのリクエストはMediaService
に転送される。しかしより複雑な場合や特別な場合を想定した柔軟性ももっている。例えば、デスクトッププラットフォームでは、CDMライブラリが有効なとき、media::mojom::ContentDecryptionModule
要求はそれ専用のユーティリティプロセスで動作しているCdmService
に転送される。他の例としてアンドロイドでは、media::mojom::Renderer
要求はRenderFrameHostImpl
で扱われ、ブラウザプロセス内にMediaPlayerRenderer
を作成する(設定でMediaService
をGPUプロセスで動かすようにしていてもそうなる)。
注:media::mojom::InterfaceFactory
インターフェースはMediaInterfaceProxy
とMediaService
間の通信で再利用される(以下参照)。
メディアサービス
メディアサービスはmojoのservice_manager::Service
でメディアプレイヤーMOJOインタフェースを実装する。いくつかのの利点がある。
柔軟なプロセスモデル
プラットフォームやプロダクトの違いにより、リモートメディアコンポーネントをどこで実行すべきかに違いがある。例えばハードウェアデコーダは通常はGPUプロセスで動くべきである。ServiceManagerContext
によりservice_manager::Service
の実行場所を選択できる。すなわちインプロセス(ブラウザ)かプロセス外(ユーティリティ)かあるいはGPUプロセスかを選択できる。よってMediaService
を使うことにより、リモートメディアコンポーネントをChromiumのプロセスタイプ(ブラウザ/ユーティリティ/GPU)でサポートするのが容易である。この設定はgn引数のmojo_media_hostで行うことができる。
mojo_media_host = "browser" or “gpu” or “utility”
メディアサービスはkMediaServiceName
を使ってServiceManagerContext
内で登録される。mojo_media_hostによりどのプロセスでサービスが登録され実行されるかが決定される。
異なるメディアコンポーネントを接続する。
いくつかのリモートメディアコンポーネントは他のコンポーネントに依存している。例えばレンダラーや音声映像デコーダが暗号ストリームを扱うにはCDMが必要である。通常はSetCdm()呼び出しでレンダラなどをCDMと接続する。もし、レンダラインターフェースとCDMインタフェースが別々にホストされていたら、SetCdm()
呼び出しを実装するのが難しくなるだろう。その両サイドを知るオブジェクトが必要になる。メディアサービスはこれを内部的に処理し、そのようなオブジェクトとして振る舞う。車輪の再開発は必要ない。詳しくは以下を参照。
MojoMediaClientによるカスタマイズ
MediaService
はプロセス外メディアコンポーネントをホストするのに必要なすべての機能を提供する。しかしメディアコンポーネント自体は提供しない。それは具体的なメディアコンポーネント実装を提唱するのはクライアントの役割である。
MojoMediaClient
インターフェースはMediaService
クライアントがメディアコンポーネントの実装を提供する方法を提供する。MediaService
が作成されるとき、MojoMediaClient
を渡せばMediaService
は作成方法を知ることができる。
例えばChromeCastはMediaService
を使ってメディアレンダラとCDMをブラウザプロセス内でホストし、CastRenderer
とCastCdm
をCastMojoMediaClient
経由で提供している。すべての状況でこのオーバーライド機構を使うわけではない。
サイト孤立化
Blinkでは、メディアエレメントとEMEメディアキーはどちらもWebLocalFrame
に属している。Chromiumでは、メディアプレイヤーとCDMはRenderFrame
に属していると解釈される。レンダプロセスではこれは明確であるが、すべてのメディアコンポーネントを1つMediaService
でホストすると(サービスマネージャは1プロセスに対して1つのサービスインスタンスしかサポートしない)フレーム境界が曖昧になる。互いにやりとりするメディアコンポーネントにとってこれは特に危険である。例えばfoo.comのレンダラはbar.netのCDMと同じMediaService内に存在することになる。bar.netのCDMがfoo.comレンダラの暗号解除に使われることは間違っている。
これを防ぐために、RenderFrame
境界をシミュレートする追加レイヤーを導入している。MediaService
は複数のInterfaceFactory
をホストし(RenderFrame
ごとに)、それぞれのIterfaceFactory
はそれが作成したメディアコンポーネントを管理する。
この理由からmedia::mojom::InterfaceFactory
インタフェースはMediaInterfaceProxy
とMediaService
間の通信で再利用される。
注:media::mojom::InterfaceFactory
の責任を分割してどのインターフェイスがどこで使われているかを明確にするプランもある。
特別なプロセス外media::Renderer達
The media::Renderer interface is a simple API, which is general enough to capture the essence of high level media playback commands. This allows us to extend the functionality of the WebMediaPlayer via specialized renderers. Specifically, we can build a sub-component that encapsulates the complexities of an advanced scenario, write a small adapter layer that exposes the component as a media::Renderer, and embed it within the existing media::Pipeline state machine. Specialized Renderers reduce technical complexity costs, by limiting the scope of details to the files and classes need them, by requiring little control flow boilerplate, and by generally having little impact on the default paths that WebMediaPlayer uses most of the time.
Two examples of complex scenarios enabled by specialized renderers are: handling HLS playback on Android by delegating it to the Android Media Player (see MediaPlayerRenderer) and casting “src=” media from an Android phone to a cast device (see FlingingRenderer). Both of these examples have sub-components that need to live in the Browser process. We therefore proxy the MediaPlayerRenderer and FlingingRenderer to the Browser process, using the Mojo interfaces defined in renderer.mojom and renderer_extensions.mojom. This idea can be generalized to handle any special case Foo scenario as a specialized OOP FooRenderer.
The classes required to create a specialized OOP FooRenderer come in pairs, serving similar purposes in their respective processes. By convention, the FooRenderer lives in the target process and the FooRendererClient lives in the Renderer process. The MojoRenderer and MojoRendererService proxies media::Renderer and media::RendererClient calls to/from the FooRenderer[Client]. One-off commands and events that can't be expressed as a media::Renderer[Client] call are carried across process boundaries by renderer extensions instead (see renderer_extension.mojom). The FooRenderer[Client]Extension mojo interfaces are implemented by their respective FooRenderer[Client] instances directly. The FooRenderer[Client]Factory sets up the scenario specific boilerplate, and all of the mojo interface pointers/requests needed to talk to the other process. Interface pointers and requests are connected across process boundaries when mojom::InterfaceFactory::CreateFooRenderer() is called. The end result is illustrated below: