こんにちは!hagi@streampackです。

あまりにも情報がなかったのでDASHのClearkey復号について備忘録を残すことにしました。

はじめに

”MPEG-DASHはClearkeyで復号できる”
この情報を元にじゃーやってみようとなりました。

本投稿はどちらかというと”こうやったらできました”ということを記録したいのでなるべくClearkeyはCENCなどの説明を省きますが仕様について興味ある方はEncrypted Media Extensionをご確認ください。

Clearkeyとは?

EMEの仕様書に記述がございますがClearkeyでは平文キーを利用し、クライアントで受け取った暗号化されたファイルとキーを利用してブラウザーのみで実装され、別途ソフトウエアやハードウエアを通さず直接復号し再生します。

This specification does not define a content protection or Digital Rights Management system. Rather, it defines a common API that may be used to discover, select and interact with such systems as well as with simpler content encryption systems. Implementation of Digital Rights Management is not required for compliance with this specification: only the Clear Key system is required to be implemented as a common baseline.

ClearkeyはDRMではありません、あくまでMPEG-CENCで暗号化コンテンツを復号する手段です。

ソフトウエア

bento4(https://www.bento4.com/)

bento4とは

MP4とMPEG DASH用のツールキット

A fast, modern, open source C++ toolkit for all your MP4 and MPEG DASH media format needs

準備

bento4の準備

Bento4のサイトの”Downloads”タブより必要なバイナリをダウンロード後解凍します。

Linuxでの例

$ wget http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-5-1-623.x86_64-unknown-linux.zip
$ unzip -q Bento4-SDK-1-5-1-623.x86_64-unknown-linux.zip

動画の準備

Bento4はmp4のツールのため基本mp4しか処理することができません。
私はこちら(http://bbb3d.renderfarming.net/download.html)より”Big Buck Bunny”の動画をダウンロードしました。

fragmented mp4の作成

MPEG-DASHの場合fragmented mp4を利用する必要があります。

$ wget http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4
$ Bento4-SDK-1-5-1-623.x86_64-unknown-linux/bin/mp4fragment bbb_sunflower_1080p_30fps_normal.mp4 bbb_sunflower_1080p_30fps_normal_frag.mp4

Bento4でフラグメンテーションの確認

フラグメンテーションがされているかどうかはBento4のmp4infoコマンドなどで確認することができます。
mp4infoを実行し”fragments”項目をご確認ください。

元動画

$ Bento4-SDK-1-5-1-623.universal-apple-macosx/bin/mp4info bbb_sunflower_1080p_30fps_normal.mp4
       :                        :
Movie:
  duration:   634533 ms
  time scale: 600
  fragments:  no

フラグメンテーション済み

$ Bento4-SDK-1-5-1-623.universal-apple-macosx/bin/mp4info bbb_sunflower_1080p_30fps_normal_frag.mp4
       :                        :
Movie:
  duration:   634533 ms
  time scale: 1000
  fragments:  yes

暗号キー、IDの準備

32 hex digitのキーとKID準備します。
今回はdateのmd5sumの結果を利用しました。

$ date | md5sum
359bce01a7527ed2bdf8870b88f4d0b6

$ date | md5sum
aa40a4ddc7ab0ea77340975ccd230fc7

そこでキー、KIDをそれぞれ下記とします。

キー:359bce01a7527ed2bdf8870b88f4d0b6
KID:aa40a4ddc7ab0ea77340975ccd230fc7

今回復号用に上記をキーKIDをbase64urlにしたものが必要となります。

キーKIDをbase64に変換します。

$ echo 359bce01a7527ed2bdf8870b88f4d0b6 | xxd -r -p | base64
NZvOAadSftK9+IcLiPTQtg==
>> echo aa40a4ddc7ab0ea77340975ccd230fc7 | xxd -r -p | base64
qkCk3cerDqdzQJdczSMPxw==

利用するのはbase64urlとなりますので最後の”==”を削除してください。

キー:  359bce01a7527ed2bdf8870b88f4d0b6
キー base64:  NZvOAadSftK9+IcLiPTQtg
KID:  aa40a4ddc7ab0ea77340975ccd230fc7
KID base64:  qkCk3cerDqdzQJdczSMPxw

mp4暗号化

Bento4のmp4encryptコマンドでフラグメント化したmp4をMPEG-CENCで暗号化します。
Bento4での暗号化は

$ mp4encrypt --method MPEG-CENC --key [track ID]:[キー]:[IV もしくは"random"] --property [track ID]:[name]:[値] --global-option mpeg-cenc.eme-pssh:true [入力] [出力]

作成したファイルやキーを元に下記を入力します。

trackID:1=動画、2=音声
キー:359bce01a7527ed2bdf8870b88f4d0b6
property name:KID
KID値:aa40a4ddc7ab0ea77340975ccd230fc7
入力:bbb_sunflower_1080p_30fps_normal_frag.mp4
出力:bbb_sunflower_1080p_30fps_normal_enc.mp4

実行

下記では動画トラック(track ID=1)のみを暗号化しております。別トラックを含める場合はそのトラックIDの–key、–propertyをご指定ください。

$ mp4encrypt --method MPEG-CENC --key 1:359bce01a7527ed2bdf8870b88f4d0b6:random --property 1:KID:aa40a4ddc7ab0ea77340975ccd230fc7 --global-option mpeg-cenc.eme-pssh:true bbb_sunflower_1080p_30fps_normal_frag.mp4 bbb_sunflower_1080p_30fps_normal_enc.mp4

復号

ちなみにに復号はmp4decryptでも可能です。

$ mp4decrypt --key 1:359bce01a7527ed2bdf8870b88f4d0b6 bbb_sunflower_1080p_30fps_normal_enc.mp4 bbb_sunflower_1080p_30fps_normal_dec.mp4

mpeg-dash化

mpeg-dash化はmp4dashコマンドで実行します。
Bento4ではABR(adaptive bitrate)も対応していますが今回は割愛します。

$ mp4dash  --use-segment-timeline bbb_sunflower_1080p_30fps_normal_enc.mp4

mp4dashコマンドを実行すると”output” フォルダーが作成その中にマニフェスト、音声、動画ファイルが格納されています。

$ ls output/
audio       stream.mpd  video
$ cat output/stream.mpd
<?xml version="1.0" ?>
<MPD mediaPresentationDuration="PT10M34.533S" minBufferTime="PT4.58S" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011">
  <!-- Created with Bento4 mp4-dash.py, VERSION=1.8.0-623 -->
  <Period>
    <!-- Video -->
    <AdaptationSet maxHeight="1080" maxWidth="1920" mimeType="video/mp4" minHeight="1080" minWidth="1920" segmentAlignment="true" startWithSAP="1">
      <SegmentTemplate initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1" timescale="30000">
        <SegmentTimeline>
          <S d="250000"/>
          <S d="237000"/>
                :           :
          <S d="225000"/>
        </SegmentTimeline>
      </SegmentTemplate>
      <Representation bandwidth="5912870" codecs="avc1.640029" frameRate="30" height="1080" id="video/avc1" scanType="progressive" width="1920"/>
    </AdaptationSet>
    <!-- Audio -->
    <AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1">
      <SegmentTemplate initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1" timescale="48000">
        <SegmentTimeline>
          <S d="400384"/>
          <S d="378880"/>
                :           :
          <S d="345216"/>
        </SegmentTimeline>
      </SegmentTemplate>
      <Representation audioSamplingRate="48000" bandwidth="131552" codecs="mp4a.40.2" id="audio/und/mp4a">
        <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

これらのファイルをwebサーバーで公開する必要があります。
私はそのままs3におき、”make public”しました。

再生

どのように記述したら良いかわからないのでまずは下記だとFirefoxだと再生できますがChromeだとうまく再生はできないです。
base64urlのキーとKIDを”clearkeys”で提供しています。

<!DOCTYPE html>
<html>
<head>
   <link href="//vjs.zencdn.net/7.0/video-js.min.css" rel="stylesheet">
   <title></title>
   <meta charset="utf-8" />
   <script src="//vjs.zencdn.net/7.0/video.js"></script>
   <script src="//cdn.dashjs.org/latest/dash.all.debug.js"></script>
   <script src="js/videojs-dash.min.js"></script>
</head>
<body onload="init()">
   <video id='example-video' width=600 height=300 class="video-js vjs-default-skin" controls> </video>

   <script>
   var init = function () {
      var options = {
         src: 'output/stream.mpd',
         type: 'application/dash+xml',
         keySystemOptions: [
             {
                name: 'org.w3.clearkey',
                options: {
                   clearkeys: {
                      "qkCk3cerDqdzQJdczSMPxw":"NZvOAadSftK9+IcLiPTQtg"
                   }
                }
             }
         ]
      };
      var player = videojs('example-video');
      player.src(options);
      player.play();
   };
   </script>

</body>
</html>

Videojs5+Shaka playerをこちら(https://github.com/halibegic/videojs-shaka)のプラグインを利用することによってFirefoxとChromeでは再生することがけきます。
こちらではキー、KIDのhex値をそのまま利用しています。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="//cdnjs.cloudflare.com/ajax/libs/video.js/5.19.1/alt/video-js-cdn.min.css" rel="stylesheet">
</head>
<body>
  <div class="embed">
    <div>
      <video id="my-video" class="embed-responsive-item video-js" controls preload="auto" width="884" height="497">
        <source src="output/stream.mpd" type="application/dash+xml">
      </video>
    </div>
  </div>
  <script src="//cdnjs.cloudflare.com/ajax/libs/video.js/5.19.1/video.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/shaka-player/2.1.0/shaka-player.compiled.js"></script>
  <script src="js/videojs-shaka.js"></script>
  <script type="text/javascript">
    //https://github.com/halibegic/videojs-shaka

    var player = videojs('#my-video', {
      techOrder: ['shaka', 'html5'],
      shaka: {
        drm: {
          clearKeys: {
            'aa40a4ddc7ab0ea77340975ccd230fc7': '359bce01a7527ed2bdf8870b88f4d0b6'
          }
        }
      },
      fluid: true
    });
  </script>
</body>

</html>

暗号化+DASH化を1発でやるには

ちなみに暗号化とDASH化は1発で行うことが可能です。

例:

$ mp4dash --use-segment-timeline --encryption-key=aa40a4ddc7ab0ea77340975ccd230fc7:359bce01a7527ed2bdf8870b88f4d0b6 --encryption-args="--global-option mpeg-cenc.eme-pssh:true" bbb_sunflower_1080p_30fps_normal_enc.mp4

最後に

暗号化はBento4を利用することにより比較的簡単に行うことができますが復号はかなり癖がある印象でした。
本投稿はあまり詳細な情報がなく、今後追記もしくは続編を投稿する予定ですがどなたかの参考程度になれば幸いです。

EMEの仕様書と合わせてSam Duttonさんのこちら(https://www.html5rocks.com/ja/tutorials/eme/basics/) すごく参考となりました。(日本語訳です。)

HLSのAES-128暗号化はここ(Video on demand (VOD) HLSをPCで作成)をご参照ください。

元記事はこちら

MPEG-DASHストリームのClearkey復号