video updates (playlists)

This commit is contained in:
rlx 2011-08-20 09:48:28 +00:00
parent a7c94a8019
commit 0369eaff14
2 changed files with 334 additions and 110 deletions

View file

@ -11,77 +11,42 @@ Ox.VideoElement = function(options, self) {
}) })
.options(options || {}); .options(options || {});
self.options.src = Ox.isArray(self.options.src) ? self.options.src : [self.options.src];
Ox.print('VIDEO ELEMENT OPTIONS', self.options) Ox.print('VIDEO ELEMENT OPTIONS', self.options)
self.currentPart = 0; self.items = [];
self.duration = 0;
self.durations = self.options.src.map(function() {
return 0;
});
self.offsets = [];
self.parts = self.options.src.length;
self.paused = true; self.paused = true;
self.$video = $('<div>');
self.$videos = self.options.src.map(function(src, i) { if (Ox.isFunction(self.options.src)) {
return $('<video>') self.isPlaylist = true;
.css({position: 'absolute'}) self.currentItem = 0;
.bind({ self.currentPage = 0;
ended: function(e) { self.loadedMetadata = false;
alert(e); self.pageLength = 2;
if (i < self.parts - 1) { self.options.src(function(items) {
setCurrentPart(self.currentPart + 1); self.numberOfItems = items;
self.video.play(); self.numberOfPages = Math.ceil(self.numberOfItems / self.pageLength);
} else { loadPages(function() {
self.ended = true; Ox.print('VIDEO PAGES LOADED');
self.paused = true; setCurrentItem(0);
that.triggerEvent('ended'); if (!self.loadedMedatata) {
} self.loadedMetadata = true;
},
loadedmetadata: function() {
self.durations[i] = self.videos[i].duration;
Ox.print(i, 'lm', self.durations);
if (Ox.every(self.durations)) {
self.duration = Ox.sum(self.durations);
self.offsets = Ox.range(self.parts).map(function(i) {
return Ox.sum(Ox.sub(self.durations, 0, i));
});
Ox.print('s.o', self.offsets)
that.triggerEvent('loadedmetadata'); 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));
} }
},
progress: function() {
}, function getCurrentPage() {
seeked: function() { return Math.floor(self.currentItem / self.pageLength);
that.triggerEvent('seeked');
},
seeking: function() {
that.triggerEvent('seeking');
},
stop: function() {
self.video.pause();
that.triggerEvent('ended');
} }
})
.attr(Ox.extend({
preload: 'metadata',
src: src
}, i == 0 && self.options.autoplay ? {
autoplay: 'autoplay'
} : {}))
.hide()
.appendTo(that.$element);
});
self.videos = self.$videos.map(function($video) {
return $video[0];
});
self.$video = self.$videos[0].show();
self.video = self.$video[0];
function getCurrentTime() { function getCurrentTime() {
return self.offsets[self.currentPart] + self.video.currentTime; return self.items[self.currentItem].offsets[self.currentPart] + self.video.currentTime;
} }
function getset(key, value) { function getset(key, value) {
@ -95,15 +60,172 @@ Ox.VideoElement = function(options, self) {
return ret; return ret;
} }
function loadPage(page, callback) {
Ox.print('VIDEO loadPage', page)
//page = Ox.mod(page, self.numberOfPages);
var loadedmetadata = 0,
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.print('VIDEO page', page, 'loaded')
callback && callback();
}
});
});
});
} else {
Ox.print('PAGE IN CACHE')
callback && callback();
}
}
function loadPages(callback) {
var currentPage = self.currentPage,
nextPage = Ox.mod(currentPage + 1, self.numberOfPages),
previousPage = Ox.mod(currentPage - 1, self.numberOfPages)
loadPage(currentPage, function() {
if (nextPage != currentPage) {
loadPage(nextPage, function() {
if (previousPage != currentPage && previousPage != nextPage) {
unloadPage(previousPage);
}
});
}
callback && callback();
});
}
function loadItem(src, points, callback) {
src = Ox.isArray(src) ? src : [src];
var item = {
currentPart: 0,
duration: 0,
durations: src.map(function() {
return 0;
}),
offsets: [],
parts: src.length
};
if (points) {
item.points = points;
}
item.$videos = src.map(function(src, i) {
// in all browsers except firefox,
// loadedmetadata fires only once per src
src += '?' + Ox.uid();
return $('<video>')
.css({position: 'absolute'})
.bind({
ended: function() {
if (i < self.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(Ox.sub(item.durations, 0, i));
});
//Ox.print('METADATA OF', src, 'LOADED', item)
if (self.isPlaylist) {
callback && callback();
} else {
setCurrentItem(0);
that.triggerEvent('loadedmetadata');
}
}
},
progress: function() {
// not implemented
},
seeked: function() {
that.triggerEvent('seeked');
},
seeking: function() {
that.triggerEvent('seeking');
},
stop: function() {
// custom event to be triggered on removal from the DOM
self.video.pause();
self.video.src = '';
that.triggerEvent('ended');
}
})
.attr(Ox.extend({
preload: 'metadata',
src: src
}, i == 0 && self.options.autoplay ? {
autoplay: 'autoplay'
} : {}))
.hide()
.appendTo(that.$element);
});
item.videos = item.$videos.map(function($video) {
return $video[0];
});
return item;
}
function setCurrentItem(item) {
Ox.print('scI', item);
var interval;
item = Ox.mod(item, self.numberOfItems);
self.video && self.video.pause();
if (self.items[item]) {
set();
} else {
that.triggerEvent('seeking');
interval = setInterval(function() {
Ox.print('ITEM', item, 'NOT AVAILABLE');
if (self.items[item]) {
clearInterval(interval);
that.triggerEvent('seeked');
set();
}
}, 250);
}
function set() {
self.currentItem = item;
self.currentPart = -1;
setCurrentTime(0);
if (self.isPlaylist) {
that.triggerEvent('pointschange');
that.triggerEvent('sizechange');
self.currentPage = getCurrentPage();
loadPages();
}
}
}
function setCurrentPart(part) { function setCurrentPart(part) {
Ox.print('sCP', part); Ox.print('sCP', part);
var css = {}; var css = {};
['left', 'top', 'width', 'height'].forEach(function(key) { ['left', 'top', 'width', 'height'].forEach(function(key) {
css[key] = self.$video.css(key); css[key] = self.$video.css(key);
}); });
self.$video.hide(); if (self.video) {
self.video.pause(); self.video.pause();
self.$video = self.$videos[part].css(css).show(); self.$video.hide();
}
self.$video = self.items[self.currentItem].$videos[part].css(css).show();
self.video = self.$video[0]; self.video = self.$video[0];
!self.paused && self.video.play(); !self.paused && self.video.play();
self.currentPart = part; self.currentPart = part;
@ -112,11 +234,12 @@ Ox.VideoElement = function(options, self) {
function setCurrentTime(time) { function setCurrentTime(time) {
Ox.print('sCT', time); Ox.print('sCT', time);
var currentPart, currentTime; var currentPart, currentTime,
Ox.loop(self.parts - 1, -1, -1, function(i) { item = self.items[self.currentItem];
if (self.offsets[i] <= time) { Ox.loop(item.parts - 1, -1, -1, function(i) {
if (item.offsets[i] <= time) {
currentPart = i; currentPart = i;
currentTime = time - self.offsets[i]; currentTime = time - item.offsets[i];
return false; return false;
} }
}); });
@ -127,6 +250,22 @@ Ox.VideoElement = function(options, self) {
self.video.currentTime = currentTime; self.video.currentTime = currentTime;
} }
function unloadPage(page) {
//page = Ox.mod(page, self.numberOfPages);
Ox.print('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];
}
});
}
that.animate = function() { that.animate = function() {
self.$video.animate.apply(self.$video, arguments); self.$video.animate.apply(self.$video, arguments);
return that; return that;
@ -149,18 +288,34 @@ Ox.VideoElement = function(options, self) {
}; };
that.css = function() { that.css = function() {
var interval;
if (self.$video) {
self.$video.css.apply(self.$video, arguments); self.$video.css.apply(self.$video, arguments);
} else {
interval = setInterval(function() {
Ox.print('VIDEO NOT YET AVAILABLE');
if (self.$video) {
clearInterval(interval);
self.$video.css.apply(self.$video, arguments);
}
}, 250);
}
return that; return that;
}; };
that.duration = function() { that.duration = function() {
return self.duration; // 86399
return self.items[self.currentItem].duration;
}; };
that.muted = function() { that.muted = function() {
return getset('muted', arguments[0]); return getset('muted', arguments[0]);
}; };
that.points = function() {
return self.items[self.currentItem].points;
};
that.pause = function() { that.pause = function() {
self.paused = true; self.paused = true;
self.video.pause(); self.video.pause();
@ -177,6 +332,16 @@ Ox.VideoElement = function(options, self) {
return that; return that;
}; };
that.playNext = function() {
Ox.print('PLAY NEXT')
setCurrentItem(self.currentItem + 1);
self.video.play();
};
that.playPrevious = function() {
setCurrentItem(self.currentItem - 1);
};
that.src = function() { that.src = function() {
var ret; var ret;
if (arguments.length == 0) { if (arguments.length == 0) {

View file

@ -10,12 +10,12 @@ Ox.VideoPlayer <f> Generic Video Player
in <n> In point (sec) in <n> In point (sec)
out <n> Out point (sec) out <n> Out point (sec)
text <s> Text text <s> Text
# fixme: documentation is wrong controlsBottom <[s]|[]> Bottom controls, from left to right
controls <[[s]]|[[][]]> Controls, first top, then bottom, from left to right Can be 'fullscreen', 'scale', 'title', 'find', 'menu', 'play', 'playInToOut',
Can be 'fullscreen', 'scale', 'title', 'find', 'menu', 'previous', 'next', 'mute', 'volume', 'size', 'timeline', 'space',
'play', 'playInToOut', 'mute', 'volume', 'size', 'timeline', 'space',
'position', 'resolution', 'settings'. The 'space' control is just 'position', 'resolution', 'settings'. The 'space' control is just
empty space that separates left-aligned from right-aligned controls. empty space that separates left-aligned from right-aligned controls.
controlsTop <[s]|[]> Top controls, from left to right
duration <n|-1> Duration (sec) duration <n|-1> Duration (sec)
enableFind <b|false> If true, enable find enableFind <b|false> If true, enable find
enableFullscreen <b|false> If true, enable fullscreen enableFullscreen <b|false> If true, enable fullscreen
@ -140,6 +140,7 @@ Ox.VideoPlayer = function(options, self) {
} }
self.video = self.options.video[self.options.resolution]; self.video = self.options.video[self.options.resolution];
} else { } else {
self.isPlaylist = Ox.isFunction(self.options.video);
self.video = self.options.video; self.video = self.options.video;
} }
@ -284,8 +285,10 @@ Ox.VideoPlayer = function(options, self) {
.bindEvent(Ox.extend({ .bindEvent(Ox.extend({
ended: ended, ended: ended,
loadedmetadata: loadedmetadata, loadedmetadata: loadedmetadata,
pointschange: pointschange,
seeked: seeked, seeked: seeked,
seeking: seeking seeking: seeking,
sizechange: sizechange
}, self.options.progress ? { }, self.options.progress ? {
progress: progress progress: progress
} : {})) } : {}))
@ -516,6 +519,22 @@ Ox.VideoPlayer = function(options, self) {
}) })
.appendTo(self['$controls' + titleCase]); .appendTo(self['$controls' + titleCase]);
} else if (control == 'next') {
self.$nextClipButton = Ox.Button({
style: 'symbol',
title: 'arrowRight',
tooltip: 'Next',
type: 'image'
})
.addClass('OxVideo')
.bindEvent({
click: function() {
goToNextClip(1);
}
})
.appendTo(self['$controls' + titleCase]);
} else if (control == 'play') { } else if (control == 'play') {
self.$playButton = Ox.Button({ self.$playButton = Ox.Button({
@ -609,6 +628,22 @@ Ox.VideoPlayer = function(options, self) {
fontSize: '9px' fontSize: '9px'
}); });
} else if (control == 'previous') {
self.$previousClipButton = Ox.Button({
style: 'symbol',
title: 'arrowLeft',
tooltip: 'Previous',
type: 'image'
})
.addClass('OxVideo')
.bindEvent({
click: function() {
goToNextClip(-1);
}
})
.appendTo(self['$controls' + titleCase]);
} else if (control == 'resolution') { } else if (control == 'resolution') {
self.$resolutionButton = Ox.Element({ self.$resolutionButton = Ox.Element({
@ -801,7 +836,7 @@ Ox.VideoPlayer = function(options, self) {
.html('0') .html('0')
.appendTo(self.$find); .appendTo(self.$find);
self.$previousButton = Ox.Button({ self.$previousResultButton = Ox.Button({
disabled: true, disabled: true,
style: 'symbol', style: 'symbol',
title: 'arrowLeft', title: 'arrowLeft',
@ -816,7 +851,7 @@ Ox.VideoPlayer = function(options, self) {
}) })
.appendTo(self.$find); .appendTo(self.$find);
self.$nextButton = Ox.Button({ self.$nextResultButton = Ox.Button({
disabled: true, disabled: true,
style: 'symbol', style: 'symbol',
title: 'arrowRight', title: 'arrowRight',
@ -1316,6 +1351,28 @@ Ox.VideoPlayer = function(options, self) {
return Ox.UI.getImageURL('symbol' + symbol, 'modern'); return Ox.UI.getImageURL('symbol' + symbol, 'modern');
} }
function goToNextClip(direction) {
self.$video[direction == 1 ? 'playNext' : 'playPrevious']();
}
function goToNextResult(direction) {
var found = false,
position = 0;
direction == -1 && self.results.reverse();
Ox.forEach(self.results, function(v) {
if (direction == 1 ? v['in'] > self.options.position : v.out < self.options.position) {
position = v['in'];
found = true;
return false;
}
});
direction == -1 && self.results.reverse();
if (!found) {
position = self.results[direction == 1 ? 0 : self.results.length - 1]['in'];
}
setPosition(position + self.secondsPerFrame);
}
function goToPoint() { function goToPoint() {
that.triggerEvent('gotopoint'); that.triggerEvent('gotopoint');
} }
@ -1416,10 +1473,10 @@ Ox.VideoPlayer = function(options, self) {
}); });
self.out = self.options.playInToOut && self.out = self.options.playInToOut &&
self.options.out < self.$video.duration() ? self.out < self.$video.duration() ?
self.options.out : self.$video.duration(); self.out : self.$video.duration();
self.options.duration = self.out - self['in']; self.options.duration = self.out - self['in'];
Ox.print('----------------------------------------', self.options.position) Ox.print('---------------------------------------- POS', self.options.position)
//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);
@ -1475,13 +1532,17 @@ Ox.VideoPlayer = function(options, self) {
function playing() { function playing() {
self.options.position = self.$video.currentTime(); self.options.position = self.$video.currentTime();
if ( if (
(self.options.playInToOut || self.playInToOut) && (self.options.playInToOut || self.playInToOut || self.isPlaylist) &&
self.options.position >= self.options.out self.options.position >= self.out
) { ) {
if (self.isPlaylist) {
self.$video.playNext();
} else {
togglePaused(); togglePaused();
setPosition(self.options.out/*, 'video'*/); setPosition(self.out/*, 'video'*/);
//ended(); //ended();
self.playInToOut = false; self.playInToOut = false;
}
} else { } else {
setPosition(self.options.position, 'video'); setPosition(self.options.position, 'video');
} }
@ -1499,6 +1560,15 @@ Ox.VideoPlayer = function(options, self) {
} }
} }
function pointschange() {
var points = self.$video.points();
self['in'] = points[0];
self.out = points[1];
self.options.duration = self.out - self['in'];
setPosition(self['in']);
Ox.print('POINTSCHANGE', self['in'], self.out, self.options.position, self.options.duration)
}
function progress() { function progress() {
var buffered = self.$video.buffered(); var buffered = self.$video.buffered();
for (var i = 0; i < buffered.length; i++) { for (var i = 0; i < buffered.length; i++) {
@ -1555,7 +1625,7 @@ Ox.VideoPlayer = function(options, self) {
} }
function setPosition(position, from) { function setPosition(position, from) {
self.options.position = Ox.limit(position, self['in'], self['out']); self.options.position = Ox.limit(position, self['in'], self.out);
/* /*
self.options.position = Math.round( self.options.position = Math.round(
position * self.options.fps position * self.options.fps
@ -1686,6 +1756,13 @@ Ox.VideoPlayer = function(options, self) {
//Ox.print('?!?', self.$subtitle.css('bottom'), self.$subtitle.height()) //Ox.print('?!?', self.$subtitle.css('bottom'), self.$subtitle.height())
} }
function sizechange() {
self.videoWidth = self.$video.videoWidth();
self.videoHeight = self.$video.videoHeight();
self.videoCSS = getVideoCSS();
self.$video.css(self.videoCSS);
};
function changeVolumeBy(num) { function changeVolumeBy(num) {
showVolume(); showVolume();
self.options.volume = Ox.limit(self.options.volume + num, 0, 1); self.options.volume = Ox.limit(self.options.volume + num, 0, 1);
@ -1781,10 +1858,10 @@ Ox.VideoPlayer = function(options, self) {
//Ox.print('results', self.results.length); //Ox.print('results', self.results.length);
if (self.$find) { if (self.$find) {
self.$results.html(self.results.length); self.$results.html(self.results.length);
self.$previousButton.options({ self.$previousResultButton.options({
disabled: self.results.length <= 1 disabled: self.results.length <= 1
}); });
self.$nextButton.options({ self.$nextResultButton.options({
disabled: self.results.length <= 1 disabled: self.results.length <= 1
}); });
self.$clearButton.options({ self.$clearButton.options({
@ -1801,24 +1878,6 @@ Ox.VideoPlayer = function(options, self) {
} }
} }
function goToNextResult(direction) {
var found = false,
position = 0;
direction == -1 && self.results.reverse();
Ox.forEach(self.results, function(v) {
if (direction == 1 ? v['in'] > self.options.position : v['out'] < self.options.position) {
position = v['in'];
found = true;
return false;
}
});
direction == -1 && self.results.reverse();
if (!found) {
position = self.results[direction == 1 ? 0 : self.results.length - 1]['in'];
}
setPosition(position + self.secondsPerFrame);
}
function submitPositionInput() { function submitPositionInput() {
self.$positionInput.hide(); self.$positionInput.hide();
self.$position.html('').show(); self.$position.html('').show();