Skip to content

Instantly share code, notes, and snippets.

@onetechgenius
Forked from anonymous/index.html
Created May 2, 2016 01:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onetechgenius/50976cd78da97b59fc6495ba80db8f41 to your computer and use it in GitHub Desktop.
Save onetechgenius/50976cd78da97b59fc6495ba80db8f41 to your computer and use it in GitHub Desktop.
leanback
<html>
<script type="text/javascript">
...
// do: define HTML5 LocalStorage Extension configuration
LBP.LocalStorage.conf = {
options: {
volume: {
show: true,
ckbx: true
}, // show volume selector, selected on startup
time: {
show: true,
ckbx: true
}, // show time selector, selected on startup
subtitles: {
show: true,
ckbx: true
}, // show subtitles selector, selected on startup
reset: {
show: true
} // show reset selector to reset stored configuration
},
domain: {
store: true, // if global domain-based settings should be stored, default "false"
config: {
storeVolume: true // if volume changes should be stored globaly, default "false"
}
},
debug: true // if debug informations should be shown in console, default "false"
};
...
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<script src="../../popcorn.js"></script>
<script src="../common/popcorn._MediaElementProto.js"></script>
<script src="popcorn.HTMLMediaElement.js"></script>
<script src="popcorn.HTMLVideoElement.unit.js"></script>
<script src="../common/common.unit.js"></script>
<script src="../../test/inject.js"></script>
<script src="../../test/popcorn.inject.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>LBP Test Page</title>
<!-- LeanBack Player - CSS Theme -->
<link rel="stylesheet" media="screen" type="text/css" href="css.player/leanbackPlayer.default.css" />
<!-- LeanBack Player - JS Source -->
<script type="text/javascript" src="./js.player/leanbackPlayer.pack.js"></script>
<!-- LeanBack Player Translation(s) (Examples) - JS Source -->
<script type="text/javascript" src="./js.player/leanbackPlayer.en.js"></script>
<!-- LeanBack Player - English Translation -->
<script type="text/javascript" src="./js.player/leanbackPlayer.de.js"></script>
<!-- LeanBack Player - German Translation -->
<script type='text/javascript' src='./jwplayer/swfobject.js'></script>
<script type='text/javascript' src='./jwplayer/jwplayer.js'></script>
<script type="text/javascript">
LBP.options = {
// show controls bar below video-viewport; default is "false"
controlsBelow: true,
// (delayed) hiding of LB player controls; default is "true"
hideControls: false,
// show playbackrate element in controls bar to change between "playbackRates"
// only available if supported by browser (if set to "true"); default is "false"
showPlaybackRates: true,
// if playbackrates should be extended; by default following are available
playbackRates: [0.5, 1, 1.3, 1.6, 2],
showSubtitles: true,
}
</script>
</head>
<body>
<h1 id="qunit-header">Popcorn HTMLVideoElement Tests</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">
<div id="video"></div>
<div class="leanback-player-video">
<!-- HTML5 Video Element -->
<video width="640" height="360" preload="auto" autoplay controls poster="https://dl.dropboxusercontent.com/u/524589044/Athumb/384907-x-men-apocalypse-poster-crop.jpg">
<!-- HTML5 Video Source(s) -->
<source src="http://www.deadlyblogger.com/NewRelease/xmen1.mp4" type='video/mp4' ; />
<track enabled="true" kind="subtitles" label="English" srclang="en" type="text/x-srt" src="./subtitles/classroom_01a.srt"></track>
}); </video>
 </div>
</body>
</html>
chromecastapi = require('chromecast-api')
var browser = new chromecastapi.Browser()
browser.on('deviceOn', function(device) {
device.play('http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4', 0, function() {
console.log('Playing in your chromecast')
setTimeout(function() {
//Pause the video
device.pause(function() {
console.log('Paused')
});
}, 30000);
setTimeout(function() {
//Stop video
device.stop(function() {
console.log('Stopped')
});
}, 40000);
})
})
// Order matters, and async before sync
QUnit.config.reorder = false;
module("Popcorn Player");
test("Base player methods", 4, function() {
ok(Popcorn.player, "Popcorn.player function exists");
ok(Popcorn.smart, "Popcorn.smart function exists");
Popcorn.player("newplayer");
ok(Popcorn.newplayer, "Popcorn.player registers new players");
ok(Popcorn.player.registry.newplayer, "newplayers enter Popcorn.player.registry");
});
asyncTest("Base player functionality", function() {
Popcorn.player("baseplayer");
var p2 = Popcorn.baseplayer("#video"),
expects = 12,
count = 0,
cueCount = 0,
// These make sure events are only fired once
// any second call will produce a failed test
forwardStart = false,
forwardEnd = false,
backwardStart = false,
backwardEnd = false,
wrapperRunning = {
one: false,
two: false
};
function plus() {
if (++count === expects) {
// clean up added events after tests
Popcorn.removePlugin("forwards");
Popcorn.removePlugin("backwards");
Popcorn.removePlugin("wrapper");
p2.removePlugin("cue");
start();
}
}
Popcorn.plugin("forwards", function() {
return {
start: function(event, options) {
if (!options.startFired) {
options.startFired = true;
forwardStart = !forwardStart;
ok(forwardStart, "forward's start fired");
plus();
}
},
end: function(event, options) {
if (!options.endFired) {
options.endFired = true;
forwardEnd = !forwardEnd;
p2.currentTime(1).play();
ok(forwardEnd, "forward's end fired");
plus();
}
}
};
});
p2.forwards({
start: 3,
end: 4
});
Popcorn.plugin("backwards", function() {
return {
start: function(event, options) {
if (!options.startFired) {
options.startFired = true;
backwardStart = !backwardStart;
p2.currentTime(0).play();
ok(true, "backward's start fired");
plus();
}
},
end: function(event, options) {
if (!options.endFired) {
options.endFired = true;
backwardEnd = !backwardEnd;
ok(backwardEnd, "backward's end fired");
plus();
p2.currentTime(5).play();
}
}
};
});
p2.backwards({
start: 1,
end: 2
});
Popcorn.plugin("wrapper", {
start: function(event, options) {
wrapperRunning[options.wrapper] = true;
},
end: function(event, options) {
wrapperRunning[options.wrapper] = false;
}
});
// second instance of wrapper is wrapping the first
p2.wrapper({
start: 6,
end: 7,
wrapper: "one"
})
.wrapper({
start: 5,
end: 8,
wrapper: "two"
})
// checking wrapper 2's start
.cue(5, function() {
if (cueCount === 0) {
cueCount++;
ok(wrapperRunning.two, "wrapper two is running at second 5");
plus();
ok(!wrapperRunning.one, "wrapper one is stopped at second 5");
plus();
}
})
// checking wrapper 1's start
.cue(6, function() {
if (cueCount === 1) {
cueCount++;
ok(wrapperRunning.two, "wrapper two is running at second 6");
plus();
ok(wrapperRunning.one, "wrapper one is running at second 6");
plus();
}
})
// checking wrapper 1's end
.cue(7, function() {
if (cueCount === 2) {
cueCount++;
ok(wrapperRunning.two, "wrapper two is running at second 7");
plus();
ok(!wrapperRunning.one, "wrapper one is stopped at second 7");
plus();
}
})
// checking wrapper 2's end
.cue(8, function() {
if (cueCount === 3) {
cueCount++;
ok(!wrapperRunning.two, "wrapper two is stopped at second 9");
plus();
ok(!wrapperRunning.one, "wrapper one is stopped at second 9");
plus();
}
});
p2.currentTime(3).play();
});
test("player gets a proper _teardown", 1, function() {
var teardownCalled = false;
Popcorn.player("teardownTester", {
_teardown: function() {
teardownCalled = true;
}
});
var pop = Popcorn.teardownTester("#video");
pop.destroy();
equal(teardownCalled, true, "teardown function was called.");
});
asyncTest("Popcorn.smart player selector", function() {
var expects = 11,
count = 0,
timeout;
function plus() {
if (++count == expects) {
document.getElementById("video").innerHTML = "";
start();
}
}
expect(expects);
Popcorn.player("spartaPlayer", {
_canPlayType: function(nodeName, url) {
return url === "this is sparta" && nodeName !== "unsupported element";
}
});
Popcorn.player("neverEverLand");
// matching url to player returns true
ok(Popcorn.spartaPlayer.canPlayType("div", "this is sparta"), "canPlayType method succeeds on valid url!");
plus();
ok(Popcorn.spartaPlayer.canPlayType("unsupported element", "this is sparta") === false, "canPlayType method fails on invalid container!");
plus();
ok(!!Popcorn.smart("#video", "this is sparta").media.nodeName, "A player was found for this URL");
plus();
// invalid target throws meaningful error
try {
Popcorn.smart("#non_existing_tag", "this is sparta");
} catch (e) {
ok(true, "Popcorn.smart throws exception when target is invalid.");
plus();
}
// not matching url to player returns false
ok(Popcorn.spartaPlayer.canPlayType("div", "this is not sparta") === false, "canPlayType method fails on invalid url!");
plus();
ok(Popcorn.spartaPlayer.canPlayType("video", "this is not sparta") === false, "canPlayType method fails on invalid url and invalid container!");
plus();
var thisIsNotSparta = Popcorn.smart("#video", "this is not sparta", {
events: {
error: function(e) {
clearTimeout(timeout);
ok(true, "invalid player failed and called error callback");
plus();
}
}
});
// Safari won't pass this test, so we'll just skip it
// https://bugs.webkit.org/show_bug.cgi?id=88423
// https://webmademovies.lighthouseapp.com/projects/63272-popcornjs/tickets/1226
timeout = setTimeout(function() {
ok(true, "Workaround for Safari regression on error event with error callback test");
plus();
}, 1000);
equal(thisIsNotSparta.media.nodeName, "VIDEO", "no player was found for this URL, default to video element");
plus();
// no existing canPlayType function returns undefined
ok(Popcorn.neverEverLand.canPlayType("guessing time!", "is this sparta?") === undefined, "non exist canPlayType returns undefined");
plus();
Popcorn.player("somePlayer", {
_canPlayType: function(nodeName, url) {
return url === "canPlayType";
}
});
Popcorn.somePlayer("#video", "canPlayType", {
events: {
canplaythrough: function(e) {
ok(true, "canPlayType passed on a valid type");
plus();
}
}
}).destroy();
Popcorn.somePlayer("#video", "cantPlayType", {
events: {
error: function(e) {
ok(true, "canPlayType failed on an invalid type");
plus();
}
}
}).destroy();
});
asyncTest("Popcorn.smart - audio and video elements", function() {
var expects = 2,
count = 0,
instanceDiv = document.getElementById("video"),
p;
function plus() {
if (++count == expects) {
start();
}
}
p = Popcorn.smart("#video", ["../../test/italia.ogg", "../../test/silence.mp3"]);
equal(instanceDiv.children[0].nodeName, "VIDEO", "Smart player correctly creates HTML5 media elements");
instanceDiv.innerHTML = "";
p.destroy();
plus();
p = Popcorn.smart("#video", "../../test/trailer.ogv");
equal(instanceDiv.children[0].nodeName, "VIDEO", "Smart player correctly creates video elements");
p.destroy();
plus();
});
asyncTest("Popcorn.smart - controls off by default as per spec", function() {
var p1 = Popcorn.smart("#videoControls", "../../test/trailer.ogv");
ok(!p1.controls(), "Video Element has no controls");
start();
});
asyncTest("Popcorn.smart - YouTube wrapper", 1, function() {
var src = "http://www.youtube.com/watch/?v=nfGV32RNkhw",
p1 = Popcorn.smart("#video", src);
p1.on("loadedmetadata", function() {
equal(p1.media.src, src, "Popcorn.smart correctly uses YouTube Wrapper");
start();
});
});
asyncTest("Popcorn.smart - Vimeo wrapper", 1, function() {
var src = "http://vimeo.com/12235444",
p1 = Popcorn.smart("#video", src);
p1.on("loadedmetadata", function() {
equal(p1.media.currentSrc, src, "Popcorn.smart correctly uses Vimeo Wrapper");
start();
});
});
asyncTest("Popcorn.smart - Null Video wrapper", 1, function() {
var src = "#t=,100",
p1 = Popcorn.smart("#video", src);
p1.on("loadedmetadata", function() {
equal(p1.media.currentSrc, src, "Popcorn.smart correctly uses Null Video Wrapper");
start();
});
});
(function(Popcorn) {
// combines calls of two function calls into one
var combineFn = function(first, second) {
first = first || Popcorn.nop;
second = second || Popcorn.nop;
return function() {
first.apply(this, arguments);
second.apply(this, arguments);
};
};
// ID string matching
var rIdExp = /^(#([\w\-\_\.]+))$/;
Popcorn.player = function(name, player) {
// return early if a player already exists under this name
if (Popcorn[name]) {
return;
}
player = player || {};
var playerFn = function(target, src, options) {
options = options || {};
// List of events
var date = new Date() / 1000,
baselineTime = date,
currentTime = 0,
readyState = 0,
volume = 1,
muted = false,
events = {},
// The container div of the resource
container = typeof target === "string" ? Popcorn.dom.find(target) : target,
basePlayer = {},
timeout,
popcorn;
if (!Object.prototype.__defineGetter__) {
basePlayer = container || document.createElement("div");
}
// copies a div into the media object
for (var val in container) {
// don't copy properties if using container as baseplayer
if (val in basePlayer) {
continue;
}
if (typeof container[val] === "object") {
basePlayer[val] = container[val];
} else if (typeof container[val] === "function") {
basePlayer[val] = (function(value) {
// this is a stupid ugly kludgy hack in honour of Safari
// in Safari a NodeList is a function, not an object
if ("length" in container[value] && !container[value].call) {
return container[value];
} else {
return function() {
return container[value].apply(container, arguments);
};
}
}(val));
} else {
Popcorn.player.defineProperty(basePlayer, val, {
get: (function(value) {
return function() {
return container[value];
};
}(val)),
set: Popcorn.nop,
configurable: true
});
}
}
var timeupdate = function() {
date = new Date() / 1000;
if (!basePlayer.paused) {
basePlayer.currentTime = basePlayer.currentTime + (date - baselineTime);
basePlayer.dispatchEvent("timeupdate");
timeout = setTimeout(timeupdate, 10);
}
baselineTime = date;
};
basePlayer.play = function() {
this.paused = false;
if (basePlayer.readyState >= 4) {
baselineTime = new Date() / 1000;
basePlayer.dispatchEvent("play");
timeupdate();
}
};
basePlayer.pause = function() {
this.paused = true;
basePlayer.dispatchEvent("pause");
};
Popcorn.player.defineProperty(basePlayer, "currentTime", {
get: function() {
return currentTime;
},
set: function(val) {
// make sure val is a number
currentTime = +val;
basePlayer.dispatchEvent("timeupdate");
return currentTime;
},
configurable: true
});
Popcorn.player.defineProperty(basePlayer, "volume", {
get: function() {
return volume;
},
set: function(val) {
// make sure val is a number
volume = +val;
basePlayer.dispatchEvent("volumechange");
return volume;
},
configurable: true
});
Popcorn.player.defineProperty(basePlayer, "muted", {
get: function() {
return muted;
},
set: function(val) {
// make sure val is a number
muted = +val;
basePlayer.dispatchEvent("volumechange");
return muted;
},
configurable: true
});
Popcorn.player.defineProperty(basePlayer, "readyState", {
get: function() {
return readyState;
},
set: function(val) {
readyState = val;
return readyState;
},
configurable: true
});
// Adds an event listener to the object
basePlayer.addEventListener = function(evtName, fn) {
if (!events[evtName]) {
events[evtName] = [];
}
events[evtName].push(fn);
return fn;
};
// Removes an event listener from the object
basePlayer.removeEventListener = function(evtName, fn) {
var i,
listeners = events[evtName];
if (!listeners) {
return;
}
// walk backwards so we can safely splice
for (i = events[evtName].length - 1; i >= 0; i--) {
if (fn === listeners[i]) {
listeners.splice(i, 1);
}
}
return fn;
};
// Can take event object or simple string
basePlayer.dispatchEvent = function(oEvent) {
var evt,
self = this,
eventInterface,
eventName = oEvent.type;
// A string was passed, create event object
if (!eventName) {
eventName = oEvent;
eventInterface = Popcorn.events.getInterface(eventName);
if (eventInterface) {
evt = document.createEvent(eventInterface);
evt.initEvent(eventName, true, true, window, 1);
}
}
if (events[eventName]) {
for (var i = events[eventName].length - 1; i >= 0; i--) {
events[eventName][i].call(self, evt, self);
}
}
};
// Attempt to get src from playerFn parameter
basePlayer.src = src || "";
basePlayer.duration = 0;
basePlayer.paused = true;
basePlayer.ended = 0;
options && options.events && Popcorn.forEach(options.events, function(val, key) {
basePlayer.addEventListener(key, val, false);
});
// true and undefined returns on canPlayType means we should attempt to use it,
// false means we cannot play this type
if (player._canPlayType(container.nodeName, src) !== false) {
if (player._setup) {
player._setup.call(basePlayer, options);
} else {
// there is no setup, which means there is nothing to load
basePlayer.readyState = 4;
basePlayer.dispatchEvent("loadedmetadata");
basePlayer.dispatchEvent("loadeddata");
basePlayer.dispatchEvent("canplaythrough");
}
} else {
// Asynchronous so that users can catch this event
setTimeout(function() {
basePlayer.dispatchEvent("error");
}, 0);
}
popcorn = new Popcorn.p.init(basePlayer, options);
if (player._teardown) {
popcorn.destroy = combineFn(popcorn.destroy, function() {
player._teardown.call(basePlayer, options);
});
}
return popcorn;
};
playerFn.canPlayType = player._canPlayType = player._canPlayType || Popcorn.nop;
Popcorn[name] = Popcorn.player.registry[name] = playerFn;
};
Popcorn.player.registry = {};
Popcorn.player.defineProperty = Object.defineProperty || function(object, description, options) {
object.__defineGetter__(description, options.get || Popcorn.nop);
object.__defineSetter__(description, options.set || Popcorn.nop);
};
// player queue is to help players queue things like play and pause
// HTML5 video's play and pause are asynch, but do fire in sequence
// play() should really mean "requestPlay()" or "queuePlay()" and
// stash a callback that will play the media resource when it's ready to be played
Popcorn.player.playerQueue = function() {
var _queue = [],
_running = false;
return {
next: function() {
_running = false;
_queue.shift();
_queue[0] && _queue[0]();
},
add: function(callback) {
_queue.push(function() {
_running = true;
callback && callback();
});
// if there is only one item on the queue, start it
!_running && _queue[0]();
}
};
};
// Popcorn.smart will attempt to find you a wrapper or player. If it can't do that,
// it will default to using an HTML5 video in the target.
Popcorn.smart = function(target, src, options) {
var node = typeof target === "string" ? Popcorn.dom.find(target) : target,
i, srci, j, media, mediaWrapper, popcorn, srcLength,
// We leave HTMLVideoElement and HTMLAudioElement wrappers out
// of the mix, since we'll default to HTML5 video if nothing
// else works. Waiting on #1254 before we add YouTube to this.
wrappers = "HTMLYouTubeVideoElement HTMLVimeoVideoElement HTMLSoundCloudAudioElement HTMLNullVideoElement".split(" ");
if (!node) {
Popcorn.error("Specified target `" + target + "` was not found.");
return;
}
// If our src is not an array, create an array of one.
src = typeof src === "string" ? [src] : src;
// Loop through each src, and find the first playable.
for (i = 0, srcLength = src.length; i < srcLength; i++) {
srci = src[i];
// See if we can use a wrapper directly, if not, try players.
for (j = 0; j < wrappers.length; j++) {
mediaWrapper = Popcorn[wrappers[j]];
if (mediaWrapper && mediaWrapper._canPlaySrc(srci) === "probably") {
media = mediaWrapper(node);
popcorn = Popcorn(media, options);
// Set src, but not until after we return the media so the caller
// can get error events, if any.
setTimeout(function() {
media.src = srci;
}, 0);
return popcorn;
}
}
// No wrapper can play this, check players.
for (var key in Popcorn.player.registry) {
if (Popcorn.player.registry.hasOwnProperty(key)) {
if (Popcorn.player.registry[key].canPlayType(node.nodeName, srci)) {
// Popcorn.smart( player, src, /* options */ )
return Popcorn[key](node, srci, options);
}
}
}
}
// If we don't have any players or wrappers that can handle this,
// Default to using HTML5 video. Similar to the HTMLVideoElement
// wrapper, we put a video in the div passed to us via:
// Popcorn.smart( div, src, options )
var videoHTML,
videoElement,
videoID = Popcorn.guid("popcorn-video-"),
videoHTMLContainer = document.createElement("div");
videoHTMLContainer.style.width = "100%";
videoHTMLContainer.style.height = "100%";
// If we only have one source, do not bother with source elements.
// This means we don't have the IE9 hack,
// and we can properly listen to error events.
// That way an error event can be told to backup to Flash if it fails.
if (src.length === 1) {
videoElement = document.createElement("video");
videoElement.id = videoID;
node.appendChild(videoElement);
setTimeout(function() {
// Hack to decode html characters like &amp; to &
var decodeDiv = document.createElement("div");
decodeDiv.innerHTML = src[0];
videoElement.src = decodeDiv.firstChild.nodeValue;
}, 0);
return Popcorn('#' + videoID, options);
}
node.appendChild(videoHTMLContainer);
// IE9 doesn't like dynamic creation of source elements on <video>
// so we do it in one shot via innerHTML.
videoHTML = '<video id="' + videoID + '" preload=auto autobuffer>';
for (i = 0, srcLength = src.length; i < srcLength; i++) {
videoHTML += '<source src="' + src[i] + '">';
}
videoHTML += "</video>";
videoHTMLContainer.innerHTML = videoHTML;
if (options && options.events && options.events.error) {
node.addEventListener("error", options.events.error, false);
}
return Popcorn('#' + videoID, options);
};
})(Popcorn);
module("Popcorn Parser");
asyncTest("Parsing Functions", function() {
var expects = 3,
count = 0,
popperly = Popcorn("#video");
function plus() {
if (++count === expects) {
start();
}
}
expect(expects);
ok(typeof Popcorn.parser === "function", "Popcorn.parser is a function");
plus();
Popcorn.parser("parseJSON", "json", function(data) {
return this;
});
ok(typeof popperly.parseJSON === "function", "Popcorn.parser created a parseJSON function");
plus();
ok(typeof popperly.parseJSON().parseJSON("data/test.js").parseJSON === "function", "parseJSON function is chainable");
plus();
});
asyncTest("Parsing Integrity", function() {
var expects = 6,
count = 0,
timeOut = 0,
poppercore = Popcorn("#video");
function plus() {
if (++count === expects) {
start();
// clean up added events after tests
Popcorn.removePlugin("parserTest");
}
}
expect(expects);
Popcorn.parser("parseJSON2", function(data) {
ok(typeof data.json === "object", "data.json exists");
plus();
return data.json;
});
Popcorn.parser("parseJSON3", "json", function(data) {
ok(typeof data === "object", "data exists");
plus();
return data;
});
Popcorn.plugin("parserTest", {
start: function() {
ok(true, "parserTest started");
plus();
},
end: function() {
ok(true, "parserTest ended");
plus();
}
});
poppercore.parseJSON2("data/parserData.json", function() {
poppercore.parseJSON3("data/parserData.json", function() {
poppercore.currentTime(5).play();
});
});
});
asyncTest("Parsing Handler - References unavailable plugin", function() {
var expects = 1,
count = 0,
timeOut = 0,
interval,
poppercore = Popcorn("#video");
function plus() {
if (++count === expects) {
start();
// clean up added events after tests
clearInterval(interval);
poppercore.removePlugin("parserTest");
}
}
expect(expects);
Popcorn.parser("parseJson", function(data) {
return data.json;
});
poppercore.parseJson("data/parseMissing.json");
// interval used to wait for data to be parsed
interval = setInterval(function() {
poppercore.currentTime(5).play().currentTime(6);
ok(true, "Ignored call to missing plugin ");
plus();
}, 2000);
});
asyncTest("Parser Support - audio", function() {
var expects = 3,
count = 0,
timeOut = 0,
interval,
audiocorn = Popcorn("#audio");
function plus() {
if (++count === expects) {
start();
Popcorn.removePlugin("testAudioParser");
}
}
expect(expects);
Popcorn.plugin("testAudioParser", {
start: function() {
ok(true, "testAudioParser started: " + Math.round(this.currentTime()));
plus();
},
end: function() {
ok(true, "testAudioParser ended: " + Math.round(this.currentTime()));
plus();
}
});
Popcorn.parser("parseAudio", function(data) {
ok(typeof data.json === "object", "data.json exists");
plus();
return data.json;
});
audiocorn.parseAudio("data/parserAudio.json", function() {
audiocorn.currentTime(4).play();
});
});
(function(Popcorn) {
var
AP = Array.prototype,
OP = Object.prototype,
forEach = AP.forEach,
slice = AP.slice,
hasOwn = OP.hasOwnProperty,
toString = OP.toString;
// stores parsers keyed on filetype
Popcorn.parsers = {};
// An interface for extending Popcorn
// with parser functionality
Popcorn.parser = function(name, type, definition) {
if (Popcorn.protect.natives.indexOf(name.toLowerCase()) >= 0) {
Popcorn.error("'" + name + "' is a protected function name");
return;
}
// fixes parameters for overloaded function call
if (typeof type === "function" && !definition) {
definition = type;
type = "";
}
if (typeof definition !== "function" || typeof type !== "string") {
return;
}
// Provides some sugar, but ultimately extends
// the definition into Popcorn.p
var natives = Popcorn.events.all,
parseFn,
parser = {};
parseFn = function(filename, callback, options) {
if (!filename) {
return this;
}
// fixes parameters for overloaded function call
if (typeof callback !== "function" && !options) {
options = callback;
callback = null;
}
var that = this;
Popcorn.xhr({
url: filename,
dataType: type,
success: function(data) {
var tracksObject = definition(data, options),
tracksData,
tracksDataLen,
tracksDef,
idx = 0;
tracksData = tracksObject.data || [];
tracksDataLen = tracksData.length;
tracksDef = null;
// If no tracks to process, return immediately
if (!tracksDataLen) {
return;
}
// Create tracks out of parsed object
for (; idx < tracksDataLen; idx++) {
tracksDef = tracksData[idx];
for (var key in tracksDef) {
if (hasOwn.call(tracksDef, key) && !!that[key]) {
that[key](tracksDef[key]);
}
}
}
if (callback) {
callback();
}
}
});
return this;
};
// Assign new named definition
parser[name] = parseFn;
// Extend Popcorn.p with new named definition
Popcorn.extend(Popcorn.p, parser);
// keys the function name by filetype extension
//Popcorn.parsers[ name ] = true;
return parser;
};
})(Popcorn);
/**
* The HTMLVideoElement and HTMLAudioElement are wrapped media elements
* that are created within a DIV, and forward their properties and methods
* to a wrapped object.
*/
(function(Popcorn, document) {
function canPlaySrc(src) {
// We can't really know based on URL.
return "maybe";
}
function wrapMedia(id, mediaType) {
var parent = typeof id === "string" ? document.querySelector(id) : id,
media = document.createElement(mediaType);
parent.appendChild(media);
// Add the helper function _canPlaySrc so this works like other wrappers.
media._canPlaySrc = canPlaySrc;
return media;
}
Popcorn.HTMLVideoElement = function(id) {
return wrapMedia(id, "video");
};
Popcorn.HTMLVideoElement._canPlaySrc = canPlaySrc;
Popcorn.HTMLAudioElement = function(id) {
return wrapMedia(id, "audio");
};
Popcorn.HTMLAudioElement._canPlaySrc = canPlaySrc;
}(Popcorn, window.document));
// Find a suitable video source for this browser.
var videoSource = (function() {
var v = document.createElement("video"),
sources = [{
type: "video/webm",
file: "../../test/trailer.webm"
}, {
type: "video/mp4",
file: "../../test/trailer.mp4"
}, {
type: "video/ogg",
file: "../../test/trailer.ogv"
}],
source,
sourcesLength = sources.length;
while (sourcesLength--) {
source = sources[sourcesLength];
if (v.canPlayType(source.type) !== "") {
return source;
}
}
throw "No Supported Media Types found for this browser.";
}());
var testData = {
videoSrc: videoSource.file,
videoType: videoSource.type,
expectedDuration: 64.544,
createMedia: function(id) {
return Popcorn.HTMLVideoElement(id);
}
};
// ==============================
// Variables
// ==============================
$color-palette: ( body: #222, blue: #1ca7ec, red: #ff3b2d, yellow: #fee70f, green: #50cd4d);
$background-index: 20;
$character-index: 200;
$foreground-index: 2000;
$site-gutter: 8vmin;
// ==============================
// Helpers
// ==============================
%simple-cover {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
// ==============================
// Resets
// ==============================
*,
*:before,
*:after {
box-sizing: border-box;
position: relative;
}
*:before,
*:after {
pointer-events: none;
}
// ==============================
// Structure
// ==============================
html {
height: 100%;
font-family: 'Roboto';
font-size: 3vmin;
font-weight: 300;
text-align: center;
background: map-get($color-palette, body);
color: rgba(#fff, .85);
}
body {
height: 100%;
min-height: 100%;
margin: 0;
}
// ==============================
// Basic Content
// ==============================
a {
text-decoration: none;
color: map-get($color-palette, blue);
}
// ==============================
// Loading
// ==============================
.loading {
@extend %simple-cover;
animation: temporaryAppearance 3s forwards;
}
@keyframes temporaryAppearance {
0%,
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
// ==============================
// Loading Label
// ==============================
.loading-label {
font-size: 8vmin;
font-weight: 100;
}
// ==============================
// Loading Indicator
// ==============================
.loading-indicator {
display: block;
position: absolute;
top: 50%;
left: 50%;
height: 50vmin;
width: 50vmin;
animation: spin 5s linear infinite;
background: map-get($color-palette, body);
border: 1vw solid transparent;
border-top-color: map-get($color-palette, blue);
border-right-color: map-get($color-palette, red);
border-bottom-color: map-get($color-palette, yellow);
border-left-color: map-get($color-palette, green);
border-radius: 100vmin;
transform: translate(-50%, -50%);
&:before {
position: absolute;
top: -10%;
left: -10%;
z-index: $background-index;
height: 50%;
width: 50%;
content: '';
border-radius: 100vmin;
box-shadow: inset 10vmin -10vmin 5vmin 0 map-get($color-palette, body);
}
}
@keyframes spin {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
// ==============================
// Standby
// ==============================
.standby {
@extend %simple-cover;
animation: permanentAppearance 3s 1s forwards;
background: url('http://i.imgur.com/JQAM3Uo.jpg') no-repeat 0 0;
background-position: 50% 50%;
background-size: cover;
opacity: 0;
&:before {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
content: '';
background: rgba(#000, .5);
}
}
@keyframes permanentAppearance {
0%,
80% {
opacity: 0;
}
100% {
opacity: 1;
}
}
// ==============================
// Prompt
// ==============================
.prompt {
position: absolute;
bottom: 18%;
left: 0;
width: 100%;
padding: 0 $site-gutter;
white-space: nowrap;
}
// ==============================
// Action
// ==============================
.action {
margin: 0;
padding-top: 50vmin;
font-size: 9vmin;
font-weight: 300;
line-height: 1;
color: map-get($color-palette, blue);
}
// ==============================
// Discover
// ==============================
.discover {
padding-bottom: 2.5vmin;
border-bottom: .2vmin solid rgba(#fff, .2);
a {
box-shadow: 0 .2vmin 0 0 rgba(map-get($color-palette, blue), 0);
transition: box-shadow .4s;
&:hover {
box-shadow: 0 .2vmin 0 0 map-get($color-palette, blue);
}
}
}
// ==============================
// Chromecast Label
// ==============================
.chromecast-label {
position: absolute;
bottom: $site-gutter;
left: $site-gutter;
padding-left: 7.5vmin;
text-align: left;
&:before {
position: absolute;
top: 15%;
left: 0;
width: 6vmin;
height: 75%;
content: '';
border: .5vmin solid map-get($color-palette, blue);
}
}
%label {
margin: 0;
font-weight: 300;
}
// ==============================
// Chromecast Name
// ==============================
.chromecast__name {
@extend %label;
font-size: 3vmin;
}
// ==============================
// Chromecast Network
// ==============================
.chromecast__network {
@extend %label;
font-size: 2vmin;
}
// ==============================
// Time
// ==============================
.time-island {
position: absolute;
bottom: $site-gutter;
right: $site-gutter;
text-align: right;
}
// ==============================
// Time
// ==============================
time {
@extend %label;
font-size: 7vmin;
line-height: 1;
}
<!-- surrounding element with class - needed!! --> <div class="leanback-player-video"> <!-- HTML5 <video> element --> <video width="360" height="240" preload="metadata" controls poster="./folder/poster.jpg"> <!-- HTML5 <video> sources --> <source src="./folder/video.webm" type='video/webm; codecs="vp8, vorbis"'/> <source src="./folder/video.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'/> <source src="./folder/video.ogv" type='video/ogg; codecs="theora, vorbis"'/> </video> </div> <!-- surrounding element with class - needed!! --> <div class="leanback-player-audio" width="350px"> <!-- HTML5 <audio> element --> <audio preload="metadata" controls> <!-- HTML5 <audio> sources --> <source src="./folder/audio.mp4" type='audio/mp4; codecs="mp4a.40.2"'/> <source src="./folder/audio.mp3" type='audio/mpeg; codecs="vorbis"'/> <source src="./folder/audio.oga" type='audio/ogg; codecs="vorbis"'/> </audio> </div> <!-- surrounding element with class - needed!! --> <div class="leanback-player-video"> <!-- HTML5 <video> element --> <video width="360" height="240" preload="metadata" controls poster="./folder/poster.jpg"> <!-- HTML5 <video> sources --> ... <!-- Flash-Fallback --> <object class="leanback-player-flash-fallback" width="360" height="240" ...> ... </object> <!-- HTML-Fallback - Download Links --> <div class="leanback-player-html-fallback"> <img src="./folder/poster.jpg" width="360" height="240" alt="Poster Image" title="No HTML5 media playback capabilities available." /> <div> <strong>Download Video:</strong> <a href="./folder/video.mp4">.mp4</a>,
<a href="./folder/video.webm">.webm</a>,
<a href="./folder/video.ogv">.ogv</a> </div> </div> </video> </div> <!-- surrounding element with class - needed!! --> <div class="leanback-player-audio" width="350px"> <!-- HTML5 <audio> element --> <audio preload="metadata" controls> <!-- HTML5 <audio> sources --> ... <!-- HTML-Fallback - Download Links --> <div class="leanback-player-html-fallback"> <div> <strong>Download Audio:</strong> <a href="./folder/audio.mp4">.mp4</a> <a href="./folder/audio.mp3">.mp3</a> <a href="./folder/audio.oga">.oga</a> </div> </div> </audio> </div> <script type="text/javascript"> LBP.options= {
// your options here (see list of options below)
}
</script> // if video player should start in fullscreen mode; default is "false"
autoFullscreen: true,
// if you want to use native player on iPad; default is "false"
defaultIPadControls: true,
// set up default language, en = english, de = german, fr = french, ...
defaultLanguage: "en",
// change to browser language if available
setToBrowserLanguage: true,
// video controls bar elements;
// by default all of them available (if present by options and in CSS theme)
defaultControls: ["Play",
"Pause",
"Stop",
"Progress",
"Timer",
"Volume",
"Playback",
"Subtitles",
"Sources",
"Fullscreen"],
// audio controls bar elements;
// by default all of them available (if present by options and in CSS theme)
defaultAudioControls: ["Play",
"Pause",
"Stop",
"Progress",
"Timer",
"Volume"],
// extended controls to be shown within the player;
// by default all of them available
controlsExtra: ["Poster",
"Embed",
"Logo",
"Spinner",
"BigPlay"],
// show controls bar below video-viewport; default is "false"
controlsBelow: true,
// (delayed) hiding of LB player controls; default is "true"
hideControls: false,
// if delayed hiding, hide controls bar after x seconds
hideControlsTimeout: 4,
// prevent hiding of controls bar if video paused; default is "false"
hideControlsOnPause: true,
// if media element should be paused and unfocused (CSS) on focus lost
pauseOnFocusLost: false,
// prevent playing more than one media element (player) at same time
pauseOnPlayerSwitch: true,
// focus first (video) player on initialization; default is "true"
focusFirstOnInit: false,
// if media element buffering should be stopped/restarted on focus lost/re-focused
// overwrites pauseOnPlayerSwitch to "true"
// stores playback-status (playing/paused) to come back
handleBufferingOnFocusLost: true, // (Experimental)
// if poster-image should reappear once video ended; default is "true"
posterRestore: false,
// set default volume; default is "6"
defaultVolume: 3,
// set how many volume rates; default is "8"
volumeRates: 10,
// hide subtitles element from controls bar; default is "true" (shown)
showSubtitles: false,
// set default language for subtitles; can differ from "defaultLanguage"
defaultSubtitleLanguage: "en",
// set to "false" to disable subtitle on player initialization; default is "true"
initSubtitle: false,
// show element in controls bar to change between source resolutions; default is "false"
showSources: true,
// show playbackrate element in controls bar to change between "playbackRates"
// only available if supported by browser (if set to "true"); default is "false"
showPlaybackRates: true,
// if playbackrates should be extended; by default following are available
playbackRates: [0.25,
0.5,
1,
2],
// default timer format,
// could be "PASSED_DURATION" (default), "PASSED_REMAINING", "PASSED_HOVER_REMAINING"
defaultTimerFormat: "PASSED_DURATION",
// set up seek-skip in seconds; to jump back or forward x seconds
seekSkipSec: 3,
// set up seek-skip in percent; to jump back or forward x percent
seekSkipPerc: 10,
// if you want to show your users an embed-code item within the player; default is "false"
showEmbedCode: true, // (Experimental)
// logo path/url; set up position with CSS
logo: "",
// use preloading spinner or not; by default is "true"
useSpinner: true,
// use to adapt spinner circles to your needs (CSS)
useSpinnerCircles: 7,
// trigger events of HTML5 media elements
// eg.: loadstart, progress, suspend, abort, error, emptied, stalled;
// play, pause; loadedmetadata, loadeddata, waiting, playing, canplay, canplaythrough;
// seeking, seeked, timeupdate, ended; ratechange, durationchange, volumechange
triggerHTML5Events: ["play",
"pause"],
<script type="text/javascript"> LBP.options= {
... // set up default subtitle language; can differ from "defaultLanguage" option
// HINT: if you want to use "en" as default, there is no need to add subtitle option
defaultSubtitleLanguage: "en", ...
}
</script> <!-- surrounding element with class - needed!! --> <div class="leanback-player-video"> <!-- HTML5 <video> element --> <video width="360" height="240" preload="metadata" controls poster="./folder/poster.jpg"> <!-- HTML5 <video> sources --> ... <!-- subtitle-references with <track>-element --> <track enabled="true" kind="subtitles" label="German" srclang="de" type="text/x-srt" src="./folder/srt_example_de.srt"></track> <track enabled="true" kind="subtitles" label="English" srclang="en" type="text/plain" src="./folder/sbv_example_en.sbv"></track> <track enabled="true" kind="subtitles" label="France" srclang="fr" type="text/plain" src="./folder/sbv_example_fr.sbv"></track> </video> </div> <script type="text/javascript"> LBP.options= {
... // show controls bar element to change between source resolutions; default is "false"
showSources: true, ...
}
</script> <!-- surrounding element with class - needed!! --> <div class="leanback-player-video"> <!-- HTML5 <video> element --> <video width="360" height="240" preload="metadata" controls poster="./folder/poster.jpg"> <!-- HTML5 <video> sources --> <source src="./folder/video_1.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'/> <source src="./folder/video_2.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'/> <source src="./folder/video_3.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'/> <source src="./folder/video_1.webm" type='video/webm; codecs="vp8, vorbis"'/> <source src="./folder/video_2.webm" type='video/webm; codecs="vp8, vorbis"'/> <source src="./folder/video_3.webm" type='video/webm; codecs="vp8, vorbis"'/> <source src="./folder/video_1.ogv" type='video/ogg; codecs="theora, vorbis"'/> <source src="./folder/video_2.ogv" type='video/ogg; codecs="theora, vorbis"'/> <source src="./folder/video_3.ogv" type='video/ogg; codecs="theora, vorbis"'/> </video> </div> <script type="text/javascript"> ... // do: define ChapterNavigation options
LBP.ChapterNavigation= {
"unique_id": {
cue: [ {
cueTime: {
start: "0", end: "12"
}
,
cueHeadline: "Intro",
cueContent: "<strong>A small and short Intro</strong>"
}
,
{
cueTime: {
start: "14", end: "69"
}
,
cueHeadline: "Head-Snarlers",
cueContent: "<strong>Second Part</strong><br/>- \"2nd\" -"
}
,
{
cueTime: {
start: "70", end: "150"
}
,
cueHeadline: "Third Pard",
cueContent: "<strong>Darkness</strong><br/>- \"3rd...\" -"
}
], // if playback should start if cue clicked (timeline and cue list)
cueClickPlay: true, // true when drawing the cuelist with JS (recommended)
cueList: true, // use h for cueTime values hh:mm:ss.ms[msms]
// use m for cueTime values mm:ss.ms[msms]
// use s for cueTime values ss.ms[msms]
// use ms for cueTime values ms[msms]
cueTimeUnit: "s", // true if cueheadline should be shown in fullscreen mode
cueFullscreen: true, // time in seconds to show the cueheadline
// 0 for complete duration between the start and end cueTime of a cue
cueFullscreenTime: 4, // duration of video to calculate positions of chapters in progress bar
duration: 150
}
}
;
... </script> ... <!-- HTML5 <video> element with id="unique_id" --> <video id="unique_id" ...> ... </video> <!-- if cueList is "true" the chapter navigation needs a container like below with id="unique_id_list" --> <div id="unique_id_list" class="cue_list"><h3>Chapter Navigation - Headline</h3></div> ... <script type="text/javascript"> ... // do: define HDButton option(s)
LBP.HDButton= {
"unique_id1": {
HDStream: [ "./folder/video1_hd.mp4", "./folder/video1_hd.webm", "./folder/video1_hd.ogv"]
}
,
"unique_id2": {
HDStream: [ "./folder/video2_hd.mp4", "./folder/video2_hd.webm", "./folder/video2_hd.ogv"]
}
,
CSS: false, // using css style button (true|false)
SDBtn: "./img/HDButton.off.png",
HDBtn: "./img/HDButton.on.png",
controlsBar: true, // add HDButton to controls bar; overwrites other options (CSS,...)
mobileOnly: true // HDButton for mobile devices only; eg. use showSources for desktop
}
;
... </script> ... <!-- HTML5 <video> element #1 --> <video id="unique_id1" ... > ... <!-- HTML5 <video> element #2 --> <video id="unique_id2" ... > ... ... <!-- HTML5 <video> element --> <video id="unique_id" ... ... // use video playlist categories (true), default is false
useCategories: false,
// default video playlist category name
defaultCategory: "Default",
// set video playlist items height
itemHeight: 150,
// set video playlist items poster width
itemPosterWidth: 82,
// set video playlist items poster height
itemPosterHeight: 52,
// video playlist as element in controls bar (true) or as child of video-window (false)
// like eg. the Big-Play button
controlsElement: true <script type="text/javascript"> ... LBP.vplaylist["unique_id"]= {
items: [ {
category: "My 1st Playlist",
title: "First example video",
duration: 53,
poster: "http://url/poster1.jpg",
source: ["http://url/video1.mp4",
"http://url/video1.ogv"],
sources: [ {
src: "http://url/video1_1.mp4"
}
,
{
src: "http://url/video1_1.ogv"
}
],
subtitles: [ {
kind: "subtitles", label: "German", srclang: "de", type: "text/x-srt", src: "http://url/sub1_de.srt"
}
,
{
kind: "subtitles", label: "English", srclang: "en", type: "text/plain", src: "http://url/sub1_en.sbv"
}
,
{
kind: "subtitles", label: "France", srclang: "fr", type: "text/plain", src: "http://url/sub1_fr.sbv"
}
]
}
,
{
category: "My 2st Playlist",
title: "Second example video",
duration: 185,
poster: "http://url/poster2.jpg",
source: ["http://url/video2.mp4",
"http://url/video2.ogv"],
sources: [ {
src: "http://url/video2_1.mp4"
}
,
{
src: "http://url/video2_1.ogv"
}
],
subtitles: [ {
kind: "subtitles", label: "German", srclang: "de", type: "text/x-srt", src: "http://url/sub2_de.srt"
}
,
{
kind: "subtitles", label: "English", srclang: "en", type: "text/plain", src: "http://url/sub2_en.sbv"
}
,
{
kind: "subtitles", label: "France", srclang: "fr", type: "text/plain", src: "http://url/sub2_fr.sbv"
}
]
}
]
}
... </script> <script type="text/javascript"> ... // do: define XSPF Audio Playlist options
LBP.XSPFAudio.options= {
// if playlist style "width" is defined within your CSS; default is "false"
fixedWidth: true
}
;
// do: provide XSPF Audio Playlist URL for HTML5 <audio> element id="unique_id"
LBP.XSPFAudio["unique_id"]="http://url/tracklist.xml";
... </script> ... <!-- HTML5 <audio> element with id="unique_id" --> <audio id="unique_id" ...> <source src="http://url/audio_file.mp4" type='audio/mp4; codecs="mp4a.40.2"' /> ... </audio> <div id="unique_id_list"></div> <!-- the XSPF playlist container --> ... [leanback_video mp4="source_1_url.mp4|source_2_url.mp4" webm="source_1_url.webm|source_2_url.webm" ogg="source_1_url.ogv|source_2_url.ogv" poster="poster_url.jpg" width="640" height="360" preload="metadata" autoplay="true" loop="true" subtitles="German|de|text/x-srt|subtitle_1_url.srt||English|en|text/plain|subtitle_2_url.sbv" flash="flash_fallback.swf"] [leanback_audio mp4="source_1_url.mp4" mp3="source_1_url.mp3" ogg="source_1_url.oga" preload="metadata" autoplay="true" loop="true"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment