A Pen by One Tech Genius on CodePen.
-
-
Save onetechgenius/50976cd78da97b59fc6495ba80db8f41 to your computer and use it in GitHub Desktop.
leanback
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 & 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); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ============================== | |
// 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