DRMで動画を守ってあげたい(ExoPlayer + Widevine)
はじめに
こんにちは!!!!最近初めてOSS(ExoPlayer)にPRが出せて喜んでいるぐみおじです!!!!
今日は動画を不正利用から守るための技術について触れていきます!動画を守れる優しい世界を目指していきましょうしょうb
注: この記事では、Android(ExoPlayer) + DRM(Widevine)にフォーカスをあてていきます!!
なぜ動画を守るか
なんで動画を守る必要があるか?
よく挙げられるのは無断転載とかですかね?誰かが一生懸命時間かけて、利益を得るために作成された動画が第三者にその権利を奪われてしまうとかとか....
そんな悲しい世界を少しでも減らすために動画を不正利用から守っていきましょう!!!
動画を守るには
でわ、どうやって動画を守るのでしょう? たくさんやり方はありますが、セキュリティレベルを上げようと思えば思うほどコストがかかってきます...なので保護対象コンテンツとコストとのトレードオフで選択していきましょう
動画を守る方法にどんなのがある?
たくさんありますが今回は、ストリーミング配信には欠かせないこのご時世かつ、セキュリティレベルが最も高いDRMについてみていきます!!!
余談: めちゃくちゃ余談なんですが、商用のDVD, BDとかを保護するためにCSS, AACS..etc.が使われてるのですが、暗号化keyがインターネットに流出してしまったことで無力になってしまったらしいです....どんな強固なセキュリティもkey一つで裸になるんだな~って。気をつけましょうね!!!
暗号化配信について書いたので、ぜひぜひこちらもmm
DRMとは
next scapeさんの記事を見ましょう!!!!
動画を守る手段の1つで、最もアツいのか〜!くらいの認識で大丈夫です。
DRMを実現するのに重要なCDMについて、少し触れます
CDMとは
Content Decryption Moduleの略。
これはDRMコンテンツを復号化するための役割を持っています。
AndoridのCDMはGoogleに認可されたデバイスに搭載されており、使用するDRMに対応していないと機能しません。
DRM対応したい!CDM搭載している!だからPlayReadyのコンテンツが再生できる!という訳にはいかないということですね、、、
WidevineはGoogleが提供しているので、ほぼ全てのAndroidデバイスに搭載されているのと、ライセンス料がかからないので安心してください。
ライセンスサーバーとは
対象のDRMコンテンツを復号化するための情報のやり取りを行うサーバーです。
CDMを搭載していて、対象のDRMに対してのライセンスサーバーに対応していたら、復号化するための情報をやり取りすることができます。
どゆこと????
対象のDRMごとに企業がライセンスサーバーを管理しています。
DRM対応したい!CDM搭載している!だからPlayReadyのコンテンツが再生できる!という訳にはいかないということですね、、、
これの意味はCDMがPlayReadyに対応していないから、そもそもライセンスサーバーとのやり取りが無視されてしまう的なやつです。
なぜWidevineか
AndroidアプリでExoPlayerを使う前提で話しますが、ExoPlayerのドキュメントにサポートしている対応表が載っています
DRMの選択肢にWidevine, ClearKey, PlayReadyと3種類あります。 cenc, cens, cbcs, cbc1はどうやって暗号化するかな〜っていうモードのようなものだと思ってください。 詳しい説明はこの記事ではしませんので、ぜひ調べてみてください!
- cenc -> CTR モード + Sub-Sample Encryption
- cbc1 -> CBC モード + Sub-Sample Encryption
- cens -> CTR モード + Sub-Sample + Patterned Encryption
- cbcs -> CBC モード + Sub-Sample + Patterned Encryption
ClearKeyはDRMの枠には入りますが、WidevineやPlayReadyと比べると力が弱いです。理由は、JSONでやり取りしているので通信キャプチャなどされてしまうとkey情報などが漏れて、堅牢性が低くなるからです。 そもそもDASHでしか使えないので、HLSで配信している場合は使えません...。
PlayReadyはAndroidTVだけですね。。選択肢から外しましょう。
Widevineを使うのですが、modeはcenc
以外はversion7.1からですね...現状のminSdkVerで考えるとまだ難しそうです。
HLSはfmp4の形式しか対応していないので、気をつけましょう!
余談ですが、WidevineはJSONではなく、ProtocolBufferでやり取りが行われますので安心ですね。
最後にDRMのセキュリティレベルについて触れておきましょう。
DRMセキュリティレベル
DRMにはセキュリティレベルみたいな概念があります。
WidevineだとL1 ~ L3という表現が使われ、L1が最もセキュアです。
先程のPlayReadyの横にSL2000みたいなのが添えられてたと思いますが、あれもPlayReadyのセキュリティレベルの表現方法です。
じゃあ最も強いL1を使えばいいんじゃないの?って思うかもしれませんが、TEEという環境がデバイスに用意されている必要があります。 このTEEで復号化などが行われてるかどうかで、レベルが変わってくるのでデバイスに依存するということですね...。
AndroidにはTrusty OSというAndroid OSと同じプロセッサで並行で動作するOSが提供されてます。これがベンダーによって、対応されていたらL1認定ということですかね?(この辺りは深堀りできていません) source.android.google.cn
TEEで処理しないのが最低レベルのL3になります。ディズニーなどの有名な一部のコンテンツは、配信するにはL1しか許さん!みたいな企業もあるので、L3のみの対応だと厳しそうですね....
制限が設けられてないコンテンツはL3で、一部のコンテンツはL1で扱うようにして、対応できない場合は扱いのデバイスでは残念ながら動画を視聴することはできません....ごめんなさいって表示するのがいいんですかね? :thinking_face:
自分の端末のセキュリティレベルとCDMの詳細知りたくない?
DRM infoというアプリで確認することができますので、興味がある方はみてみるといいかもしれません!
自分はGalaxy S10+を使用しています。
ClearKeyとWidevineに対応していて、レベルはL1ということが分かりますね!さすがGalaxy!
WideVine事前準備
実はめちゃくちゃめんどくさいです...。next scapeさんが準備手順をまとめてくださってるのでこちらを読んでくださいましmm
要するにプロキシサーバーを建てるには、世界のどこかで行われているCWIPという試験に部署の誰かが2人以上合格しないとスタートラインにすら建てないと.....うわぁ....。
そのためのnext scapeさんのようなコストはかかりますが、提供してくださる企業があるというわけですね。なるほど
Android + DRM
AndroidにはDRM機能を提供するためのDRM frameworkが用意されてます。
DRM frameworkがやってくれること
今まで説明に出てきた、ライセンスサーバーとのやり取り、CDMへのアクセスetc.をやってくれるわけですね!
ExoPlayerを使う場合は内部でこれをよしなにやってくれます。
ExoPlayer + DRM
それでは実際に動画にWidevineのプロテクトをかけてみて、ExoPlayerで再生していきましょう!
DRMコンテンツの準備
適当な動画を用意 -> こちらのサンプルを拝借
用意した動画にShakaPackagerでWidevineプロテクトをかけて、HLSとDASHを用意してみる。
このExampleのようにそのまんま実行するだけですb
packager \ in=sample.mp4,stream=audio,output=audio.mp4 \ in=sample.mp4,stream=video,output=video.mp4 \ --protection_scheme cenc \ --enable_widevine_encryption \ --key_server_url https://license.uat.widevine.com/cenc/getcontentkey/widevine_test \ --content_id 7465737420636f6e74656e74206964 \ --signer widevine_test \ --aes_signing_key 1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9 \ --aes_signing_iv d58ce954203b7c9a9a9d467f59839249 \ --mpd_output sample.mpd \ --hls_master_playlist_output sample_master.m3u8
protection_schemeでmode設定できるんですが、cencに指定してます!
生成された動画は自分でhostingしてもいいですし、assets配下に置いて DefaultDataSourceFactory
を使ってlocalで再生も可能ですb
何もせずにこのままWidevineコンテンツを再生してみましょう!
当たり前ですが、動画にはプロテクトがかかってますし、再生ができるわけないです。ただ、しっかりと守られていることが確認できました!
ExoPlayerでDRMを再生
Exoがよしなにやりすぎて、結構シンプルにできちゃいますw
注: 以下のコードは2020/06/21 現在のExoPlayer 2.11.5を使用しています
localのassetsを読み込むためにDefaultDataSource, ライセンスリクエストするためにHttpDataSourceを用意しています
val httpDataSourceFactory = DefaultHttpDataSourceFactory("exoplayer-sample-app") val dataSourceFactory = DefaultDataSourceFactory(this, httpDataSourceFactory) val hlsUri = Uri.parse("asset:///sample_master.m3u8") val dashUri = Uri.parse("asset:///sample_dash.mpd") val drmCallback = HttpMediaDrmCallback( LICENSE_SERVER_TEST, httpDataSourceFactory ) val drmSessionManager = DefaultDrmSessionManager.Builder().build(drmCallback) val hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory) val dashMediaSource = DashMediaSource.Factory(dataSourceFactory) return hlsMediaSource.setDrmSessionManager(drmSessionManager).createMediaSource(hlsUri)
めちゃくちゃシンプルを極めるなら、これだけで再生できます....すごい
再生してみましょう!再生はできますが、自分の端末でしか確認できません!キャプチャしても見ることはできません....DRMの素晴らしさを感じ取っちゃいましたね....()
登場人物はこんな感じです!
ExoMediaDrm -> DRMコンテンツを復号化するための鍵をリクエストするための情報を生成してくれる FrameworkMediaDrm -> ExoMediaDrmを実装したやつ、MediaDrm APIを含むDRM FrameworkをExo内部でwrapしたやつですね!CDMはここに含まれることになります DrmSessionManager -> DrmSessionを管理してくれる(そのまんま) -> 大抵のユースケースはDefaultDrmSessionManagerで満たせる -> Default値でwidevine指定とかproviderに勝手にUUID生成してsetしてくれるので、buildにdrmcallback渡すだけが一番シンプル HttpMediaDrmCallback -> 基本offlineでDRMを取り扱わない限りは通信しないとだめ -> provisioningやkeyのrequest..etc.のDrmSessionからのcallbackを受けて、ライセンスサーバーにpostしてくれる -> localもあるけど、今回はテストサーバーにリクエストするからHttpでいきます
何となく今まで説明してきたところに当てはまりそうですね!
DRMコンテンツ再生したい -> プレイリストなどの様々な情報から、CDMに復号化ししたいから、ライセンスサーバーにリクエストするための情報をくれってお願い -> リクエスト情報を元にPlayerがライセンスサーバーにそれをPOSTして、復号化するための情報をレスポンスとして受け取る -> そのレスポンスをまたCDMに流して、DRMコンテンツが復号される -> 再生完了v
つまり、Playerやアプリ側はライセンスサーバーとのやりとりを仲介してるだけで、ほとんどの作業はDRM Framework配下で行われてることがわかりますね!
以下は、まだまだ自分の理解が曖昧なのでこれから深掘っていきたい箇所です!
key rorateしたい場合 -> multiSession(true)にしたらよい
offline対応したい場合 -> setModeを使い、offlineLicenseKeySetIdを設定してあげるとよさそう
clearなコンテンツとdrmコンテンツが混じってる場合 -> decoder切り替えを毎回行うとパフォーマンスが悪くなるので、setUseDrmSessionsForClearContent(true)を使おう
まとめ
色々準備や知識が大変なDRMですが、使えるようになれば強力な武器になるということが伝わりましたかね!?!?
DRMを使って、動画を守っていきましょう!!
それでは皆さん、よいExo lifeを!!!!!!