blob: 26d8d0e096104595222c595c26017b88dbf0499c [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2014 The Cobalt Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Legacy Media Source and Encrypted Media Conformance Tests (2013)</title>
<link rel="stylesheet" href="style-20150612143746.css" type="text/css"></link>
<script type="text/javascript">
function Alert(msg) {
console.log(msg);
throw msg;
}
// util-20150612143746.js begin
(function() {
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
throw new TypeError('What is trying to be bound is not a function');
}
var aArgs = Array.prototype.slice.call(arguments, 1);
var fToBind = this;
var fNOP = function() {};
var fBound = function() {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
var util = {};
util.createElement = function(tag, id, class_, innerHTML) {
var element = document.createElement(tag);
if (id != null)
element.id = id;
if (innerHTML != null)
element.innerHTML = innerHTML;
if (class_ != null)
element.classList.add(class_);
return element;
};
util.getClosestElement = function(refElement) {
if (arguments.length === 1)
return null;
var bestElement = arguments[1];
var bestDistance =
Math.abs((bestElement.offsetLeft + bestElement.offsetWidth / 2) -
(refElement.offsetLeft + refElement.offsetWidth / 2));
for (var i = 2; i < arguments.length; ++i) {
var currElement = arguments[i];
var currDistance =
Math.abs((currElement.offsetLeft + currElement.offsetWidth / 2) -
(refElement.offsetLeft + refElement.offsetWidth / 2));
if (currDistance < bestDistance) {
bestDistance = currDistance;
bestElement = currElement;
}
}
return bestElement;
};
util.fireEvent = function(obj, eventName) {
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent(eventName, true, false);
obj.dispatchEvent(event);
} else if (document.createEventObject) {
obj.fireEvent('on' + eventName);
}
};
util.getElementWidth = function(element) {
var style = window.getComputedStyle(element);
var width = 0;
if (!isNaN(parseInt(style.width))) width += parseInt(style.width);
if (!isNaN(parseInt(style.marginLeft))) width += parseInt(style.marginLeft);
if (!isNaN(parseInt(style.marginRight))) width += parseInt(style.marginRight);
return width;
};
util.isValidArgument = function(arg) {
return typeof(arg) != 'undefined' && arg != null;
};
util.MakeCapitalName = function(name) {
name = name.substr(0, 1).toUpperCase() + name.substr(1);
var offset = 0;
for (;;) {
var space = name.indexOf(' ', offset);
if (space === -1)
break;
name = name.substr(0, space + 1) +
name.substr(space + 1, 1).toUpperCase() + name.substr(space + 2);
offset = space + 1;
}
return name;
};
util.Round = function(value, digits) {
return Math.round(value * Math.pow(10, digits)) / Math.pow(10, digits);
};
util.SizeToText = function(size, unitType) {
var unit = 'B';
if (!!unitType && (unitType == 'B' || unitType == 'b')) {
unit = unitType;
}
if (size >= 1024 * 1024) {
size /= 1024 * 1024;
unit = 'M';
} else if (size >= 1024) {
size /= 1024;
unit = 'K';
}
if ((size - Math.floor(size)) * 10 <
Math.floor(size))
size = Math.floor(size);
else
size = util.Round(size, 3);
return size + unit;
};
util.formatStatus = function(status) {
if (typeof status === 'undefined')
return 'undefined';
else if (typeof status === 'string')
return '"' + status + '"';
else if (typeof status === 'number')
return status.toString();
else if (typeof status === 'boolean')
return status ? 'true' : 'false';
throw 'unknown status type';
};
util.getAttr = function(obj, attr) {
attr = attr.split('.');
if (!obj || attr.length === 0)
return undefined;
while (attr.length) {
if (!obj)
return undefined;
obj = obj[attr.shift()];
}
return obj;
};
util.resize = function(str, newLength, fillValue) {
if (typeof str != 'string')
throw 'Only string is supported';
if (str.length > newLength) {
str.substr(0, newLength);
} else {
while (str.length < newLength)
str += fillValue;
}
return str;
};
window.util = util;
})();
// util-20150612143746.js end
// streamDef-20150612143746.js begin
function getStreamDef(index) {
var d = {};
index = index || 0;
var streamDefinitions = [
{
AudioType: 'audio/mp4; codecs="mp4a.40.2"',
VideoType: 'video/mp4; codecs="avc1.640028"',
AudioTiny: ['media/car-20120827-8b.mp4', 717502, 181.62],
AudioNormal: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
200000: 12.42}],
AudioNormalAdv: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
200000: 12.42}],
AudioHuge: ['media/car-20120827-8d.mp4', 5789853, 181.58, {
'appendAudioOffset': 17.42}],
VideoTiny: ['media/car-20120827-85.mp4', 6015001, 181.44, {
'videoChangeRate': 11.47}],
VideoNormal: ['media/car-20120827-86.mp4', 15593225, 181.44, {
'mediaSourceDuration': Infinity}],
VideoHuge: ['media/car-20120827-89.mp4', 95286345, 181.44],
AudioTinyClearKey: ['media/car_cenc-20120827-8b.mp4', 783470, 181.62],
AudioNormalClearKey: ['media/car_cenc-20120827-8c.mp4', 3013084, 181.58],
AudioHugeClearKey: ['media/car_cenc-20120827-8d.mp4', 5918365, 181.58],
VideoTinyClearKey: ['media/car_cenc-20120827-85.mp4', 6217017, 181.44],
VideoNormalClearKey: ['media/car_cenc-20120827-86.mp4', 15795193, 181.44],
VideoHugeClearKey: ['media/car_cenc-20120827-89.mp4', 95488313, 181.44],
VideoStreamYTCenc: ['media/oops_cenc-20121114-145-no-clear-start.mp4', 39980507, 242.71],
VideoTinyStreamYTCenc: ['media/oops_cenc-20121114-145-143.mp4', 7229257, 30.03],
VideoSmallStreamYTCenc: ['media/oops_cenc-20121114-143-no-clear-start.mp4', 12045546, 242.71],
Audio1MB: ['media/car-audio-1MB-trunc.mp4', 1048576, 65.875],
Video1MB: ['media/test-video-1MB.mp4', 1053406, 1.04],
ProgressiveLow: ['media/car_20130125_18.mp4', 15477531, 181.55],
ProgressiveNormal: ['media/car_20130125_22.mp4', 55163609, 181.55],
ProgressiveHigh: [],
}, {
AudioType: 'audio/mp4; codecs="mp4a.40.2"',
VideoType: 'video/webm; codecs="vp9"',
AudioTiny: ['media/car-20120827-8b.mp4', 717502, 181.62],
AudioNormal: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
200000: 12.42}],
AudioNormalAdv: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
200000: 12.42}],
AudioHuge: ['media/car-20120827-8d.mp4', 5789853, 181.58, {
'appendAudioOffset': 17.42}],
VideoTiny: ['media/feelings_vp9-20130806-242.webm', 4478156, 135.46, {
'videoChangeRate': 15.35}],
VideoNormal: ['media/feelings_vp9-20130806-243.webm', 7902885, 135.46, {
'mediaSourceDuration': 135.469}],
VideoHuge: ['media/feelings_vp9-20130806-247.webm', 27757852, 135.46],
AudioTinyClearKey: ['media/car_cenc-20120827-8b.mp4', 783470, 181.62],
AudioNormalClearKey: ['media/car_cenc-20120827-8c.mp4', 3013084, 181.58],
AudioHugeClearKey: ['media/car_cenc-20120827-8d.mp4', 5918365, 181.58],
VideoTinyClearKey: [],
VideoNormalClearKey: [],
VideoHugeClearKey: [],
VideoStreamYTCenc: [],
VideoTinyStreamYTCenc: [],
VideoSmallStreamYTCenc: [],
Audio1MB: ['media/car-audio-1MB-trunc.mp4', 1048576, 65.875],
Video1MB: ['media/vp9-video-1mb.webm', 1103716, 1.00],
ProgressiveLow: [],
ProgressiveNormal: [],
ProgressiveHigh: [],
}
];
d.AudioType = streamDefinitions[index]['AudioType'];
d.VideoType = streamDefinitions[index]['VideoType'];
var CreateAudioDef = function(src, size, duration, customMap) {
return {name: 'audio', type: d.AudioType, size: size, src: src,
duration: duration, bps: Math.floor(size / duration),
customMap: customMap};
};
var CreateVideoDef = function(src, size, duration, customMap) {
return {name: 'video', type: d.VideoType, size: size, src: src,
duration: duration, bps: Math.floor(size / duration),
customMap: customMap};
};
d.AudioTiny = CreateAudioDef.apply(this, streamDefinitions[index]['AudioTiny']);
d.AudioNormal = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormal']);
d.AudioNormalAdv = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormalAdv']);
d.AudioHuge = CreateAudioDef.apply(this,streamDefinitions[index]['AudioHuge']);
d.VideoTiny = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTiny']);
d.VideoNormal = CreateVideoDef.apply(this, streamDefinitions[index]['VideoNormal']);
d.VideoHuge = CreateVideoDef.apply(this, streamDefinitions[index]['VideoHuge']);
d.AudioTinyClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioTinyClearKey']);
d.AudioNormalClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormalClearKey']);
d.AudioHugeClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioHugeClearKey']);
d.VideoTinyClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTinyClearKey']);
d.VideoNormalClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoNormalClearKey']);
d.VideoHugeClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoHugeClearKey']);
d.VideoStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoStreamYTCenc']);
d.VideoTinyStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTinyStreamYTCenc']);
d.VideoSmallStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoSmallStreamYTCenc']);
d.Audio1MB = CreateAudioDef.apply(this, streamDefinitions[index]['Audio1MB']);
d.Video1MB = CreateVideoDef.apply(this, streamDefinitions[index]['Video1MB']);
d.ProgressiveLow = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveLow']);
d.ProgressiveNormal = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveNormal']);
d.ProgressiveHigh = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveHigh']);
d.isWebM = function() {
return index === 1;
}
return d;
}
var StreamDef = getStreamDef();
function UpdateStreamDef(index) {
StreamDef = getStreamDef(index);
}
// streamDef-20150612143746.js end
// focusManager-20150612143746.js begin
(function() {
var INFINITY = 100000;
var CLOSE = 50;
var MAX_FUDGE = INFINITY;
var DIRECTION_WEIGHT = 0.5;
var LEFT = new Pair(-1, 0);
var UP = new Pair(0, -1);
var RIGHT = new Pair(1, 0);
var DOWN = new Pair(0, 1);
function Pair(x, y) {
this.x = x;
this.y = y;
this.add = function(operand) {
return new Pair(this.x + operand.x, this.y + operand.y);
};
this.sub = function(operand) {
return new Pair(this.x - operand.x, this.y - operand.y);
};
this.dot = function(operand) {
return this.x * operand.x + this.y * operand.y;
};
this.dotRelative = function(ref, operand) {
return this.x * (operand.x - ref.x) + this.y * (operand.y - ref.y);
};
this.distTo = function(operand) {
return Math.sqrt(this.x * operand.x + this.y * operand.y);
};
this.distTo2 = function(operand) {
return this.x * operand.x + this.y * operand.y;
};
this.cross = function(operand) {
return this.x * operand.y - this.y * operand.x;
};
}
function Rect(left, top, width, height) {
this.left = left;
this.top = top;
this.width = width;
this.height = height;
this.right = this.left + this.width;
this.bottom = this.top + this.height;
var rangeDist = function(start, end, startRef, endRef) {
if (start < startRef) {
if (end < startRef)
return startRef - end;
return 0;
}
if (start <= endRef)
return 0;
return start - endRef;
};
this.valid = function() {
return this.width !== 0 && this.height !== 0;
};
this.inside = function(x, y) {
// Technically speaking, this is not correct. However, this works out for
// our usage.
return x >= this.left && x < this.left + this.width &&
y >= this.top && y < this.top + this.height;
};
this.intersect = function(that) {
return this.inside(that.left, that.top) ||
this.inside(that.right, that.top) ||
this.inside(that.left, that.bottom) ||
this.inside(that.right, that.bottom) ||
that.inside(this.left, this.top) ||
that.inside(this.right, this.top) ||
that.inside(this.left, this.bottom) ||
that.inside(this.right, this.bottom);
};
this.intersectComplete = function(that) {
var centerXThat = (that.left + that.right) * 0.5;
var centerYThat = (that.top + that.bottom) * 0.5;
var halfThatWidth = that.width * 0.5;
var halfThatHeight = that.height * 0.5;
var expandedRect = new Rect(
this.left - halfThatWidth, this.top - halfThatHeight,
this.width + that.width, this.height + that.height);
return expandedRect.inside(centerXThat, centerYThat);
};
this.distanceSquared = function(ref, dir) {
var x, y;
if (dir.x === -1) {
x = Math.max((ref.left - this.right) * DIRECTION_WEIGHT, 0);
y = rangeDist(this.top, this.bottom, ref.top, ref.bottom);
} else if (dir.x === 1) {
x = Math.max((this.left - ref.right) * DIRECTION_WEIGHT, 0);
y = rangeDist(this.top, this.bottom, ref.top, ref.bottom);
} else if (dir.y === -1) {
x = rangeDist(this.left, this.right, ref.left, ref.right);
y = Math.max((ref.top - this.bottom) * DIRECTION_WEIGHT, 0);
} else {
x = rangeDist(this.left, this.right, ref.left, ref.right);
y = Math.max((this.top - ref.bottom) * DIRECTION_WEIGHT, 0);
}
return x * x + y * y;
};
this.generateSideSliver = function(dir) {
var left, right, top, bottom;
if (dir === LEFT) {
left = this.left - CLOSE;
right = this.left - 1;
top = (this.top + this.bottom) * 0.5;
bottom = top;
} else if (dir === RIGHT) {
left = this.right + 1;
right = this.right + CLOSE;
top = (this.top + this.bottom) * 0.5;
bottom = top;
} else if (dir === UP) {
left = (this.left + this.right) * 0.5;
right = (this.left + this.right) * 0.5;
top = this.top - CLOSE;
bottom = this.top - 1;
} else {
left = (this.left + this.right) * 0.5;
right = (this.left + this.right) * 0.5;
top = this.bottom + 1;
bottom = this.bottom + CLOSE;
}
return new Rect(left, top, right - left, bottom - top);
};
// Generates a rectangle to check if there are any other rectangles strictly
// to one side (defined by dir) of 'this'.
this.generateSideRect = function(dir, fudge) {
if (!fudge) {
return this.generateSideSliver(dir);
}
var left, right, top, bottom;
if (dir === LEFT) {
left = -INFINITY;
right = this.left - 1;
top = this.top - fudge;
bottom = this.bottom + fudge;
} else if (dir === RIGHT) {
left = this.right + 1;
right = INFINITY;
top = this.top - fudge;
bottom = this.bottom + fudge;
} else if (dir === UP) {
left = this.left - fudge;
right = this.right + fudge;
top = -INFINITY;
bottom = this.top - 1;
} else {
left = this.left - fudge;
right = this.right + fudge;
top = this.bottom + 1;
bottom = INFINITY;
}
return new Rect(left, top, right - left, bottom - top);
};
this.toSideOf = function(target, dir) {
var testX = [0, this.right - this.center.x, this.left - this.center.x];
var testY = [0, this.bottom - this.center.y, this.top - this.center.y];
var testLineSegRel0 = new Pair(
testX[dir.x - (dir.x != 0)], testY[dir.y - (dir.y != 0)]);
var testLineSegRel1 = new Pair(
testY[dir.x + (dir.x != 0)], testY[dir.y + (dir.y != 0)]);
return dir.cross(testLineSegRel0) * dir.cross(testLineSegRel1) <= 0 &&
this.intersect(target);
};
this.toString = function() {
return '(' + this.left + ', ' + this.top + ', ' + this.right + ', ' +
this.bottom + ')';
};
};
function createRect(element) {
var offsetLeft = element.offsetLeft;
var offsetTop = element.offsetTop;
var e = element.offsetParent;
while (e && e !== document.body) {
offsetLeft += e.offsetLeft;
offsetTop += e.offsetTop;
e = e.offsetParent;
}
return new Rect(offsetLeft, offsetTop,
element.offsetWidth, element.offsetHeight);
};
function FocusManager() {
var elements = [];
var handlers = [];
var pickElement_ = function(currElem, dir, fudge) {
var rect = createRect(currElem);
var rectSide = rect.generateSideRect(dir, fudge);
var bestDistanceSquared = INFINITY * INFINITY;
var bestElement = null;
for (var i = 0; i < elements.length; ++i) {
if (elements[i] !== currElem) {
var r = createRect(elements[i]);
if (r.valid() && r.intersectComplete(rectSide)) {
var distanceSquared = r.distanceSquared(rect, dir);
if (!bestElement || distanceSquared < bestDistanceSquared) {
bestElement = elements[i];
bestDistanceSquared = distanceSquared;
}
}
}
}
return bestElement;
};
var pickElement = function(currElem, dir) {
return pickElement_(currElem, dir) ||
pickElement_(currElem, dir, 2) ||
pickElement_(currElem, dir, MAX_FUDGE);
};
var onkeydown = function(e) {
if (elements.indexOf(e.target) !== -1) {
var dir;
if (e.keyCode === 37) { // left
dir = LEFT;
} else if (e.keyCode === 38) { // up
dir = UP;
} else if (e.keyCode === 39) { // right
dir = RIGHT;
} else if (e.keyCode === 40) { // down
dir = DOWN;
} else {
return true;
}
var element = pickElement(e.target, dir);
if (element) {
element.focus();
e.stopPropagation();
e.preventDefault();
}
}
return true;
};
this.add = function(element) {
if (elements.indexOf(element) === -1) {
elements.push(element);
handlers.push(element.addEventListener('keydown', onkeydown));
}
};
};
window.addEventListener('load', function() {
var focusManager = new FocusManager;
var elements = document.getElementsByClassName('focusable');
for (var i = 0; i < elements.length; ++i)
focusManager.add(elements[i]);
/*var links = document.getElementsByTagName('A');
for (var i = 0; i < links.length; ++i)
focusManager.add(links[i]);*/
});
})();
// focusManager-20150612143746.js end
// logger-20150612143746.js begin
(function() {
var Logger = function(log) {
this.throwError = true;
this.log = log;
this.assert = function(cond, msg) {
if (!cond) {
this.log('Assert failed: ' + msg);
try {
var x = y.z.u.v.w;
} catch (e) {
this.log(e.stack);
}
if (this.throwError) throw 'Assert: ' + msg;
}
};
this.check = function(condition, passMsg, failMsg) {
if (condition)
this.log(passMsg);
else
this.assert(false, failMsg);
};
this.checkEq = function(x, y, name) {
var result = (x == y) ||
(typeof(x) === 'number' && typeof(y) === 'number' &&
isNaN(x) && isNaN(y));
this.check(result, 'checkEq passed: ' + name + ' is (' + x + ').',
name + ' is (' + x + ') which should be (' + y + ')');
};
this.checkNE = function(x, y, name) {
var result = (x != y) &&
!((typeof(x) === 'number' && typeof(y) === 'number' &&
isNaN(x) && isNaN(y)));
this.check(result, 'checkNE passed: ' + name + ' is (' + x + ').',
name + ' is (' + x + ') which shouldn\'t.');
};
};
window.createLogger = function(log) {
return new Logger(log || console.log.bind(console));
};
})();
// logger-20150612143746.js end
// js/harness/xhr-20150612143746.js begin
(function() {
var BYPASS_CACHE = false;
// Hook the onload event for request that is finished successfully
var Request = function(manager, logger, file, onload, postLength,
start, length) {
var self = this;
this.open = function() {
this.xhr = new XMLHttpRequest();
this.onload = onload;
this.type = util.isValidArgument(postLength) ? 'POST' : 'GET';
this.xhr.open(this.type,
file + (BYPASS_CACHE ? '?' + (new Date()).getTime() : ''));
this.xhr.responseType = 'arraybuffer';
this.startTime = new Date().getTime();
this.lastUpdate = this.startTime;
if (start != null && length != null)
this.xhr.setRequestHeader(
'Range', 'bytes=' + start + '-' + (start + length - 1));
this.xhr.addEventListener('error', function(e) {
if (self.xhr.status === 404)
Alert('Failed to find "' + file +
'" with error 404. Is it on the server?');
manager.requestFinished(self);
logger.log('XHR error with code', self.xhr.status);
self.open();
self.send();
});
this.xhr.addEventListener('timeout', function(e) {
manager.requestFinished(self);
logger.log('XHR timeout');
self.open();
self.send();
});
this.xhr.addEventListener('load', function(e) {
manager.requestFinished(self);
return self.onload(e);
});
this.xhr.addEventListener('progress', function onProgress(e) {
if (e.lengthComputable && (e.loaded === e.total)) {
self.xhr.removeEventListener('progress', onProgress);
}
self.lastUpdate = new Date().getTime();
});
};
this.getRawResponse = function() {
if (this.xhr.status === 404)
Alert('Failed to find "' + file +
'" with error 404. Is it on the server?');
logger.assert(this.xhr.status >= 200 && this.xhr.status < 300,
'XHR bad status: ' + this.xhr.status);
return this.xhr.response;
};
this.getResponseData = function() {
if (this.xhr.status === 404)
Alert('Failed to find "' + file +
'" with error 404. Is it on the server?');
logger.assert(this.xhr.status >= 200 && this.xhr.status < 300,
'XHR bad status: ' + this.xhr.status);
var result = new Uint8Array(this.xhr.response);
if (length != null) {
var rangeHeader = this.xhr.getResponseHeader('Content-Range');
var lengthHeader = this.xhr.getResponseHeader('Content-Length');
if (!rangeHeader && lengthHeader) {
logger.assert(length <= lengthHeader,
'Length of response is smaller than request');
result = result.subarray(start, start + length);
logger.checkEq(result.length, length, 'XHR length');
return result;
}
logger.checkEq(result.length, length, 'XHR length');
}
return result;
};
this.send = function(postData) {
manager.addRequest(this);
if (postData) {
logger.checkEq(this.type, 'POST', 'XHR requestType');
this.xhr.send(postData);
} else {
logger.checkEq(this.type, 'GET', 'XHR requestType');
this.xhr.send();
}
};
this.abort = function() {
this.xhr.abort();
};
this.open();
};
var XHRManager = function(logger) {
var requests = [];
this.totalRequestDuration = 0;
this.addRequest = function(request) {
logger.checkEq(requests.indexOf(request), -1, 'request index');
requests.push(request);
};
this.requestFinished = function(request) {
var currentTime = new Date().getTime();
this.totalRequestDuration += currentTime - request.startTime;
logger.checkNE(requests.indexOf(request), -1, 'request index');
requests.splice(requests.indexOf(request), 1);
};
this.abortAll = function() {
for (var i = 0; i < requests.length; ++i)
requests[i].abort();
requests = [];
};
this.createRequest = function(file, onload, start, length) {
return new Request(this, logger, file, onload, null, start, length);
};
this.createPostRequest = function(file, onload, postLength, start, length) {
return new Request(this, logger, file, onload, postLength, start, length);
};
this.hasActiveRequests = function() {
if (requests.length > 0) {
return true;
}
return false;
}
this.getLastUpdate = function() {
if (requests.length == 0) {
return null;
}
var latestUpdate = 0;
for (var i in requests) {
latestUpdate = Math.max(requests[i].lastUpdate, latestUpdate);
}
return latestUpdate;
};
};
window.createXHRManager = function(logger) {
return new XHRManager(logger);
};
})();
// js/harness/xhr-20150612143746.js end
// js/harness/timeout-20150612143746.js begin
(function() {
var TimeoutManager = function(logger) {
var timers = [];
var intervals = [];
var getUniqueItem = function(container) {
var id = 0;
while (typeof(container[id]) != 'undefined')
++id;
container[id] = {id: id};
return container[id];
};
var timeoutHandler = function(id) {
if (timers[id]) {
var func = timers[id].func;
delete timers[id];
func();
}
};
var intervalHandler = function(id) {
var func = intervals[id].func;
func();
};
this.setTimeout = function(func, timeout) {
var timer = getUniqueItem(timers);
timer.func = func;
var id = window.setTimeout(timeoutHandler.bind(this, timer.id), timeout);
};
this.setInterval = function(func, timeout) {
var interval = getUniqueItem(intervals);
interval.func = func;
interval.id = window.setInterval(intervalHandler, timeout, interval.id);
};
this.clearAll = function() {
for (var id = 0; id < timers.length; ++id)
if (typeof(timers[id]) != 'undefined')
window.clearTimeout(timers[id].id);
timers = [];
for (var id = 0; id < intervals.length; ++id)
if (typeof(intervals[id]) != 'undefined')
window.clearInterval(intervals[id].id);
intervals = [];
};
};
window.createTimeoutManager = function(logger) {
return new TimeoutManager(logger);
};
})();
// js/harness/timeout-20150612143746.js end
// js/harness/testView-20150612143746.js begin
var TestView = (function() {
var createElement = util.createElement;
var createAnchor = function(text, id) {
return util.createElement('span', id, 'rightmargin20', text);
};
var createOption = function(text, value) {
var option = document.createElement('option');
option.text = text;
option.value = value;
return option;
};
function TestView(mseSpec) {
this.mseSpec = mseSpec;
this.testList = null;
var selectors = [];
var switches = [];
var commands = [];
var testSuites = [];
var links = [];
this.addSelector = function(text, optionTexts, values, callback) {
optionTexts = optionTexts instanceof Array ? optionTexts : [optionTexts];
values = values instanceof Array ? values : [values];
if (optionTexts.length !== values.length)
throw "text length and value length don't match!";
selectors.push({
'text': text,
'optionTexts': optionTexts,
'values': values,
'cb': callback
})
};
this.addSwitch = function(text, id) {
switches.push({text: text, id: id});
};
this.addCommand = function(text, id, title, onclick) {
commands.push({text: text, id: id, title: title, onclick: onclick});
};
this.addTestSuite = function(text, href) {
testSuites.push({text: text, href: href});
};
this.addTestSuites = function(testTypes) {
var isTestTypeSupported = function(testType) {
var supported = testTypes[testType].supported;
if (typeof supported === 'string' && supported == 'all') {
return true;
} else if (typeof supported === 'object') {
for (var index in supported) {
if (supported[index] == mseSpec) {
return true;
}
}
}
return false;
};
for (var testType in testTypes) {
if (testType !== currentTestType && isTestTypeSupported(testType)) {
this.addTestSuite(testTypes[testType].name, '?test_type=' + testType);
}
}
}
this.addLink = function(text, href) {
links.push({text: text, href: href});
};
this.generate = function() {
var heading = '[' + this.mseSpec + '] ' +
testTypes[currentTestType].heading + ' (v 20150612143746-4K5xqupUzgiRyTYP)';
document.title = testTypes[currentTestType].title;
document.body.appendChild(createElement('div', 'title', null, heading));
document.body.appendChild(createElement('div', 'info'));
document.body.appendChild(createElement('div', 'usage'));
document.body.appendChild(createElement('div', 'testview'));
var div = document.getElementById(this.divId);
div.innerHTML = '';
div.appendChild(createElement('div', 'testsuites', 'container'));
div.appendChild(createElement('div', 'switches', 'container'));
div.appendChild(createElement('div', 'controls', 'container'));
var testContainer = createElement('div', null, 'container');
testContainer.appendChild(createElement('div', 'testlist', 'box-left'));
testContainer.appendChild(createElement('div', 'testarea'));
div.appendChild(testContainer);
var outputArea = createElement('div', 'outputarea');
var textArea = createElement('div', 'output');
textArea.rows = 10;
textArea.cols = 80;
outputArea.appendChild(textArea);
div.appendChild(outputArea);
var switchDiv = document.getElementById('switches');
for (var i = 0; i < switches.length; ++i) {
var id = switches[i].id;
switchDiv.appendChild(document.createTextNode(switches[i].text));
switchDiv.appendChild(createAnchor(window[id] ? 'on' : 'off', id));
switchDiv.lastChild.href = 'javascript:;';
switchDiv.lastChild.onclick = (function(id) {
return function(e) {
var wasOff = e.target.innerHTML === 'off';
e.target.innerHTML = wasOff ? 'on' : 'off';
window[id] = wasOff;
};
})(id);
}
for (var i = 0; i < selectors.length; ++i) {
switchDiv.appendChild(document.createTextNode(selectors[i].text));
var select = document.createElement('select');
for (var j = 0; j < selectors[i].optionTexts.length; ++j) {
select.appendChild(createOption(selectors[i].optionTexts[j],
selectors[i].values[j]));
}
select.onchange = selectors[i].cb;
switchDiv.appendChild(select);
}
switchDiv.appendChild(
createElement('span', 'finish-count', null, '0 tests finished'));
var controlsDiv = document.getElementById('controls');
for (var i = 0; i < commands.length; ++i) {
controlsDiv.appendChild(createAnchor(commands[i].text, commands[i].id));
controlsDiv.lastChild.href = 'javascript:;';
controlsDiv.lastChild.onclick = commands[i].onclick;
controlsDiv.lastChild.title = commands[i].title;
}
for (var i = 0; i < links.length; ++i) {
controlsDiv.appendChild(createAnchor(links[i].text));
controlsDiv.lastChild.href = links[i].href;
}
var testSuitesDiv = document.getElementById('testsuites');
for (var i = 0; i < testSuites.length; ++i) {
testSuitesDiv.appendChild(createAnchor(testSuites[i].text));
testSuitesDiv.lastChild.href = testSuites[i].href;
}
this.testList.generate(document.getElementById('testlist'));
};
this.addTest = function(desc) {
return this.testList.addTest(desc);
};
this.anySelected = function() {
return this.testList.anySelected();
};
};
return {
create: function(mseSpec) {
return new TestView(mseSpec);
}};
})();
// js/harness/testView-20150612143746.js end
// fullTestList-20150612143746.js begin
(function() {
var createElement = util.createElement;
function Test(desc, fields) {
var INDEX = 0;
var STATUS = INDEX + 1;
var DESC = STATUS + 1;
var FIELD = DESC + 1;
this.index = desc.index;
this.id = 'test-row' + this.index;
this.desc = desc;
this.steps = [];
this.createElement = function(element) {
element.id = this.id;
element.appendChild(createElement('td', null, 'index',
this.index + 1 + '.'));
element.appendChild(
createElement('td', null, 'status',
'<input type="checkbox" checked="yes"></input> >'));
element.appendChild(createElement('td', null, 'desc'));
for (var field = 0; field < fields.length; ++field)
element.appendChild(createElement('td', null, 'state', 0));
var link = createElement('span', null, null, desc.desc);
link.href = 'javascript:;';
link.onclick = desc.onclick;
link.title = desc.title;
element.childNodes[DESC].appendChild(link);
var explanationPoint = createElement('span', null, 'desc-expl-point', '?');
var explanation = createElement(
'span', null, 'desc-expl-popup', desc.title);
explanationPoint.appendChild(explanation);
element.childNodes[DESC].appendChild(explanationPoint);
};
this.addStep = function(name) {
var tr = createElement('tr');
tr.appendChild(createElement('td', null, 'small'));
tr.appendChild(createElement('td', null, 'small'));
tr.appendChild(createElement('td', null, 'small',
this.steps.length + 1 + '. ' + name));
for (var field = 0; field < fields.length; ++field)
tr.appendChild(createElement('td', null, 'small', 0));
var element = document.getElementById(this.id);
if (this.steps.length !== 0)
element = this.steps[this.steps.length - 1];
if (element.nextSibling)
element.parentNode.insertBefore(tr, element.nextSibling);
else
element.parentNode.appendChild(tr);
this.steps.push(tr);
};
this.updateStatus = function() {
var element = document.getElementById(this.id);
element.childNodes[STATUS].className =
this.desc.running ? 'status_current' : 'status';
for (var field = 0; field < fields.length; ++field)
element.childNodes[FIELD + field].innerHTML =
this.desc[fields[field].replace(' ', '_')];
};
this.selected = function() {
var element = document.getElementById(this.id);
return element.childNodes[STATUS].childNodes[0].checked;
};
this.select = function() {
var element = document.getElementById(this.id);
element.childNodes[STATUS].childNodes[0].checked = true;
};
this.deselect = function() {
var element = document.getElementById(this.id);
element.childNodes[STATUS].childNodes[0].checked = false;
};
}
function TestList(fields) {
var tableId = 'test-list-table';
var headId = tableId + '-head';
var bodyId = tableId + '-body';
var tests = [];
if (!fields || !fields.length)
throw 'No test fields';
this.addColumnHeader = function(class_, text) {
var head = document.getElementById(headId);
var th = createElement('th', null, class_, text);
th.scope = 'col';
head.appendChild(th);
};
this.addTest = function(desc) {
var test = new Test(desc, fields);
tests.push(test);
return test;
};
this.generate = function(div) {
var table = document.createElement('table');
table.id = tableId;
div.appendChild(table);
var thead = createElement('thead');
table.classList.add('test-table');
table.innerHTML = '';
var head = createElement('tr');
var body = createElement('tbody');
head.id = headId;
body.id = bodyId;
thead.appendChild(head);
table.appendChild(thead);
table.appendChild(body);
this.addColumnHeader('index');
this.addColumnHeader('status');
this.addColumnHeader('desc', 'Test');
for (var i = 0; i < fields.length; ++i)
this.addColumnHeader('state', util.MakeCapitalName(fields[i]));
for (var i = 0; i < tests.length; ++i) {
var tr = createElement('tr');
body.appendChild(tr);
tests[i].createElement(tr);
tests[i].updateStatus();
}
};
this.getTest = function(index) {
return tests[index];
};
this.anySelected = function() {
for (var i = 0; i < tests.length; ++i)
if (tests[i].selected())
return true;
return false;
};
this.selectAll = function() {
for (var i = 0; i < tests.length; ++i)
tests[i].select();
};
this.deselectAll = function() {
for (var i = 0; i < tests.length; ++i)
tests[i].deselect();
};
};
window.createFullTestList = function(fields) {
return new TestList(fields);
};
})();
// fullTestList-20150612143746.js end
// js/harness/fullTestView-20150612143746.js begin
var fullTestView = (function() {
function FullTestView(fields) {
var self = this;
this.divId = 'testview';
this.testCount = 0;
this.initialize = function() {
this.testList = createFullTestList(fields);
this.addSwitch('Loop: ', 'loop');
this.addSwitch('Stop on failure: ', 'stoponfailure');
this.addSwitch('Log: ', 'logging');
this.addSwitch('WebM/VP9 (2015/tip only): ', 'enablewebm');
this.addCommand('Select All', 'select-all', 'Select all tests.',
this.testList.selectAll.bind(this.testList));
this.addCommand('Deselect All', 'deselect-all', 'Deselect all tests.',
this.testList.deselectAll.bind(this.testList));
this.addCommand('Run Selected', 'run-selected',
'Run all selected tests in order.',
function(e) {
if (self.onrunselected)
self.onrunselected.call(self, e);
});
this.addLink('Links', 'links.html');
this.addLink('Instructions', 'instructions.html');
this.addLink('Changelog', 'main.html');
this.addLink('Download', 'download-20150612143746.tar.gz');
this.addTestSuites(testTypes);
};
this.addTest = function(desc) {
return this.testList.addTest(desc);
};
this.generate = function() {
FullTestView.prototype.generate.call(this);
// document.getElementById('run-selected').focus();
};
this.getTest = function(index) {
return this.testList.getTest(index);
};
this.finishedOneTest = function() {
++this.testCount;
document.getElementById('finish-count').innerHTML =
this.testCount === 1 ? this.testCount + ' test finished' :
this.testCount + ' tests finished';
};
this.anySelected = function() {
return this.testList.anySelected();
};
this.initialize();
};
//FullTestView.prototype = TestView.create();
//FullTestView.prototype.constructor = FullTestView;
return {
create: function(mseSpec, fields) {
FullTestView.prototype = TestView.create(mseSpec);
FullTestView.prototype.constructor = FullTestView;
return new FullTestView(fields);
}
};
})();
// js/harness/fullTestView-20150612143746.js end
// js/harness/compactTestList-20150612143746.js begin
(function() {
var ITEM_IN_COLUMN = 25; // Test item count in a column
var CATEGORY_SPACE = 1; // Row between the end of the last category and the
// beginning of the next category
var MIN_ROW_AT_THE_BOTTOM = 2; // If at the bottom of the table and the row
// count is less than this, start a new column.
var createElement = util.createElement;
function Category(categoryName) {
this.setElement = function(nameCell, statusCell) {
nameCell.className = 'cell-category';
nameCell.innerText = categoryName;
};
this.setDoubleElement = function(cellElem) {
cellElem.className = 'cell-category';
cellElem.setAttribute('colspan', 2);
cellElem.innerText = categoryName;
};
}
function Test(desc, style) {
var self = this;
this.index = desc.index;
this.nameId = 'test-item-name-' + this.index;
this.statusId = 'test-item-status-' + this.index;
this.desc = desc;
this.steps = [];
this.style = style;
this.createElement = function(name, status) {
name.id = this.nameId;
status.id = this.statusId;
var link = createElement('span', null, null,
this.index + 1 + '. ' + this.desc.desc);
link.href = 'javascript:;';
link.onclick = desc.onclick;
link.title = desc.title;
name.appendChild(link);
this.updateStatus(status);
};
this.updateStatus = function(status) {
var text = this.desc.status;
var failureStatus = '';
if (text && text.length > 5) text = '';
status = status ? status : document.getElementById(this.statusId);
if (this.style === 'extra compact') {
failureStatus = this.desc.mandatory ? 'test-status-fail' :
'test-status-optional-fail';
if (this.desc.running) {
status.className = 'test-status-running';
} else if (this.desc.failures) {
status.className = failureStatus;
} else if (this.desc.timeouts) {
status.className = failureStatus;
} else if (this.desc.passes) {
status.className = 'test-status-pass';
} else {
status.className = 'test-status-none';
}
} else {
failureStatus = this.desc.mandatory ? 'cell-status-fail' :
'cell-status-normal';
if (this.desc.running) {
status.innerHTML = '&nbsp;...&nbsp;';
status.className = 'cell-status-running';
} else if (this.desc.failures) {
status.innerHTML = text || '&nbsp;Fail&nbsp;';
status.className = failureStatus;
} else if (this.desc.timeouts) {
status.innerHTML = text || '&nbsp;Fail&nbsp;';
status.className = failureStatus;
} else if (this.desc.passes) {
status.innerHTML = text || '&nbsp;Pass&nbsp;';
status.className = 'cell-status-pass';
} else {
status.innerHTML = ' ';
status.className = 'cell-status-normal';
}
}
};
this.selected = function() {
return true;
};
this.getElement = function() {
return document.getElementById(this.nameId).childNodes[0];
};
}
function TestList(style) {
var self = this;
var tests = [];
var SINGLE_WIDTH_CELL = 1;
var DOUBLE_WIDTH_CELL = 2;
this.style = style || '';
// returns array [row, column]
var getTableDimension = function() {
var lastCategory = '';
var cells = 0;
var rowLeft;
for (var i = 0; i < tests.length; ++i) {
if (lastCategory !== tests[i].desc.category) {
rowLeft = ITEM_IN_COLUMN - cells % ITEM_IN_COLUMN;
if (rowLeft < MIN_ROW_AT_THE_BOTTOM)
cells += rowLeft;
if (cells % ITEM_IN_COLUMN !== 0)
cells += CATEGORY_SPACE;
cells++;
lastCategory = tests[i].desc.category;
} else if (cells % ITEM_IN_COLUMN === 0) {
cells++; // category (continued)
}
cells++;
}
return [Math.min(cells, ITEM_IN_COLUMN),
Math.floor((cells + ITEM_IN_COLUMN - 1) / ITEM_IN_COLUMN)];
};
var createExtraCompactTable = function(div, table) {
var lastCategory = null;
var totalCells = 0;
var totalTests = 0;
var rowsRemaining = 0;
var layoutColumnSpan = [];
var rows = [];
var j = 0;
var createEmptyCells = function(row) {
if (table.childNodes.length <= row) {
table.appendChild(createElement('tr'));
}
var tr = table.childNodes[row];
var elems = [
createElement('td', null, 'test-status-none'),
createElement('td', null, 'cell-name', '&nbsp;')
];
tr.appendChild(elems[0]);
tr.appendChild(elems[1]);
return elems;
};
var createTestCells = function(testIndex, row, test) {
var cells = createEmptyCells(row);
tests[testIndex].createElement(cells[1], cells[0]);
};
var createCategoryCell = function(row, categoryName) {
if (table.childNodes.length <= row) {
table.appendChild(createElement('tr'));
}
var tr = table.childNodes[row];
var elem = createElement('td', null, 'cell-name', '&nbsp;');
tr.appendChild(elem);
(new Category(categoryName)).setDoubleElement(elem);
};
for (var i = 0; i < tests.length; ++i) {
var currCategory = tests[i].desc.category;
if (lastCategory !== currCategory) {
rowsRemaining = ITEM_IN_COLUMN - totalCells % ITEM_IN_COLUMN;
if (rowsRemaining < MIN_ROW_AT_THE_BOTTOM) {
// Add a row for heading.
for (j = 0; j < rowsRemaining; ++j) {
createEmptyCells(totalCells % ITEM_IN_COLUMN);
totalCells += 1;
}
}
if (totalCells % ITEM_IN_COLUMN !== 0) {
// Add a row for extra space before heading, if in middle of column.
for (j = 0; j < CATEGORY_SPACE; ++j) {
createEmptyCells(totalCells % ITEM_IN_COLUMN);
totalCells += 1;
}
}
lastCategory = currCategory;
createCategoryCell(totalCells % ITEM_IN_COLUMN, lastCategory);
totalCells++;
} else if (totalCells % ITEM_IN_COLUMN === 0) {
// category (continued)
createCategoryCell(totalCells % ITEM_IN_COLUMN, lastCategory);
totalCells++;
}
createTestCells(totalTests, totalCells % ITEM_IN_COLUMN, lastCategory);
totalCells++;
totalTests++;
}
div.innerHTML = '';
div.appendChild(table);
};
this.addTest = function(desc) {
var test = new Test(desc, this.style);
tests.push(test);
return test;
};
this.generate = function(div) {
var table = createElement('div', null, 'compact-list');
var tr;
var dim = getTableDimension();
var lastCategory = '';
var row;
var column;
if (self.style === 'extra compact') {
createExtraCompactTable(div, table);
} else {
for (row = 0; row < dim[0]; ++row) {
tr = createElement('div');
table.appendChild(tr);
for (column = 0; column < dim[1]; ++column) {
tr.appendChild(createElement('span', null, 'cell-name', '&nbsp;'));
tr.appendChild(createElement('span', null, 'cell-divider'));
tr.appendChild(createElement('span', null, 'cell-status-normal'));
}
}
div.innerHTML = '';
div.appendChild(table);
row = column = 0;
for (var i = 0; i < tests.length; ++i) {
if (lastCategory !== tests[i].desc.category) {
if (ITEM_IN_COLUMN - row <= MIN_ROW_AT_THE_BOTTOM) {
row = 0;
column++;
}
if (row % ITEM_IN_COLUMN !== 0)
row += CATEGORY_SPACE;
lastCategory = tests[i].desc.category;
(new Category(lastCategory)).setElement(
table.childNodes[row].childNodes[column * 3],
table.childNodes[row].childNodes[column * 3 + 2]);
row++;
} else if (row === 0) {
(new Category(lastCategory)).setElement(
table.childNodes[row].childNodes[column * 3],
table.childNodes[row].childNodes[column * 3 + 2]);
row++;
}
tests[i].createElement(
table.childNodes[row].childNodes[column * 3],
table.childNodes[row].childNodes[column * 3 + 2]);
row++;
if (row === ITEM_IN_COLUMN) {
row = 0;
column++;
}
}
}
};
this.getTest = function(index) {
return tests[index];
};
this.anySelected = function() {
return tests.length !== 0;
};
};
window.createCompactTestList = function(style) {
return new TestList(style);
};
})();
// js/harness/compactTestList-20150612143746.js end
// js/harness/compactTestView-20150612143746.js begin
var compactTestView = (function() {
function CompactTestView(fields, style) {
var self = this;
this.divId = 'testview';
this.testCount = 0;
this.initialize = function() {
this.testList = createCompactTestList(style);
this.addSwitch('Loop: ', 'loop');
this.addSwitch('Stop on failure: ', 'stoponfailure');
this.addSwitch('Log: ', 'logging');
this.addSwitch('WebM/VP9 (2015/tip only): ', 'enablewebm');
this.addCommand('Run All', 'run-selected', 'Run all tests in order.',
function(e) {
if (self.onrunselected)
self.onrunselected.call(self, e);
});
this.addLink('Links', 'links.html');
this.addLink('Instructions', 'instructions.html');
this.addLink('Changelog', 'main.html');
this.addLink('Download', 'download-20150612143746.tar.gz');
this.addTestSuites(testTypes);
};
this.addTest = function(desc) {
return this.testList.addTest(desc);
};
this.generate = function() {
CompactTestView.prototype.generate.call(this);
// document.getElementById('run-selected').focus();
var USAGE = 'Use &uarr;&darr;&rarr;&larr; to move around, ' +
'use ENTER to select.';
document.getElementById('usage').innerHTML = USAGE;
// document.getElementById('run-selected').focus();
};
this.getTest = function(index) {
return this.testList.getTest(index);
};
this.finishedOneTest = function() {
++this.testCount;
document.getElementById('finish-count').innerHTML =
this.testCount === 1 ? this.testCount + ' test finished' :
this.testCount + ' tests finished';
};
this.anySelected = function() {
return this.testList.anySelected();
};
this.initialize();
};
CompactTestView.prototype = TestView.create();
CompactTestView.prototype.constructor = CompactTestView;
return {
create: function(mseSpec, fields, style) {
CompactTestView.prototype = TestView.create(mseSpec);
CompactTestView.prototype.constructor = CompactTestView;
return new CompactTestView(fields, style);
}
};
})();
// js/harness/compactTestView-20150612143746.js end
// js/lib/mse/2013/mediaSourcePortability-20150612143746.js begin
/*
* without Webkit prefix and MediaSource ver 0.5 with or without Webkit prefix.
*/
function setupMsePortability() {
var dlog = function() {
var forward = window.dlog || console.log.bind(console);
forward.apply(this, arguments);
};
// Check if we have MSE 0.6 WITHOUT webkit prefix
if (window.MediaSource) {
window.MediaSource.prototype.version = 'MSE-live';
return;
}
// Check if we have MSE 0.6 WITH webkit prefix
if (window.WebKitMediaSource) {
window.MediaSource = window.WebKitMediaSource;
window.MediaSource.prototype.version = 'MSE-live-webkit';
var cou = window.URL.createObjectURL;
var creatingURL = false;
window.URL.createObjectURL = function(obj) {
if (!creatingURL) {
creatingURL = true;
var url = window.URL.createObjectURL(obj);
creatingURL = false;
return url;
}
return cou.call(this, obj);
};
var ael = window.MediaSource.prototype.addEventListener;
window.MediaSource.prototype.addEventListener = function(
type, listener, useCaptures) {
var re = /^source(open|close|ended)$/;
var match = re.exec(type);
if (match) {
ael.call(this, 'webkit' + type, listener, useCaptures);
} else {
ael.call(this, type, listener, useCaptures);
}
};
return;
}
var v = document.createElement('video');
// Do we have any forms of MSE 0.5?
// NOTE: We will only support MSE 0.5 with webkit prefix.
if (!v.webkitSourceAddId)
return;
function MediaSource() {
this.sourceBuffers = [];
this.activeSourceBuffers = this.sourceBuffers;
this.readyState = 'closed';
this.msWrapperVideo = null;
this.msWrapperDuration = NaN;
this.msWrapperSourceIdCount = 1;
this.msWrapperHandlers = {};
this.msWrapperAppended = false;
this.isWrapper = true;
}
MediaSource.prototype.version = 'MSE-v0.5-wrapped-webkit';
var missingFeature = 'Missing:';
if (!v.webkitSourceSetDuration)
missingFeature += ' webkitSourceSetDuration';
if (!v.webkitSourceTimestampOffset)
missingFeature += ' webkitSourceTimestampOffset';
if (missingFeature !== 'Missing:')
MediaSource.prototype.version += '(' + missingFeature + ')';
MediaSource.prototype.msWrapperHandler = function(name, evt) {
var handlers = this.msWrapperHandlers[name] || [];
dlog(4, 'In msWrapperHandler');
if (name === 'close') {
this.readyState = 'closed';
this.msWrapperDuration = NaN;
} else {
this.readyState = name;
}
for (var i = 0; i < handlers.length; i++) {
handlers[i].call(evt, evt);
}
};
MediaSource.prototype.attachTo = function(video) {
dlog(4, 'In msWrapperAttach');
var names = ['open', 'close', 'ended'];
for (var i = 0; i < names.length; i++) {
var h = this.msWrapperHandler.bind(this, names[i]);
video.addEventListener('webkitsource' + names[i], h);
}
this.msWrapperVideo = video;
var self = this;
video.addEventListener('durationchange', function() {
LOG(video.duration);
self.msWrapperDuration = video.duration;
});
video.src = video.webkitMediaSourceURL;
};
MediaSource.prototype.addSourceBuffer = function(type) {
if (!this.msWrapperVideo) throw 'Unattached';
var id = '' + this.msWrapperSourceIdCount;
this.msWrapperSourceIdCount += 1;
this.msWrapperVideo.webkitSourceAddId(id, type);
var buf = new SourceBuffer(this.msWrapperVideo, id);
this.sourceBuffers.push(buf);
return buf;
};
MediaSource.prototype.removeSourceBuffer = function(buf) {
for (var i = 0; i < this.sourceBuffers.length; ++i) {
if (buf === this.sourceBuffers[i]) {
this.msWrapperVideo.webkitSourceRemoveId(buf.msWrapperSourceId);
delete this.sourceBuffers.splice(i, 1)[0];
break;
}
}
};
MediaSource.prototype.endOfStream = function(opt_error) {
var v = this.msWrapperVideo;
// TODO: are these prefixed in M21?
var err;
if (opt_error === 'network') {
err = v.EOS_NETWORK_ERR;
} else if (opt_error === 'decode') {
err = v.EOS_DECODE_ERR;
} else if (!opt_error) {
err = v.EOS_NO_ERROR;
} else {
throw 'Unrecognized endOfStream error type: ' + opt_error;
}
v.webkitSourceEndOfStream(err);
};
// The 'setDuration' method of the media element is an extension to the
// MSE-v0.5 spec, which will be implemented on some devices.
// Calling this method is defined to have the same semantics as setting
// the duration property of the Media Source object in the current spec.
// Getting it is undefined, although clearly here we just return the last
// value that we set.
Object.defineProperty(MediaSource.prototype, 'duration', {
get: function() {
if (this.readyState === 'closed')
return NaN;
return this.msWrapperDuration;
},
set: function(duration) {
this.msWrapperDuration = duration;
if (this.msWrapperVideo.webkitSourceSetDuration) {
this.msWrapperVideo.webkitSourceSetDuration(duration);
} else {
dlog(1, 'webkitSourceSetDuration() missing (ignored)');
}
}
});
MediaSource.prototype.addEventListener = function(name, handler) {
var re = /^source(open|close|ended)$/;
var match = re.exec(name);
if (match && match[1]) {
name = match[1];
var l = this.msWrapperHandlers[name] || [];
l.push(handler);
this.msWrapperHandlers[name] = l;
} else {
throw 'Unrecognized event name: ' + name;
}
};
function SourceBuffer(video, id) {
this.msWrapperVideo = video;
this.msWrapperSourceId = id;
this.msWrapperTimestampOffset = 0;
}
function FakeSourceBufferedRanges() {
this.length = 0;
}
SourceBuffer.prototype.msWrapperGetBuffered = function() {
dlog(4, 'In msWrapperGetBuffered');
// Chrome 22 doesn't like calling sourceBuffered() before initialization
// segment gets appended.
if (!this.msWrapperAppended) return new FakeSourceBufferedRanges();
var v = this.msWrapperVideo;
var id = this.msWrapperSourceId;
return v.webkitSourceBuffered(id);
};
SourceBuffer.prototype.append = function(bytes) {
dlog(4, 'In append');
var v = this.msWrapperVideo;
var id = this.msWrapperSourceId;
v.webkitSourceAppend(id, bytes);
this.msWrapperAppended = true;
};
SourceBuffer.prototype.abort = function() {
dlog(4, 'In abort');
var v = this.msWrapperVideo;
var id = this.msWrapperSourceId;
v.webkitSourceAbort(id);
};
// The 'setTimestampOffset' method of the media element is an extension to the
// MSE-v0.5 spec, which will be implemented on some devices.
// Calling this method is defined to have the same semantics as setting the
// timestampOffset property of the Media Source object in the current spec.
Object.defineProperty(SourceBuffer.prototype, 'timestampOffset', {
get: function() { return this.msWrapperTimestampOffset; },
set: function(o) {
this.msWrapperTimestampOffset = o;
if (this.msWrapperVideo.webkitSourceTimestampOffset) {
this.msWrapperVideo.webkitSourceTimestampOffset(
this.msWrapperSourceId, o);
} else {
dlog(1, 'webkitSourceTimestampOffset() missing (ignored)');
}
}
});
Object.defineProperty(SourceBuffer.prototype, 'buffered', {
get: SourceBuffer.prototype.msWrapperGetBuffered
});
window.MediaSource = MediaSource;
}
// js/lib/mse/2013/mediaSourcePortability-20150612143746.js end
// js/lib/eme/encryptedMediaPortability-20150612143746.js begin
/* The code tries to wrapper the EME versions with or without webkit prefix */
function prefixedAttributeName(obj, suffix, opt_preprefix) {
suffix = suffix.toLowerCase();
if (opt_preprefix === undefined) {
opt_preprefix = '';
}
for (var attr in obj) {
var lattr = attr.toLowerCase();
if (lattr.indexOf(opt_preprefix) == 0 &&
lattr.indexOf(suffix, lattr.length - suffix.length) != -1) {
return attr;
}
}
return null;
}
function normalizeAttribute(obj, suffix, opt_preprefix) {
if (opt_preprefix === undefined) {
opt_preprefix = '';
}
var attr = prefixedAttributeName(obj, suffix, opt_preprefix);
if (attr) {
obj[opt_preprefix + suffix] = obj[attr];
}
}
function stringToArray(s) {
var array = new Uint8Array(new ArrayBuffer(s.length));
for (var i = 0; i < s.length; i++) {
array[i] = s.charCodeAt(i);
}
return array;
}
function arrayToString(a) {
return String.fromCharCode.apply(String, a);
}
function parsePlayReadyKeyMessage(message) {
// message is UTF-16LE, let's throw out every second element.
var message_ascii = new Array();
for (var i = 0; i < message.length; i += 2) {
message_ascii.push(message[i]);
}
var str = String.fromCharCode.apply(String, message_ascii);
var doc = (new DOMParser()).parseFromString(str, 'text/xml');
return stringToArray(
atob(doc.childNodes[0].childNodes[0].childNodes[0].childNodes[0].data));
}
/**
* Extracts the BMFF Clear Key ID from the init data of the segment.
* @param {ArrayBuffer} initData Init data for the media segment.
* @return {ArrayBuffer} Returns the BMFF ClearKey ID if found, otherwise the
* initData itself.
*/
function extractBMFFClearKeyID(initData) {
// Accessing the Uint8Array's underlying ArrayBuffer is impossible, so we
// copy it to a new one for parsing.
var abuf = new ArrayBuffer(initData.length);
var view = new Uint8Array(abuf);
view.set(initData);
var dv = new DataView(abuf);
var pos = 0;
while (pos < abuf.byteLength) {
var box_size = dv.getUint32(pos, false);
var type = dv.getUint32(pos + 4, false);
if (type != 0x70737368)
throw 'Box type ' + type.toString(16) + ' not equal to "pssh"';
// Scan for Clear Key header
if ((dv.getUint32(pos + 12, false) == 0x58147ec8) &&
(dv.getUint32(pos + 16, false) == 0x04234659) &&
(dv.getUint32(pos + 20, false) == 0x92e6f52c) &&
(dv.getUint32(pos + 24, false) == 0x5ce8c3cc)) {
var size = dv.getUint32(pos + 28, false);
if (size != 16) throw 'Unexpected KID size ' + size;
return new Uint8Array(abuf.slice(pos + 32, pos + 32 + size));
}
// Failing that, scan for Widevine protobuf header
if ((dv.getUint32(pos + 12, false) == 0xedef8ba9) &&
(dv.getUint32(pos + 16, false) == 0x79d64ace) &&
(dv.getUint32(pos + 20, false) == 0xa3c827dc) &&
(dv.getUint32(pos + 24, false) == 0xd51d21ed)) {
return new Uint8Array(abuf.slice(pos + 36, pos + 52));
}
pos += box_size;
}
// Couldn't find it, give up hope.
return initData;
}
function base64_encode(arr) {
var b64dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
var i;
var hexStr = "";
for (i = 0; i < arr.length; i++) {
var hex = arr[i].toString(16);
hexStr += hex.length == 1 ? "0" + hex : hex;
}
var dest = "";
var c;
for (i = 0; i+3 <= hexStr.length; i+=3) {
c = parseInt(hexStr.substring(i, i+3), 16);
dest += b64dict.charAt(c >> 6) + b64dict.charAt(c & 63);
}
if (i+1 == hexStr.length) {
c = parseInt(hexStr.substring(i, i+1), 16);
dest += b64dict.charAt(c << 2);
} else if (i+2 == hexStr.length) {
c = parseInt(hexStr.substring(i, i+2), 16);
dest += b64dict.charAt(c >> 2) + b64dict.charAt((c & 3) << 4);
}
while ((dest.length & 3) > 0) {
dest += b64pad;
}
return dest;
}
function b64tob64url(s) {
var b64urlStr = removePadding(s);
b64urlStr = b64urlStr.replace(/\+/g, "-");
b64urlStr = b64urlStr.replace(/\//g, "_");
return b64urlStr;
}
function removePadding(s) {
return s.replace(/\=/g, "");
}
function base64NoPadding_encode(arr) {
return removePadding(base64_encode(arr));
}
function base64url_encode(arr) {
return b64tob64url(base64_encode(arr));
}
(function() {
var dlog = function() {
var forward = window.dlog || console.log.bind(console);
forward.apply(this, arguments);
};
var proto = window.HTMLMediaElement.prototype;
if (proto.addKey || proto.setMediaKeys) {
return; // Non-prefix version, needn't wrap.
}
if (!proto.webkitAddKey) {
dlog(1, 'EME is not available');
return; // EME is not available at all.
}
proto.generateKeyRequest = function(keySystem, initData) {
return proto.webkitGenerateKeyRequest.call(this, keySystem, initData);
};
proto.addKey = function(keySystem, key, initData, sessionId) {
return proto.webkitAddKey.call(this, keySystem, key, initData, sessionId);
};
proto.cancelKeyRequest = function(keySystem, sessionId) {
return proto.webkitCancelKeyRequest.call(this, keySystem, sessionId);
};
var ael = proto.addEventListener;
var eventWrapper = function(listener, e) {
listener.call(this, e);
};
proto.addEventListener = function(type, listener, useCaptures) {
var re = /^(keyadded|keyerror|keymessage|needkey)$/;
var match = re.exec(type);
if (match) {
ael.call(this, 'webkit' + type, eventWrapper.bind(this, listener),
useCaptures);
} else {
ael.call(this, type, listener, useCaptures);
}
};
})();
// js/lib/eme/encryptedMediaPortability-20150612143746.js end
// js/harness/test-20150612143746.js begin
var BYPASS_CACHE = false;
var XHR_TIMEOUT_LIMIT = 5000;
(function() {
window.testTypes = {
'conformance-test': {
name: 'MSE Conformance Tests',
supported: 'all',
title: 'Media Source and Encrypted Media Conformance Tests',
heading: 'MSE Conformance Tests'
},
'encryptedmedia-test': {
name: 'EME Conformance Tests',
supported: ['2015', 'tip'],
title: 'Encrypted Media Extensions Conformance Tests',
heading: 'EME Conformance Tests'
},
'performance-test': {
name: 'MSE Performance Tests',
supported: 'all',
title: 'Media Source and Encrypted Media Conformance Tests',
heading: 'MSE Performance Tests'
},
'endurance-test': {
name: 'MSE Endurance Tests',
supported: 'all',
title: 'Media Source and Encrypted Media Conformance Tests',
heading: 'MSE Endurance Tests'
},
'progressive-test': {
name: 'Progressive Tests',
supported: 'all',
title: 'HTML Media Element Conformance Tests',
heading: 'HTML Media Element Conformance Tests'
}
};
window.kDefaultTestType = 'conformance-test';
window.currentTestType = null;
window.recycleVideoTag = true;
if (!window.LOG) {
window.LOG = console.log.bind(console);
}
var TestBase = {};
TestBase.onsourceopen = function() {
this.log('default onsourceopen()');
};
TestBase.start = function(runner, video) {
this.log('Test started');
};
TestBase.teardown = function(mseSpec) {
if (this.video != null) {
this.video.removeAllEventListeners();
this.video.pause();
if (mseSpec && mseSpec !== '0.5') // For backwards compatibility.
window.URL.revokeObjectURL(this.video.src);
this.video.src = '';
if (recycleVideoTag)
this.video.parentNode.removeChild(this.video);
}
this.ms = null;
this.video = null;
this.runner = null;
};
TestBase.log = function() {
var args = Array.prototype.slice.call(arguments, 0);
args.splice(0, 0, this.desc + ': ');
LOG.apply(this, args);
};
TestBase.dump = function() {
if (this.video) {
this.log('video.currentTime =', this.video.currentTime);
this.log('video.readyState =', this.video.readyState);
this.log('video.networkState =', this.video.networkState);
}
if (this.ms) {
this.log('ms.sb count =', this.ms.sourceBuffers.length);
for (var i = 0; i < this.ms.sourceBuffers.length; ++i) {
if (this.ms.sourceBuffers[i].buffered) {
var buffered = this.ms.sourceBuffers[i].buffered;
this.log('sb' + i + '.buffered.length', buffered.length);
for (var j = 0; j < buffered.length; ++j) {
this.log(' ' + j + ': (' + buffered.start(j) + ', ' +
buffered.end(j) + ')');
}
} else {
this.log('sb', i, 'invalid buffered range');
}
}
}
};
TestBase.timeout = 30000;
window.createTest = function(name) {
var t = function() {};
t.prototype.__proto__ = TestBase;
t.prototype.desc = name;
t.prototype.running = false;
t.prototype.category = '';
t.prototype.mandatory = true;
return t;
};
window.createMSTest = function(name) {
var t = createTest(name);
t.prototype.start = function(runner, video) {
this.ms = new MediaSource();
this.ms.addEventListener('sourceopen', this.onsourceopen.bind(this));
if (this.ms.isWrapper)
this.ms.attachTo(video);
else
this.video.src = window.URL.createObjectURL(this.ms);
this.log('MS test started');
};
return t;
};
var ConformanceTestRunner = function(testSuite, testsMask, mseSpec) {
this.testView = null;
this.currentTest = null;
this.currentTestIdx = 0;
this.assertThrowsError = true;
this.XHRManager = createXHRManager(createLogger(this.log.bind(this)));
this.timeouts = createTimeoutManager(createLogger(this.log.bind(this)));
this.lastResult = 'pass';
this.mseSpec = mseSpec || '0.5';
if (testsMask) {
this.testList = [];
testsMask = util.resize(testsMask, testSuite.tests.length,
testsMask.substr(-1));
for (var i = 0; i < testSuite.tests.length; ++i)
if (testsMask[i] === '1')
this.testList.push(testSuite.tests[i]);
} else {
this.testList = testSuite.tests;
}
this.fields = testSuite.fields;
this.info = testSuite.info;
this.viewType = testSuite.viewType;
};
ConformanceTestRunner.prototype.log = function() {
var args = Array.prototype.slice.call(arguments, 0);
args.splice(0, 0, 'ConformanceTestRunner: ');
LOG.apply(this, args);
};
ConformanceTestRunner.prototype.assert = function(cond, msg) {
if (!cond) {
++this.testList[this.currentTestIdx].prototype.failures;
this.updateStatus();
this.error('Assert failed: ' + msg, false);
}
};
ConformanceTestRunner.prototype.checkException = function(testFunc, exceptionCode) {
try {
testFunc();
this.fail('Expect exception ' + exceptionCode);
} catch (err) {
this.checkEq(err.code, exceptionCode, 'Exception');
}
};
ConformanceTestRunner.prototype.check = function(condition, passMsg, failMsg) {
if (condition)
this.log(passMsg);
else
this.assert(false, failMsg);
};
ConformanceTestRunner.prototype.checkType = function(x, y, name) {
var t = typeof(x);
var result = t === y;
this.check(result, 'checkType passed: type of ' + name + ' is (' + t + ').',
'Type of ' + name + ' is (' + t + ') which should be (' + y + ')');
};
ConformanceTestRunner.prototype.checkEq = function(x, y, name) {
var result = (x == y) ||
(typeof(x) === 'number' && typeof(y) === 'number' &&
isNaN(x) && isNaN(y));
this.check(result, 'checkEq passed: ' + name + ' is (' + x + ').',
name + ' is (' + x + ') which should be (' + y + ')');
};
ConformanceTestRunner.prototype.checkNE = function(x, y, name) {
var result = (x != y) &&
!(typeof(x) === 'number' && typeof(y) === 'number' &&
isNaN(x) && isNaN(y));
this.check(result, 'checkNE passed: ' + name + ' is (' + x + ').',
name + ' is (' + x + ') which shouldn\'t.');
};
ConformanceTestRunner.prototype.checkApproxEq = function(x, y, name) {
var diff = Math.abs(x - y);
var eps = 0.5;
this.check(diff < eps, 'checkApproxEq passed: ' + name + ' is (' + x + ').',
name + ' is (' + x + ') which should between [' +
(y - eps) + ', ' + (y + eps) + ']');
};
ConformanceTestRunner.prototype.checkGr = function(x, y, name) {
this.check(x > y, 'checkGr passed: ' + name + ' is (' + x + ').',
name + ' is (' + x +
') which should be greater than (' + y + ')');
};
ConformanceTestRunner.prototype.checkGE = function(x, y, name) {
this.check(x >= y, 'checkGE passed: ' + name + ' is (' + x + ').',
name + ' is (' + x +
') which should be greater than or equal to (' + y + ')');
};
ConformanceTestRunner.prototype.checkLE = function(x, y, name) {
this.check(x <= y, 'checkLE passed: ' + name + ' is (' + x + ').',
name + ' is (' + x +
') which should be less than or equal to (' + y + ')');
};
ConformanceTestRunner.prototype.getControlContainer = function() {
// Override this function to anchor one to the DOM.
return document.createElement('div');
};
ConformanceTestRunner.prototype.getNewVideoTag = function() {
// Override this function to anchor one to the DOM.
return document.createElement('video');
};
ConformanceTestRunner.prototype.getOutputArea = function() {
// Override this function to anchor one to the DOM.
return document.createElement('div');
};
ConformanceTestRunner.prototype.updateStatus = function() {
this.testView.getTest(this.currentTestIdx).updateStatus();
};