ExoplayerでMPEG-DASH + PlayReadyの動画を再生するためのメモ

# はじめに
Exoplayerの[supported-formats](https://google.github.io/ExoPlayer/supported-formats.html)を見るとMPEG-DASH + PlayReadyの動画が再生できるように見えるのですが、実際に再生しようとしたらうまく出来ませんでした・・・。
試行錯誤の末、なんとか出来るようになったためメモ。

**注意：厳密に検証は行っていないため、自己責任でお願いします。**

追記：Exoplayerにissueをあげたところ、PlayReadyは`<cenc:pssh>`elementと`<mspr:pro>`elementを使用でき、exoplayerは`<cenc:pssh>`elementのみ対応しているとのこと。
私のように使用したパッケージャーが`<mspr:pro>`elementしか出力しない場合、`<mspr:pro>`elementをパースする処理が必要となるようです・・・。

更に追記：Exoplayer v2系には`<mspr:pro>`対応が取り込まれたため、下記対応はv1系のみで良くなりました。
 


# 問題と修正箇所
エラーの発生箇所を調べていたら、Manifestファイルのパース時にPlayReady用の処理が書かれていないことが判明。
~~対応しているんじゃなかったのかいヾ(*｀ω´*)ﾉ　ﾌﾟﾝｽｶ~~

という訳でWidevineのパースしている箇所と、SmoothStreamingでPlayReadyを処理している箇所を調べて、試行錯誤した結果がこちらです。

Exoplayer 1.x系

``` MediaPresentationDescriptionParser.java
  protected ContentProtection parseContentProtection(XmlPullParser xpp)
      throws XmlPullParserException, IOException {
    String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
    UUID uuid = null;
    SchemeInitData data = null;
    boolean seenPsshElement = false;
    do {
      xpp.next();
      // The cenc:pssh element is defined in 23001-7:2015.
      if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
        seenPsshElement = true;
        data = new SchemeInitData(MimeTypes.VIDEO_MP4,
            Base64.decode(xpp.getText(), Base64.DEFAULT));
        uuid = PsshAtomUtil.parseUuid(data.data);
      }
      // ----- Add start -----
      if (ParserUtil.isStartTag(xpp, "mspr:pro") && xpp.next() == XmlPullParser.TEXT) {
        seenPsshElement = true;
        uuid = UUID.fromString(schemeIdUri.split(":")[2]);
        data = new SchemeInitData(MimeTypes.VIDEO_MP4,
                PsshAtomUtil.buildPsshAtom(uuid,Base64.decode(xpp.getText(), Base64.DEFAULT)));
      }
      // ----- Add end ----- 
    } while (!ParserUtil.isEndTag(xpp, "ContentProtection"));
    if (seenPsshElement && uuid == null) {
      Log.w(TAG, "Skipped unsupported ContentProtection element");
      return null;
    }
    return buildContentProtection(schemeIdUri, uuid, data);
  }
```

なお、Exoplayer 2.x系でも使えます。

``` DashManifestParser.java
  protected SchemeData parseContentProtection(XmlPullParser xpp) throws XmlPullParserException,
      IOException {
    String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); // Add
    byte[] data = null;
    UUID uuid = null;
    boolean seenPsshElement = false;
    boolean requiresSecureDecoder = false;
    do {
      xpp.next();
      // The cenc:pssh element is defined in 23001-7:2015.
      if (XmlPullParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
        seenPsshElement = true;
        data = Base64.decode(xpp.getText(), Base64.DEFAULT);
        uuid = PsshAtomUtil.parseUuid(data);
      } else if (XmlPullParserUtil.isStartTag(xpp, "widevine:license")) {
        String robustnessLevel = xpp.getAttributeValue(null, "robustness_level");
        requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW");
      // ----- Add start -----
      } else if (ParserUtil.isStartTag(xpp, "mspr:pro") && xpp.next() == XmlPullParser.TEXT) {
        seenPsshElement = true;
        uuid = UUID.fromString(schemeIdUri.split(":")[2]);
        data = new SchemeInitData(MimeTypes.VIDEO_MP4,
                PsshAtomUtil.buildPsshAtom(uuid,Base64.decode(xpp.getText(), Base64.DEFAULT)));
      }
      // ----- Add end ----- 
    } while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection"));
    if (!seenPsshElement) {
      return null;
    } else if (uuid != null) {
      return new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder);
    } else {
      Log.w(TAG, "Skipped unsupported ContentProtection element");
      return null;
    }
}
```

なお、先程も書いたとおり、既存ソースの処理を真似してなんとか動くようにしたという状態なので、詳しくは理解していません。
くれぐれも自己責任でお願いします。

確信が欲しいなぁ・・・。