はじめに
Exoplayerのsupported-formatsを見ると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系
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系でも使えます。
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;
}
}
なお、先程も書いたとおり、既存ソースの処理を真似してなんとか動くようにしたという状態なので、詳しくは理解していません。
くれぐれも自己責任でお願いします。
確信が欲しいなぁ・・・。