5
1

ExoPlayer供養

Last updated at Posted at 2023-12-01

ごあいさつ

この記事はGlobeeアドベントカレンダー2日目の記事です。

この度はExoPlayer供養にご列席下さりありがとうございます。
ExoPlayerがDeprecatedになったことで、出す機会を失ったナレッジを記事化し、ExoPlayerの供養としたいと思います。

この記事で書きたいこと

  • ExoPlayerを使用して、俺俺HLSのマニフェストをパースして再生する
  • ExoPlayerを使用して、DRMコンテンツを再生する
  • 複数のフォーマットのコンテンツ保護を共存させる

この記事でやらないこと

  • コンテンツ保護の意義や解説
  • 一般的な使い方

ExoPlayerで俺俺HLSのマニフェストをパースする

HLSはDRMに比べると弱いと言われていますが、逆を言えば融通が聞くということでもあります。
マニフェストファイルを独自フォーマットに変更すれば鍵の取得など、コンテンツ管理に融通を利かすことができます。

1. パサーを実装する

HLSのPlayListは HlsPlaylistParser クラスでパースされます。
このクラスをコピーして独自パサーを実装しましょう。
このコピーして改変したクラスを MyHlsPlaylistParser とします。

MyHlsPlaylistParser
class MyHlsPlaylistParser(
    private val masterPlaylist: HlsMasterPlaylist,
    private val previousMediaPlaylist: HlsMediaPlaylist?,
) : ParsingLoadable.Parser<HlsPlaylist> {
    // パサーの変更内容は省略
}

2. パサーのFactoryを実装する

HlsPlaylistParserFactory クラスを継承した MyHlsPlaylistParserFactory クラスを実装します。
(1.)で作ったMyHlsPlaylistParserParsingLoadable.Parser<HlsPlaylist> を継承していますので、以下のままで大丈夫です。

MyHlsPlaylistParserFactory
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 によって提供されます。

なので、 DefaultMediaSourceFactoryHlsPlaylistParser のケースと同じく、コピーして改変しましょう。
このコピーしてきたクラスを MyMediaSourceFactory とします。

MyMediaSourceFactory:384行付近
        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供養の結びとしたいと思います。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1