From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/media/test/eme.js | 495 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 dom/media/test/eme.js (limited to 'dom/media/test/eme.js') diff --git a/dom/media/test/eme.js b/dom/media/test/eme.js new file mode 100644 index 000000000..81f679762 --- /dev/null +++ b/dom/media/test/eme.js @@ -0,0 +1,495 @@ +const CLEARKEY_KEYSYSTEM = "org.w3.clearkey"; + +const gCencMediaKeySystemConfig = [{ + initDataTypes: ['cenc'], + videoCapabilities: [{ contentType: 'video/mp4' }], + audioCapabilities: [{ contentType: 'audio/mp4' }], +}]; + +function IsMacOSSnowLeopardOrEarlier() { + var re = /Mac OS X (\d+)\.(\d+)/; + var ver = navigator.userAgent.match(re); + if (!ver || ver.length != 3) { + return false; + } + var major = ver[1] | 0; + var minor = ver[2] | 0; + return major == 10 && minor <= 6; +} + +function bail(message) +{ + return function(err) { + if (err) { + message += "; " + String(err) + } + ok(false, message); + if (err) { + info(String(err)); + } + SimpleTest.finish(); + } +} + +function ArrayBufferToString(arr) +{ + var str = ''; + var view = new Uint8Array(arr); + for (var i = 0; i < view.length; i++) { + str += String.fromCharCode(view[i]); + } + return str; +} + +function StringToArrayBuffer(str) +{ + var arr = new ArrayBuffer(str.length); + var view = new Uint8Array(arr); + for (var i = 0; i < str.length; i++) { + view[i] = str.charCodeAt(i); + } + return arr; +} + +function StringToHex(str){ + var res = ""; + for (var i = 0; i < str.length; ++i) { + res += ("0" + str.charCodeAt(i).toString(16)).slice(-2); + } + return res; +} + +function Base64ToHex(str) +{ + var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/")); + var res = ""; + for (var i = 0; i < bin.length; i++) { + res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2); + } + return res; +} + +function HexToBase64(hex) +{ + var bin = ""; + for (var i = 0; i < hex.length; i += 2) { + bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + } + return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); +} + +function TimeRangesToString(trs) +{ + var l = trs.length; + if (l === 0) { return "-"; } + var s = ""; + var i = 0; + for (;;) { + s += trs.start(i) + "-" + trs.end(i); + if (++i === l) { return s; } + s += ","; + } +} + +function SourceBufferToString(sb) +{ + return ("SourceBuffer{" + + "AppendMode=" + (sb.AppendMode || "-") + + ", updating=" + (sb.updating ? "true" : "false") + + ", buffered=" + TimeRangesToString(sb.buffered) + + ", audioTracks=" + (sb.audioTracks ? sb.audioTracks.length : "-") + + ", videoTracks=" + (sb.videoTracks ? sb.videoTracks.length : "-") + + "}"); +} + +function SourceBufferListToString(sbl) +{ + return "SourceBufferList[" + sbl.map(SourceBufferToString).join(", ") + "]"; +} + +function UpdateSessionFunc(test, token, sessionType, resolve, reject) { + return function(ev) { + var msgStr = ArrayBufferToString(ev.message); + var msg = JSON.parse(msgStr); + + Log(token, "got message from CDM: " + msgStr); + is(msg.type, sessionType, TimeStamp(token) + " key session type should match"); + ok(msg.kids, TimeStamp(token) + " message event should contain key ID array"); + + var outKeys = []; + + for (var i = 0; i < msg.kids.length; i++) { + var id64 = msg.kids[i]; + var idHex = Base64ToHex(msg.kids[i]).toLowerCase(); + var key = test.keys[idHex]; + + if (key) { + Log(token, "found key " + key + " for key id " + idHex); + outKeys.push({ + "kty":"oct", + "kid":id64, + "k":HexToBase64(key) + }); + } else { + bail(token + " couldn't find key for key id " + idHex); + } + } + + var update = JSON.stringify({ + "keys" : outKeys, + "type" : msg.type + }); + Log(token, "sending update message to CDM: " + update); + + ev.target.update(StringToArrayBuffer(update)).then(function() { + Log(token, "MediaKeySession update ok!"); + resolve(ev.target); + }).catch(function(reason) { + bail(token + " MediaKeySession update failed")(reason); + reject(); + }); + } +} + +function MaybeCrossOriginURI(test, uri) +{ + if (test.crossOrigin) { + return "http://test2.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + uri; + } else { + return uri; + } +} + +function AppendTrack(test, ms, track, token, loadParams) +{ + return new Promise(function(resolve, reject) { + var sb; + var curFragment = 0; + var resolved = false; + var fragments = track.fragments; + var fragmentFile; + + if (loadParams && loadParams.onlyLoadFirstFragments) { + fragments = fragments.slice(0, loadParams.onlyLoadFirstFragments); + } + + function addNextFragment() { + if (curFragment >= fragments.length) { + Log(token, track.name + ": end of track"); + resolve(); + resolved = true; + return; + } + + fragmentFile = MaybeCrossOriginURI(test, fragments[curFragment++]); + + var req = new XMLHttpRequest(); + req.open("GET", fragmentFile); + req.responseType = "arraybuffer"; + + req.addEventListener("load", function() { + Log(token, track.name + ": fetch of " + fragmentFile + " complete, appending"); + sb.appendBuffer(new Uint8Array(req.response)); + }); + + req.addEventListener("error", function(){info(token + " error fetching " + fragmentFile);}); + req.addEventListener("abort", function(){info(token + " aborted fetching " + fragmentFile);}); + + Log(token, track.name + ": addNextFragment() fetching next fragment " + fragmentFile); + req.send(null); + } + + Log(token, track.name + ": addSourceBuffer(" + track.type + ")"); + sb = ms.addSourceBuffer(track.type); + sb.addEventListener("updateend", function() { + if (ms.readyState == "ended") { + /* We can get another updateevent as a result of calling ms.endOfStream() if + the highest end time of our source buffers is different from that of the + media source duration. Due to bug 1065207 this can happen because of + inaccuracies in the frame duration calculations. Check if we are already + "ended" and ignore the update event */ + Log(token, track.name + ": updateend when readyState already 'ended'"); + if (!resolved) { + // Needed if decoder knows this was the last fragment and ended by itself. + Log(token, track.name + ": but promise not resolved yet -> end of track"); + resolve(); + resolved = true; + } + return; + } + Log(token, track.name + ": updateend for " + fragmentFile + ", " + SourceBufferToString(sb)); + addNextFragment(); + }); + + addNextFragment(); + }); +} + +//Returns a promise that is resolved when the media element is ready to have +//its play() function called; when it's loaded MSE fragments. +function LoadTest(test, elem, token, loadParams) +{ + if (!test.tracks) { + ok(false, token + " test does not have a tracks list"); + return Promise.reject(); + } + + var ms = new MediaSource(); + elem.src = URL.createObjectURL(ms); + + return new Promise(function (resolve, reject) { + var firstOpen = true; + ms.addEventListener("sourceopen", function () { + if (!firstOpen) { + Log(token, "sourceopen again?"); + return; + } + + firstOpen = false; + Log(token, "sourceopen"); + return Promise.all(test.tracks.map(function(track) { + return AppendTrack(test, ms, track, token, loadParams); + })).then(function() { + if (loadParams && loadParams.noEndOfStream) { + Log(token, "Tracks loaded"); + } else { + Log(token, "Tracks loaded, calling MediaSource.endOfStream()"); + ms.endOfStream(); + } + resolve(); + }).catch(function() { + Log(token, "error while loading tracks"); + }); + }) + }); +} + +// Same as LoadTest, but manage a token+"_load" start&finished. +// Also finish main token if loading fails. +function LoadTestWithManagedLoadToken(test, elem, manager, token, loadParams) +{ + manager.started(token + "_load"); + return LoadTest(test, elem, token, loadParams) + .catch(function (reason) { + ok(false, TimeStamp(token) + " - Error during load: " + reason); + manager.finished(token + "_load"); + manager.finished(token); + }) + .then(function () { + manager.finished(token + "_load"); + }); +} + +function SetupEME(test, token, params) +{ + var v = document.createElement("video"); + v.crossOrigin = test.crossOrigin || false; + v.sessions = []; + + v.closeSessions = function() { + return Promise.all(v.sessions.map(s => s.close().then(() => s.closed))).then( + () => { + v.setMediaKeys(null); + if (v.parentNode) { + v.parentNode.removeChild(v); + } + v.onerror = null; + v.src = null; + }); + }; + + // Log events dispatched to make debugging easier... + [ "canplay", "canplaythrough", "ended", "error", "loadeddata", + "loadedmetadata", "loadstart", "pause", "play", "playing", "progress", + "stalled", "suspend", "waiting", "waitingforkey", + ].forEach(function (e) { + v.addEventListener(e, function(event) { + Log(token, "" + e); + }, false); + }); + + // Finish the test when error is encountered. + v.onerror = bail(token + " got error event"); + + var onSetKeysFail = (params && params.onSetKeysFail) + ? params.onSetKeysFail + : bail(token + " Failed to set MediaKeys on