Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DRMを導入した話

 DRMを導入した話

AndroidアプリでDRMを導入した話をLT枠でお話ししました
https://droidkaigi.connpass.com/event/391168/

Avatar for Kota Yoshikawa

Kota Yoshikawa

May 29, 2026

More Decks by Kota Yoshikawa

Other Decks in Programming

Transcript

  1. DRM / Widevineとは DRM(Digital Rights Management) デジタルコンテンツの著作権管理技術の総称 コンテンツの再生条件をライセンスで管理する仕組み コンテンツをスクショや録画から守る Widevine

    プレミアムメディア向けの Google のコンテンツ保護システム 3段階のセキュリティレベル 
 L1 : Trusted Execution Environment(TEE)内で復号・デコード
 L2 : TEE内で復号のみ 
 L3 : ソフトウェア処理
  2. Widevineの導入方法 Certified Widevine Implementation Partner Program(CWIP) 自社でWidevineライセンスサーバーを
 構築・運用できるようになる ⚠️ 認定プロセスに時間とコストがかかる

    Google認定のパートナーになる ライセンスサーバをSaaSとして提供 認定プロセスなしで導入開始できる 導入が比較的早い サーバーの運用・保守が不要 ⚠️ 利用料に応じた費用が発生する DRMベンダーを利用する
  3. Widevineの導入方法 Certified Widevine Implementation Partner Program(CWIP) 自社でWidevineライセンスサーバーを
 構築・運用できるようになる ⚠️ 認定プロセスに時間コストがかかる

    Google認定のパートナーになる ライセンスサーバをSaaSとして提供 認定プロセスなしで導入開始できる 導入が比較的早い サーバーの運用・保守が不要 ⚠️ 利用料に応じた費用が発生する DRMベンダーを利用する
  4. 今回の主な実装内容 背景:既存実装はHLSのみ対応。DRM保護されたDASHコンテンツの再生には未対応だった DASH再生への対応 ExoPlayerに DashMediaSourceを組 み込み MPD形式のマニフェス トを解析/再生できるよ うに対応 ExoPlayerに

    DashMediaSource を組み込み MPD形式のマニフェ ストを解析/再生でき るように対応 ライセンスリクエストの実装 自社プロキシサーバーへ のライセンスリクエスト client情報と認証ヘッ ダーをつけてDRMベン ダーへ中継 必要に応じてログの実装 DrmSessionManagerの実装 MediaDrm Callbackの実 装やDRMセッションの設 定 client情報と認証ヘッダー をつけてDRMベンダーへ 中継
  5. 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) }
  6. 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) }