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

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

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

#はじめに

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

本投稿はどちらかというと”こうやったらできました”ということを記録したいのでなるべくClearkeyはCENCなどの説明を省きますが仕様について興味ある方は[Encrypted Media Extension](https://www.w3.org/TR/encrypted-media)をご確認ください。

##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での例

```console
$ 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を利用する必要があります。

```console
$ 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”項目をご確認ください。

元動画

```console
$　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
```

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

```console
$ 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の結果を利用しました。

```console
$　date | md5sum
359bce01a7527ed2bdf8870b88f4d0b6

$　date | md5sum
aa40a4ddc7ab0ea77340975ccd230fc7
```

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

```
キー：359bce01a7527ed2bdf8870b88f4d0b6
KID：aa40a4ddc7ab0ea77340975ccd230fc7
```

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

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

```console
$ 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での暗号化は

```console
$ 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をご指定ください。

```console
$ 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でも可能です。

```console
$ 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)も対応していますが今回は割愛します。

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

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

```xml
$　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"で提供しています。

```html
<!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>
```

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

```html
<!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化を１発でやるには

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

例：

```console
$ 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で作成](https://qiita.com/khagi/items/6e8c8d10c77bee53487b))をご参照ください。