ごあいさつ
この記事はGlobeeアドベントカレンダー2日目の記事です。
この度はExoPlayer供養にご列席下さりありがとうございます。
ExoPlayerがDeprecatedになったことで、出す機会を失ったナレッジを記事化し、ExoPlayerの供養としたいと思います。
この記事で書きたいこと
- ExoPlayerを使用して、俺俺HLSのマニフェストをパースして再生する
- ExoPlayerを使用して、DRMコンテンツを再生する
- 複数のフォーマットのコンテンツ保護を共存させる
この記事でやらないこと
- コンテンツ保護の意義や解説
- 一般的な使い方
ExoPlayerで俺俺HLSのマニフェストをパースする
HLSはDRMに比べると弱いと言われていますが、逆を言えば融通が聞くということでもあります。
マニフェストファイルを独自フォーマットに変更すれば鍵の取得など、コンテンツ管理に融通を利かすことができます。
1. パサーを実装する
HLSのPlayListは HlsPlaylistParser クラスでパースされます。
このクラスをコピーして独自パサーを実装しましょう。
このコピーして改変したクラスを MyHlsPlaylistParser
とします。
class MyHlsPlaylistParser(
private val masterPlaylist: HlsMasterPlaylist,
private val previousMediaPlaylist: HlsMediaPlaylist?,
) : ParsingLoadable.Parser<HlsPlaylist> {
// パサーの変更内容は省略
}
2. パサーのFactoryを実装する
HlsPlaylistParserFactory
クラスを継承した MyHlsPlaylistParserFactory
クラスを実装します。
(1.)で作ったMyHlsPlaylistParser
は ParsingLoadable.Parser<HlsPlaylist>
を継承していますので、以下のままで大丈夫です。
class MyHlsPlaylistParserFactory : HlsPlaylistParserFactory {
override fun createPlaylistParser(): ParsingLoadable.Parser<HlsPlaylist> =
MyHlsPlaylistParser()
override fun createPlaylistParser(
masterPlaylist: HlsMasterPlaylist,
previousMediaPlaylist: HlsMediaPlaylist?
): ParsingLoadable.Parser<HlsPlaylist> =
MyHlsPlaylistParser(masterPlaylist, previousMediaPlaylist)
}
3. MediaSource.Factory にパサーを設定しよう
hlsのMediaSource.Factoryは HlsMediaSource.Factory
を使用します。
val hlsMediaSourceFactory = HlsMediaSource.Factory(dataSourceFactory)
.setPlaylistParserFactory(MyHlsPlaylistParserFactory())
MediaSource.Factoryまで作成できれば、後はExoPlayerインスタンス作成時に設定できるはずです。
ExoPlayerでDrm
DRMはHLSに比べると、ライセンスサーバで認証を得る必要があり、継続的な費用が発生しますが、強固にコンテンツを保護できます。
著作権のあるコンテンツを配信する場合必須級になってくるため、ExoPlayer側でも手厚くサポートされており標準の機能で実装できます。
ExoではMPEG-DASHをサポートしておりMediaItem生成時に指定することができます。
今回の例では、WideVineを使用してMediaItemを生成します。
/**
* setUri: MPEG-DASHマニフェスト.mpd
* setDrmUuid: `com.google.android.exoplayer2.C.WIDEVINE_UUID` 固定
* setDrmLicenseUri: ライセンスファイル場所。通常Appサーバ
* setDrmLicenseRequestHeaders: DRMライセンス取得にAuthorizationヘッダが必要な場合などはこれを使用する
*/
MediaItem.Builder()
.setUri("https://localhost/path/to/video-content.mpd")
.setDrmUuid(com.google.android.exoplayer2.C.WIDEVINE_UUID)
.setDrmLicenseUri("https:localhost/path/to/wide-vine/licence/file")
.setDrmLicenseRequestHeaders("Authorization Header" to "Header-Value")
.build()
生成したメディアアイテムを使用してコンテンツの再生を行うと、後はExoPlayerが決まったフォーマットでAppサーバとやり取りしてくれます。
様々なフォーマットのメディアソースをいい感じに使いたい
上の2例で俺俺HLSとDRMで保護されたコンテンツを再生する方法を紹介しましたが、1点まだ問題があります。
様々なメディアタイプまぜこぜに再生したいと思ったとき、
メディアタイプ毎にを適切なMediaSource.Factoryに振り分ける機能はExoPlayer側にあるため、
このままでは俺俺HLSと他のフォーマットを共存させることができません。
これらを共存させるためには、メディアタイプ毎に適切なMediaSource.Factoryに振り分ける、親MediaSource.Factoryを自作する必要があります。
通常この機能は DefaultMediaSourceFactory によって提供されます。
なので、 DefaultMediaSourceFactory
も HlsPlaylistParser
のケースと同じく、コピーして改変しましょう。
このコピーしてきたクラスを MyMediaSourceFactory
とします。
try {
// 変更点: 俺俺HLSに対応させるためDefaultMediaSourceFactoryから変更
// val factoryClazz =
// Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource\$Factory")
// .asSubclass(MediaSourceFactory::class.java)
// factories.put(
// C.TYPE_HLS,
// factoryClazz.getConstructor(DataSource.Factory::class.java)
// .newInstance(dataSourceFactory)
// )
val hlsMediaSourceFactory = HlsMediaSource.Factory(dataSourceFactory)
.setPlaylistParserFactory(MyHlsPlaylistParserFactory())
factories.put(C.TYPE_HLS, hlsMediaSourceFactory)
} catch (e: Exception) {
// Expected if the app was built without the hls module.
}
HLSのMediaSourceFactoryを自作したメディアソースに書き換えます。
この改変した MyMediaSourceFactory
を通常のExoPlayerの生成時に指定することで、適切にコンテンツ振り分けを行うMediaSourceFactoryになります。
おわり
いかがでしたでしょうか?
Media3になってもこの知見が生かされることを信じて、ExoPlayer供養の結びとしたいと思います。