rewrite Ox.VideoElement

This commit is contained in:
j 2013-07-09 22:48:22 +00:00
parent 708aff64e4
commit f938f281f1
2 changed files with 227 additions and 296 deletions

View file

@ -3,10 +3,13 @@
/*@ /*@
Ox.VideoElement <f> VideoElement Object Ox.VideoElement <f> VideoElement Object
options <o> Options object options <o> Options object
autoplay <b|false> autoplay
items <a|[]> array of objects with src,in,out,duration,offset
look <b|false> loop playback
self <o> Shared private variable self <o> Shared private variable
([options[, self]]) -> <o:Ox.Element> VideoElement Object ([options[, self]]) -> <o:Ox.Element> VideoElement Object
loadedmetadata <!> loadedmetadata loadedmetadata <!> loadedmetadata
pointschange <!> pointschange itemchange <!> itemchange
seeked <!> seeked seeked <!> seeked
seeking <!> seeking seeking <!> seeking
sizechange <!> sizechange sizechange <!> sizechange
@ -19,50 +22,58 @@ Ox.VideoElement = function(options, self) {
var that = Ox.Element({}, self) var that = Ox.Element({}, self)
.defaults({ .defaults({
autoplay: false, autoplay: false,
preload: 'none', loop: false,
src: [] items: []
}) })
.options(options || {}) .options(options || {})
.update({
items: function() {
self.loadedMetadata = false;
loadItems(function() {
if (self.items.length != self.numberOfItems) {
self.numberOfItems = self.items.lenth;
if (self.currentItem > self.numberOfItems) {
self.currentItem = 0;
}
}
var item = self.items[self.currentItem];
if (self.$video.attr('src') != item.src) {
setCurrentVideo();
}
});
}
})
.css({width: '100%', height: '100%'}); .css({width: '100%', height: '100%'});
Ox.Log('Video', 'VIDEO ELEMENT OPTIONS', self.options); Ox.Log('Video', 'VIDEO ELEMENT OPTIONS', self.options);
self.currentPart = 0; self.currentItem = 0;
self.items = []; self.currentTime = 0;
self.currentVideo = 0;
self.loadedMetadata = false; self.loadedMetadata = false;
self.paused = true; self.paused = true;
self.$video = $('<div>'); self.seeking = false;
self.$videos = [getVideo(), getVideo()];
self.$video = self.$videos[self.currentVideo];
self.video = self.$video[0];
self.$brightness = $('<div>').css({
width: '100%',
height: '100%',
background: 'rgb(0, 0, 0)',
opacity: 0
})
.appendTo(that);
if (Ox.isFunction(self.options.src)) { loadItems(function() {
self.isPlaylist = true; setCurrentItem(0);
self.currentItem = 0; self.options.autoplay && play();
self.currentPage = 0; });
self.loadedMetadata = false;
self.pageLength = 2;
self.options.src(function(items) {
self.numberOfItems = items;
self.numberOfPages = Math.ceil(self.numberOfItems / self.pageLength);
loadPages(function() {
Ox.Log('Video', 'VIDEO PAGES LOADED');
setCurrentItem(0);
if (!self.loadedMedatata) {
self.loadedMetadata = true;
that.triggerEvent('loadedmetadata');
that.triggerEvent('pointschange'); // fixme: needs to be triggered again, loadedmetadata messes with duration
}
});
});
} else {
self.numberOfItems = 1;
self.items.push(loadItem(self.options.src));
}
function getCurrentPage() {
return Math.floor(self.currentItem / self.pageLength);
}
function getCurrentTime() { function getCurrentTime() {
return self.items[self.currentItem].offsets[self.currentPart] + self.video.currentTime; var item = self.items[self.currentItem];
return self.seeking
? self.currentTime
: item.offset + self.video.currentTime - item['in'];
} }
function getset(key, value) { function getset(key, value) {
@ -76,232 +87,216 @@ Ox.VideoElement = function(options, self) {
return ret; return ret;
} }
function loadItem(src, points, callback) { function getVideo() {
src = Ox.isArray(src) ? src : [src]; return $('<video>')
var item = { .css({position: 'absolute'})
currentPart: 0, .on({
duration: 0, ended: function() {
durations: src.map(function() { if (self.video == this) {
return 0; setCurrentItem(self.currentItem + 1);
}), }
offsets: [], },
parts: src.length loadedmetadata: function() {
}; // metadata loaded in loadItems
if (points) { },
item.points = points; progress: function() {
} // not implemented
item.$videos = src.map(function(src, i) { },
//fixme: get rid of this to make use of browser caching seeked: function() {
// but in all browsers except firefox, if (self.video == this) {
// loadedmetadata fires only once per src
if (src.length > 0 && Ox.startsWith(Ox.parseURL(src).protocol, 'http')) {
src += '?' + Ox.uid();
}
return $('<video>')
.css({position: 'absolute'})
.on({
ended: function() {
if (i < item.parts - 1) {
setCurrentPart(self.currentPart + 1);
self.video.play();
} /*else if (self.isPlaylist) {
setCurrentItem(self.currentItem + 1);
self.video.play();
}*/ else {
self.ended = true;
self.paused = true;
that.triggerEvent('ended');
}
},
loadedmetadata: function() {
item.durations[i] = item.videos[i].duration;
if (self.isPlaylist && i == 0) {
item.videos[0].currentTime = item.points[0];
}
if (Ox.every(item.durations)) {
item.duration = Ox.sum(item.durations);
item.offsets = Ox.range(item.parts).map(function(i) {
return Ox.sum(item.durations.slice(0, i));
});
//Ox.Log('Video', 'METADATA OF', src, 'LOADED', item)
if (self.isPlaylist) {
callback && callback();
} else {
setCurrentItem(0);
self.loadedMetadata = true;
that.triggerEvent('loadedmetadata');
}
}
},
progress: function() {
// not implemented
},
seeked: function() {
that.triggerEvent('seeked'); that.triggerEvent('seeked');
}, self.seeking = false;
seeking: function() { }
that.triggerEvent('seeking'); },
}, seeking: function() {
stop: function() { //seeking event triggered in setCurrentTime
// custom event to be triggered on removal from the DOM },
if (self.video) { stop: function() {
self.video.pause(); if (self.video == this) {
} self.video.pause();
that.triggerEvent('ended'); that.triggerEvent('ended');
} }
}) },
.attr(Ox.extend({ timeupdate: function() {
preload: 'metadata', if (self.video == this) {
src: src if (self.items[self.currentItem].out && this.currentTime >= self.items[self.currentItem].out) {
}, i == 0 && self.options.autoplay ? { setCurrentItem(self.currentItem + 1);
autoplay: 'autoplay' }
} : {})) }
.hide() }
.appendTo(that);
});
item.videos = item.$videos.map(function($video) {
return $video[0];
});
self.$brightness = $('<div>').css({
width: '100%',
height: '100%',
background: 'rgb(0, 0, 0)',
opacity: 0
}) })
.attr({
preload: 'auto'
})
.hide()
.appendTo(that); .appendTo(that);
return item;
} }
function loadPage(page, callback) { function loadItems(callback) {
Ox.Log('Video', 'VIDEO loadPage', page); var currentTime = 0, i =0,
//page = Ox.mod(page, self.numberOfPages); items = self.options.items.map(function(item) {
var loadedmetadata = 0, return Ox.isObject(item) ? Ox.clone(item, true) : {src: item};
start = page * self.pageLength,
stop = Math.min(start + self.pageLength, self.numberOfItems),
pageLength = stop - start;
if (!self.items[start]) {
self.options.src([start, stop], function(data) {
data.forEach(function(data, i) {
self.items[start + i] = loadItem(data.parts, data.points, function(item) {
if (++loadedmetadata == pageLength) {
Ox.Log('Video', 'VIDEO page', page, 'loaded');
callback && callback();
}
});
});
}); });
} else {
Ox.Log('Video', 'PAGE IN CACHE'); next();
callback && callback();
function next() {
var item;
if (i < items.length) {
item = items[i];
item['in'] = item['in'] || 0;
item.offset = currentTime;
if (item.out) {
item.duration = item.out - item['in'];
}
if (item.duration) {
if(!item.out) {
item.out = item.duration - item['in'];
}
console.log('add item', item);
currentTime += item.duration;
i++;
next()
} else {
Ox.Log('VIDEO', 'getVideoInfo', item.src);
Ox.getVideoInfo(item.src, function(info) {
item.duration = info.duration;
if(!item.out) {
item.out = item['in'] + item.duration;
}
currentTime += item.duration;
i++;
next();
});
}
} else {
self.loadedMetadata = true;
self.items = items;
self.numberOfItems = self.items.length;
callback && callback();
onLoadedMetadata(self.$video, function() {
that.triggerEvent('loadedmetadata');
});
}
} }
} }
function loadPages(callback) { function loadNextVideo() {
var currentPage = self.currentPage, var item = self.items[self.currentItem],
nextPage = Ox.mod(currentPage + 1, self.numberOfPages), nextItem = Ox.mod(self.currentItem + 1, self.numberOfItems),
previousPage = Ox.mod(currentPage - 1, self.numberOfPages); next = self.items[nextItem],
loadPage(currentPage, function() { $nextVideo = self.$videos[Ox.mod(self.currentVideo + 1, self.$videos.length)],
if (nextPage != currentPage) { nextVideo = $nextVideo[0];
loadPage(nextPage, function() {
if (previousPage != currentPage && previousPage != nextPage) { nextVideo.src = next.src;
unloadPage(previousPage); onLoadedMetadata($nextVideo, function() {
} nextVideo.currentTime = next['in'] || 0;
});
}
callback && callback();
}); });
} }
function onLoadedMetadata($video, callback) {
if ($video[0].readyState) {
callback();
} else {
$video.one('loadedmetadata', function(event) {
callback();
});
}
}
function setCurrentItem(item) { function setCurrentItem(item) {
Ox.Log('Video', 'scI', item); Ox.Log('Video', 'sCI', item, self.numberOfItems);
var interval; var interval;
item = Ox.mod(item, self.numberOfItems); if(item >= self.numberOfItems) {
self.video && self.video.pause(); if (self.options.loop) {
if (self.items[item]) { item = Ox.mod(item, self.numberOfItems);
set(); } else {
} else { self.ended = true;
that.triggerEvent('seeking'); self.paused = true;
interval = setInterval(function() { self.video && self.video.pause();
Ox.Log('Video', 'ITEM', item, 'NOT AVAILABLE'); that.triggerEvent('ended');
if (self.items[item]) { return;
clearInterval(interval); }
that.triggerEvent('seeked');
set();
}
}, 250);
} }
self.video && self.video.pause();
//fixme always sync now?
set();
function set() { function set() {
self.currentItem = item; self.currentItem = item;
setCurrentPart(self.currentPart); setCurrentVideo();
if (self.isPlaylist) { onLoadedMetadata(self.$video, function() {
that.triggerEvent('pointschange');
that.triggerEvent('sizechange'); that.triggerEvent('sizechange');
self.currentPage = getCurrentPage(); that.triggerEvent('itemchange', {
loadPages(); item: self.currentItem
} });
});
} }
} }
function setCurrentPart(part) { function setCurrentVideo() {
Ox.Log('Video', 'sCP', part);
var css = {}, var css = {},
muted = false, muted = false,
volume = 1; volume = 1,
item = self.items[self.currentItem],
next;
Ox.Log('Video', 'sCV', item);
['left', 'top', 'width', 'height'].forEach(function(key) { ['left', 'top', 'width', 'height'].forEach(function(key) {
css[key] = self.$video.css(key); css[key] = self.$videos[self.currentVideo].css(key);
}); });
if (self.video) { if (self.video) {
self.$video.hide(); self.$videos[self.currentVideo].hide();
self.video.pause(); self.video.pause();
if(self.video.readyState >= self.video.HAVE_METADATA) {
self.video.currentTime = 0;
}
volume = self.video.volume; volume = self.video.volume;
muted = self.video.muted; muted = self.video.muted;
} }
self.$video = self.items[self.currentItem].$videos[part].css(css).show(); self.currentVideo = Ox.mod(self.currentVideo + 1, self.$videos.length);
self.$video = self.$videos[self.currentVideo];
self.video = self.$video[0]; self.video = self.$video[0];
if (self.$video.attr('src') != item.src) {
self.video.src = item.src;
}
self.video.volume = volume; self.video.volume = volume;
self.video.muted = muted; self.video.muted = muted;
self.$video.css(css).show();
!self.paused && self.video.play(); !self.paused && self.video.play();
self.currentPart = part; Ox.Log('Video', 'sCV', self.video.src);
Ox.Log('Video', 'sCP', part, self.video.src); if (item['in']) {
setCurrentItemTime(item['in']);
}
loadNextVideo();
}
function setCurrentItemTime(currentTime) {
Ox.Log('Video', 'sCIT', currentTime);
onLoadedMetadata(self.$video, function() {
self.video.currentTime = currentTime;
});
} }
function setCurrentTime(time) { function setCurrentTime(time) {
Ox.Log('Video', 'sCT', time); Ox.Log('Video', 'sCT', time);
var currentPart, currentTime, var currentTime, currentItem;
item = self.items[self.currentItem]; self.items.forEach(function(item, i) {
Ox.loop(item.parts - 1, -1, -1, function(i) { if (time >= item.offset
if (item.offsets[i] <= time) { && time < item.offset + item.duration) {
currentPart = i; currentItem = i;
currentTime = time - item.offsets[i]; currentTime = time - item.offset + item['in'];
return false; // break return false;
} }
}); });
Ox.Log('Video', 'sCT', time, currentPart, currentTime); // Set to end of items if time > duration
if (currentPart != self.currentPart) { if(Ox.isUndefined(currentItem) && Ox.isUndefined(currentTime)) {
setCurrentPart(currentPart); currentItem = self.items.length -1;
currentTime = self.items[currentItem].duration + self.items[currentItem]['in'];
} }
if (self.video && self.video.readyState) { Ox.Log('Video', 'sCT', time, '=>', currentItem, currentTime);
self.video.currentTime = currentTime; if (currentItem != self.currentItem) {
setCurrentItem(currentItem);
} }
} self.seeking = true;
self.currentTime = time;
function unloadPage(page) { that.triggerEvent('seeking');
//page = Ox.mod(page, self.numberOfPages); setCurrentItemTime(currentTime);
Ox.Log('Video', 'unloadPage', page);
var start = page * self.pageLength,
stop = Math.min(start + self.pageLength, self.numberOfItems);
Ox.range(start, stop).forEach(function(i) {
if (self.items[i]) {
self.items[i].$videos.forEach(function($video) {
$video[0].src = '';
$video.remove();
});
delete self.items[i];
}
});
} }
/*@ /*@
@ -352,18 +347,7 @@ Ox.VideoElement = function(options, self) {
css <f> css css <f> css
@*/ @*/
that.css = function() { that.css = function() {
var interval; self.$video.css.apply(self.$video, arguments);
if (self.$video) {
self.$video.css.apply(self.$video, arguments);
} else {
interval = setInterval(function() {
Ox.Log('Video', 'VIDEO NOT YET AVAILABLE');
if (self.$video) {
clearInterval(interval);
self.$video.css.apply(self.$video, arguments);
}
}, 250);
}
return that; return that;
}; };
@ -371,8 +355,9 @@ Ox.VideoElement = function(options, self) {
duration <f> duration duration <f> duration
@*/ @*/
that.duration = function() { that.duration = function() {
// 86399 return self.items ? Ox.sum(self.items.map(function(item) {
return self.items[self.currentItem].duration; return item.duration;
})) : NaN;
}; };
/*@ /*@
@ -382,13 +367,6 @@ Ox.VideoElement = function(options, self) {
return getset('muted', arguments[0]); return getset('muted', arguments[0]);
}; };
/*@
points <f> get points
@*/
that.points = function() {
return self.items[self.currentItem].points;
};
/*@ /*@
pause <f> pause pause <f> pause
@*/ @*/
@ -404,8 +382,8 @@ Ox.VideoElement = function(options, self) {
that.play = function() { that.play = function() {
if (self.ended) { if (self.ended) {
that.currentTime(0); that.currentTime(0);
self.ended = false;
} }
self.ended = false;
self.paused = false; self.paused = false;
self.video.play(); self.video.play();
return that; return that;
@ -424,54 +402,9 @@ Ox.VideoElement = function(options, self) {
playPrevious <f> play previous playPrevious <f> play previous
@*/ @*/
that.playPrevious = function() { that.playPrevious = function() {
Ox.Log('Video', 'PLAY PREVIOUS');
setCurrentItem(self.currentItem - 1); setCurrentItem(self.currentItem - 1);
}; self.video.play();
/*@
src <f> get/set source
@*/
that.src = function() {
var ret, src;
if (arguments.length == 0) {
ret = self.video.src;
} else {
self.options.src = Ox.isArray(arguments[0]) ? arguments[0] : [arguments[0]];
if (self.loadedMetadata) {
//fixme: get rid of this to make use of browser caching
// but in all browsers except firefox,
// loadedmetadata fires only once per src
src = self.options.src[self.currentPart];
if (src.length > 0 && Ox.startsWith(Ox.parseURL(src).protocol, 'http')) {
src += '?' + Ox.uid();
}
self.$video[0].src = src;
self.items[0].$videos.forEach(function($video, i) {
if (i != self.currentPart) {
var src = self.options.src[i];
//fixme: get rid of this to make use of browser caching
// but in all browsers except firefox,
// loadedmetadata fires only once per src
if (src.length > 0 && Ox.startsWith(Ox.parseURL(src).protocol, 'http')) {
src += '?' + Ox.uid();
}
$video[0].src = src;
}
});
} else {
self.items[0].$videos.forEach(function($video, i) {
var src = self.options.src[i];
//fixme: get rid of this to make use of browser caching
// but in all browsers except firefox,
// loadedmetadata fires only once per src
if (src.length > 0 && Ox.startsWith(Ox.parseURL(src).protocol, 'http')) {
src += '?' + Ox.uid();
}
$video[0].src = src;
});
}
ret = that;
}
return ret;
}; };
/*@ /*@

View file

@ -400,7 +400,7 @@ Ox.VideoPlayer = function(options, self) {
// and poster doesn't seem to work at all // and poster doesn't seem to work at all
Ox.extend({ Ox.extend({
preload: self.options.preload, preload: self.options.preload,
src: self.video items: self.video,
}, !self.options.paused && !self.options.playInToOut ? { }, !self.options.paused && !self.options.playInToOut ? {
/*autoplay: 'autoplay'*/ /*autoplay: 'autoplay'*/
} : {}/*, self.options.poster ? { } : {}/*, self.options.poster ? {
@ -410,7 +410,7 @@ Ox.VideoPlayer = function(options, self) {
.bindEvent(Ox.extend({ .bindEvent(Ox.extend({
ended: ended, ended: ended,
loadedmetadata: loadedmetadata, loadedmetadata: loadedmetadata,
pointschange: pointschange, itemchange: itemchange,
seeked: seeked, seeked: seeked,
seeking: seeking, seeking: seeking,
sizechange: sizechange sizechange: sizechange
@ -1780,7 +1780,7 @@ Ox.VideoPlayer = function(options, self) {
//self.options.position = Ox.limit(self.options.position, self['in'], self.out); //self.options.position = Ox.limit(self.options.position, self['in'], self.out);
//self.$video.currentTime(self.options.position); //self.$video.currentTime(self.options.position);
setPosition(self.options.position); !self.isPlaylist && setPosition(self.options.position);
self.$video.muted(self.options.muted).volume(self.options.volume); self.$video.muted(self.options.muted).volume(self.options.volume);
if (!self.options.paused) { if (!self.options.paused) {
@ -1859,13 +1859,9 @@ Ox.VideoPlayer = function(options, self) {
} }
} }
function pointschange() { function itemchange(data) {
var points = self.$video.points(); var item = self.$video.options('items')[data.item];
self['in'] = points[0]; Ox.Log('Video', 'ITEMCHANGE', item);
self.out = points[1];
self.options.duration = self.out - self['in'];
setPosition(self['in']);
Ox.Log('Video', 'POINTSCHANGE', self['in'], self.out, self.options.position, self.options.duration)
} }
function progress() { function progress() {
@ -2104,7 +2100,9 @@ Ox.VideoPlayer = function(options, self) {
} }
self.loadedMetadata = false; self.loadedMetadata = false;
showLoadingIcon(); showLoadingIcon();
self.$video.src(self.options.video[self.options.resolution]); self.$video.options({
items: self.options.video[self.options.resolution]
});
self.$playButton && self.$playButton.options({disabled: true}); self.$playButton && self.$playButton.options({disabled: true});
that.triggerEvent('resolution', { that.triggerEvent('resolution', {
resolution: self.options.resolution resolution: self.options.resolution