Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
DRMを導入した話
Search
Kota Yoshikawa
May 29, 2026
Programming
66
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
64
Other Decks in Programming
See All in Programming
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
900
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
320
Rでドレミ/Do-Re-Mi_with_R
florets1
0
120
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
8
5.5k
Simple Storage Service(S3) is not simple
iwatsukayura
0
190
CodeRabbitの効果検証と過ごしてみた3ヶ月
armondando
0
140
R8 の設定見直しでアプリサイズ削減
andpad
0
140
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.4k
phperだってパンチカードでコーディングしたい
o0h
PRO
0
200
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
440
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
580
iOSDC2026登壇資料.pdf
riofujimon
0
210
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
How to Talk to Developers About Accessibility
jct
2
560
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
74
42k
Designing Powerful Visuals for Engaging Learning
tmiket
1
590
4 Signs Your Business is Dying
shpigford
187
23k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
530
How to Ace a Technical Interview
jacobian
280
24k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
Building Applications with DynamoDB
mza
96
7.2k
Visualization
eitanlees
153
17k
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
Docker and Python
trallard
47
4.2k
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