Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DRMを導入した話
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Kota Yoshikawa
May 29, 2026
Programming
41
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
DRMを導入した話
AndroidアプリでDRMを導入した話をLT枠でお話ししました
https://droidkaigi.connpass.com/event/391168/
Kota Yoshikawa
May 29, 2026
More Decks by Kota Yoshikawa
See All by Kota Yoshikawa
Android OSのバージョンアップに追従するためにすべきこと
clown6613
0
62
Other Decks in Programming
See All in Programming
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
6.1k
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.3k
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
400
Inside Stream API
skrb
1
720
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
240
CSC307 Lecture 17
javiergs
PRO
0
320
3Dシーンの圧縮
fadis
1
770
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.1k
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.3k
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
11
4.1k
Signal Forms: Beyond the Basics @ngBaguette 2026 in Paris
manfredsteyer
PRO
0
250
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
200
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
430
The Cult of Friendly URLs
andyhume
79
6.9k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
160
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.3k
Mobile First: as difficult as doing things right
swwweet
225
10k
Exploring anti-patterns in Rails
aemeredith
3
410
GitHub's CSS Performance
jonrohan
1033
470k
The Cost Of JavaScript in 2023
addyosmani
55
10k
エンジニアに許された特別な時間の終わり
watany
107
250k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
480
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
850
Transcript
DRMを導入した話 2026/5/29 DroidKaigi.collect { #30@Tokyo }
About Me Kota Yoshikawa (吉川 晃太) @clown6613 clown6613 CyberAgent, Inc.
DRM / Widevineとは DRM(Digital Rights Management) デジタルコンテンツの著作権管理技術の総称 コンテンツの再生条件をライセンスで管理する仕組み コンテンツをスクショや録画から守る Widevine
プレミアムメディア向けの Google のコンテンツ保護システム 3段階のセキュリティレベル L1 : Trusted Execution Environment(TEE)内で復号・デコード L2 : TEE内で復号のみ L3 : ソフトウェア処理
なぜDRMが必要か ビジネス観点 コンテンツホルダーとの契約や配信要件 不正録画/再配布による収益損失の防止 技術観点 root化している端末ではFLAG_SECUREによる保護を回避できてしまう Trusted Execution Environment(TEE)というSecureなOS領域での復号が 可能でroot化端末での読み取りが難しい
参考:https://source.android.com/docs/security/features/trusty
Widevineの導入方法 Certified Widevine Implementation Partner Program(CWIP) 自社でWidevineライセンスサーバーを 構築・運用できるようになる ⚠️ 認定プロセスに時間とコストがかかる
Google認定のパートナーになる ライセンスサーバをSaaSとして提供 認定プロセスなしで導入開始できる 導入が比較的早い サーバーの運用・保守が不要 ⚠️ 利用料に応じた費用が発生する DRMベンダーを利用する
Widevineの導入方法 Certified Widevine Implementation Partner Program(CWIP) 自社でWidevineライセンスサーバーを 構築・運用できるようになる ⚠️ 認定プロセスに時間コストがかかる
Google認定のパートナーになる ライセンスサーバをSaaSとして提供 認定プロセスなしで導入開始できる 導入が比較的早い サーバーの運用・保守が不要 ⚠️ 利用料に応じた費用が発生する DRMベンダーを利用する
DRMベンダーを利用したシーケンス 初回のみデバイス証明書の発行 自社で建てたプロキシサーバーを経 由してDRMベンダーへライセンス のリクエスト Playerがライセンス情報を読 み取りコンテンツを復号
今回の主な実装内容 背景:既存実装はHLSのみ対応。DRM保護されたDASHコンテンツの再生には未対応だった DASH再生への対応 ExoPlayerに DashMediaSourceを組 み込み MPD形式のマニフェス トを解析/再生できるよ うに対応 ExoPlayerに
DashMediaSource を組み込み MPD形式のマニフェ ストを解析/再生でき るように対応 ライセンスリクエストの実装 自社プロキシサーバーへ のライセンスリクエスト client情報と認証ヘッ ダーをつけてDRMベン ダーへ中継 必要に応じてログの実装 DrmSessionManagerの実装 MediaDrm Callbackの実 装やDRMセッションの設 定 client情報と認証ヘッダー をつけてDRMベンダーへ 中継
DRM周りの実装例 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class CustomMediaDrmCallback( private val licenseUrl: String, private val authToken: String, ) : MediaDrmCallback { override fun executeProvisionRequest( uuid: UUID, request: ExoMediaDrm.ProvisionRequest ): ByteArray { // プロビジョニングはWidevineサーバーへ直接 return HttpUtil.executePost( request.defaultUrl, request.data, emptyMap() ) } override fun executeKeyRequest( uuid: UUID, request: ExoMediaDrm.KeyRequest ): ByteArray { // 自社Proxy Serverへライセンスリクエストを送信 val headers = mapOf( "Authorization" to "Bearer $authToken", "Content-Type" to "application/octet-stream" ) return HttpUtil.executePost( licenseUrl, request.data, headers ) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun buildDrmSessionManager( licenseUrl: String, authToken: String, ): DrmSessionManager { val callback = CustomMediaDrmCallback( licenseUrl = licenseUrl, authToken = authToken, ) return DefaultDrmSessionManager.Builder() .setUuidAndExoMediaDrmProvider( C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER ) .setMultiSession(false) .build(callback) }
DRM周りの実装例 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class CustomMediaDrmCallback( private val licenseUrl: String, private val authToken: String, ) : MediaDrmCallback { override fun executeProvisionRequest( uuid: UUID, request: ExoMediaDrm.ProvisionRequest ): ByteArray { // プロビジョニングはWidevineサーバーへ直接 return HttpUtil.executePost( request.defaultUrl, request.data, emptyMap() ) } override fun executeKeyRequest( uuid: UUID, request: ExoMediaDrm.KeyRequest ): ByteArray { // 自社Proxy Serverへライセンスリクエストを送信 val headers = mapOf( "Authorization" to "Bearer $authToken", "Content-Type" to "application/octet-stream" ) return HttpUtil.executePost( licenseUrl, request.data, headers ) } } Widevineサーバーへ のリクエスト デバイス証明証 の発行 プロキシサーバーへ のリクエスト 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun buildDrmSessionManager( licenseUrl: String, authToken: String, ): DrmSessionManager { val callback = CustomMediaDrmCallback( licenseUrl = licenseUrl, authToken = authToken, ) return DefaultDrmSessionManager.Builder() .setUuidAndExoMediaDrmProvider( C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER ) .setMultiSession(false) .build(callback) }
実装時にはまったポイント multiSessionの設定 1コンテンツに対して複数の鍵が必要な場合に有効にする(キーローテーションとか) 配信設定で映像と音声で異なる鍵が設定されており、音声しか流れないような現象が発生 エミュレータでのデバッグ L3での検証が限界、動作確認は実機推奨 エミュレータではビルドのたびにprovisionRequestが必要 うまく動かない時はその箇所を見直して正常に動くことがあった Player.Listener#onMetaDataで値が検知できない DASHでの再生に対応する中で、onMetaDataにEventStreamが流れてこなくなった
実装時にはまったポイント multiSessionの設定 1コンテンツに対して複数の鍵が必要な場合に有効にする(キーローテーションとか) 配信設定で映像と音声で異なる鍵が設定されており、音声しか流れないような現象が発生 エミュレータでのデバッグ L3での検証が限界、動作確認は実機推奨 エミュレータではビルドのたびにprovisionRequestが必要 うまく動かない時はその箇所を見直すと良い Player.Listener#onMetaDataで値が検知できない DASHでの再生に対応する中で、onMetaDataが流れてこなくなった
実装時にはまったポイント multiSessionの設定 1コンテンツに対して複数の鍵が必要な場合に有効にする(キーローテーションとか) 配信設定で映像と音声で異なる鍵が設定されており、音声しか流れないような現象が発生 エミュレータでのデバッグ L3での検証が限界、動作確認は実機推奨 エミュレータではビルドのたびにprovisionRequestが必要 うまく動かない時はその箇所を見直して正常に動くことがあった Player.Listener#onMetaDataで値が検知できない DASHでの再生に対応する中で、onMetaDataにEventStreamが流れてこなくなった
まとめ DRMは「動けば終わり」ではなく、映像・音声の鍵管理・セッション設計など細かい設定が再生 品質に影響する Widevineの仕様はドキュメントが少なく、実際に動かしながら挙動を確認する試行錯誤が必要 エミュレータとの挙動, multiSessionの設定など、見落としやすいポイントが多い クライアント側の実装自体は多くないが、バックエンドの実装者とのコミュニケーションをとり ながら設定を決めていく必要がある drm-test: https://github.com/clown6613/drm-test