0
1

More than 3 years have passed since last update.

ExoPlayerでHLSをData URIで復号・再生するには

Last updated at Posted at 2020-04-30

※この記事はDRMが施されたHLSの復号についてではなく、AES等で暗号化されたHLSを復号する方法について記載しています。

HLSの暗号化の仕様

HLSストリームの暗号化に関する情報はm3u8ファイルの#EXT-X-KEYに記載されています。
例えば、以下のファイルでは暗号化の方法・暗号化キーのurl・初期化ベクトルの値がわかります。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:1653
#EXT-X-DISCONTINUITY-SEQUENCE:36
#EXT-X-KEY:METHOD=AES-128,URI="https://hogehoge",IV=0xdad51a9c61cc4344c03a566980ac21e7
#EXTINF:8.333333,
/ts/CD8xZLiy68pVp3/h264/480/a1qLvavaVwX.ts
#EXTINF:8.333334,
/ts/CD8xZLiy68pVp3/h264/480/LAAK1hXHVLa.ts
#EXTINF:8.333333,
/ts/CD8xZLiy68pVp3/h264/480/KXzsNPf8KYx.ts
#EXTINF:8.333333,
/ts/CD8xZLiy68pVp3/h264/480/JmHJgZGPZQA.ts

このキーファイルのURIはData URI schemeでもExoPlayerで再生することができます(参考)。
つまり、暗号化キーのUrlではなくキーそのものをm3u8ファイルにベタ書きしてHLSを配信・再生することが可能だということです。

パーサをカスタマイズする

HLSに限らず、ExoPlayerではDataSourceを自由にinjectすることが可能です。
ここではデフォルトで使用されているHlsPlaylistParserの代わりに自前でカスタマイズしたHLSMediaParserを使用してExoPlayerを初期化します。

class CustomHlsPlaylistParserFactory() : HlsPlaylistParserFactory  {

    override fun createPlaylistParser(): ParsingLoadable.Parser<HlsPlaylist> =
        CustomHlsPlaylistParser()

    override fun createPlaylistParser(masterPlaylist: HlsMasterPlaylist): ParsingLoadable.Parser<HlsPlaylist> =
        CustomHlsPlaylistParser(masterPlaylist)
}

class CustomHlsPlaylistParser() : ParsingLoadable.Parser<HlsPlaylist> {


    @Throws(IOException::class)
    override parse(uri: Uri, inputStream: InputStream): HlsPlaylist {
     // パーサを実装
     // HlsPlaylistParser#parseMediaPlaylist`のfullSegmentEncryptionKeyUri`を任意のData Uri schemeに書き換えます
    }


    private fun toDataUriScheme(file: File): String {
        val base64str = Base64.encodeToString(file.readBytes(), Base64.NO_WRAP)//NO_PADDINGは使用できません
        return "data:text/plain;base64,$base64str"
    }
}


fun initPlayer(remoteUrl: String): HlsMediaSource {
    val userAgent = WebView(getApplication()).settings.userAgentString
    val hppf = CustomHlsPlaylistParserWrapper(getApplication(), this)
    val dsf = DefaultDataSourceFactory(getApplication(), userAgent)//DefaultDataSourceFactory
    val mediaSource =  HlsMediaSource.Factory(dsf)
        .setPlaylistParserFactory(hppf)
        .createMediaSource(remoteUrl.toUri())
    player.prepare(mediaSource)
    player.playWhenReady = true
}

なお、HLSの仕様書によればAES-128ではキーのパディングを省略できないようです。

An encryption method of AES-128 signals that Media Segments are completely encrypted using the Advanced Encryption Standard (AES) [AES_128] with a 128-bit key, Cipher Block Chaining (CBC), and Public-Key Cryptography Standards #7 (PKCS7) padding [RFC5652].

また、DataSourceFactoryにはDefaultHttpDataSourceFactoryではなくDefaultDataSourceFactoryを用います(DRMを使用する場合はDataSchemeDataSource.Factory)。もしDefaultHttpDataSourceFactoryを使用すると、キーのURIがUrlとして扱われてしまい、MalformedURLExceptionがスローされます。

これで任意のキーに書き換えることができました。

0
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
0
1