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 || {});
self.options.src = Ox.isArray(self.options.src) ? self.options.src : [self.options.src];
Ox.print('VIDEO ELEMENT OPTIONS', self.options)
self.currentPart = 0;
self.duration = 0;
self.durations = self.options.src.map(function() {
return 0;
});
self.offsets = [];
self.parts = self.options.src.length;
self.items = [];
self.paused = true;
self.$video = $('<div>');
self.$videos = self.options.src.map(function(src, i) {
return $('<video>')
.css({position: 'absolute'})
.bind({
ended: function(e) {
alert(e);
if (i < self.parts - 1) {
setCurrentPart(self.currentPart + 1);
self.video.play();
} else {
self.ended = true;
self.paused = true;
that.triggerEvent('ended');
}
},
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');
}
},
progress: function() {
},
seeked: function() {
that.triggerEvent('seeked');
},
seeking: function() {
that.triggerEvent('seeking');
},
stop: function() {
self.video.pause();
that.triggerEvent('ended');
if (Ox.isFunction(self.options.src)) {
self.isPlaylist = true;
self.currentItem = 0;
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.print('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
}
})
.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];
});
});
} else {
self.numberOfItems = 1;
self.items.push(loadItem(self.options.src));
}
function getCurrentPage() {
return Math.floor(self.currentItem / self.pageLength);
}
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) {
@ -95,15 +60,172 @@ Ox.VideoElement = function(options, self) {
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) {
Ox.print('sCP', part);
var css = {};
['left', 'top', 'width', 'height'].forEach(function(key) {
css[key] = self.$video.css(key);
});
self.$video.hide();
self.video.pause();
self.$video = self.$videos[part].css(css).show();
if (self.video) {
self.video.pause();
self.$video.hide();
}
self.$video = self.items[self.currentItem].$videos[part].css(css).show();
self.video = self.$video[0];
!self.paused && self.video.play();
self.currentPart = part;
@ -112,11 +234,12 @@ Ox.VideoElement = function(options, self) {
function setCurrentTime(time) {
Ox.print('sCT', time);
var currentPart, currentTime;
Ox.loop(self.parts - 1, -1, -1, function(i) {
if (self.offsets[i] <= time) {
var currentPart, currentTime,
item = self.items[self.currentItem];
Ox.loop(item.parts - 1, -1, -1, function(i) {
if (item.offsets[i] <= time) {
currentPart = i;
currentTime = time - self.offsets[i];
currentTime = time - item.offsets[i];
return false;
}
});
@ -127,6 +250,22 @@ Ox.VideoElement = function(options, self) {
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() {
self.$video.animate.apply(self.$video, arguments);
return that;
@ -149,18 +288,34 @@ Ox.VideoElement = function(options, self) {
};
that.css = function() {
self.$video.css.apply(self.$video, arguments);
var interval;
if (self.$video) {
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;
};
that.duration = function() {
return self.duration;
// 86399
return self.items[self.currentItem].duration;
};
that.muted = function() {
return getset('muted', arguments[0]);
};
that.points = function() {
return self.items[self.currentItem].points;
};
that.pause = function() {
self.paused = true;
self.video.pause();
@ -177,6 +332,16 @@ Ox.VideoElement = function(options, self) {
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() {
var ret;
if (arguments.length == 0) {

View file

@ -10,12 +10,12 @@ Ox.VideoPlayer <f> Generic Video Player
in <n> In point (sec)
out <n> Out point (sec)
text <s> Text
# fixme: documentation is wrong
controls <[[s]]|[[][]]> Controls, first top, then bottom, from left to right
Can be 'fullscreen', 'scale', 'title', 'find', 'menu',
'play', 'playInToOut', 'mute', 'volume', 'size', 'timeline', 'space',
controlsBottom <[s]|[]> Bottom controls, from left to right
Can be 'fullscreen', 'scale', 'title', 'find', 'menu', 'play', 'playInToOut',
'previous', 'next', 'mute', 'volume', 'size', 'timeline', 'space',
'position', 'resolution', 'settings'. The 'space' control is just
empty space that separates left-aligned from right-aligned controls.
controlsTop <[s]|[]> Top controls, from left to right
duration <n|-1> Duration (sec)
enableFind <b|false> If true, enable find
enableFullscreen <b|false> If true, enable fullscreen
@ -140,6 +140,7 @@ Ox.VideoPlayer = function(options, self) {
}
self.video = self.options.video[self.options.resolution];
} else {
self.isPlaylist = Ox.isFunction(self.options.video);
self.video = self.options.video;
}
@ -284,8 +285,10 @@ Ox.VideoPlayer = function(options, self) {
.bindEvent(Ox.extend({
ended: ended,
loadedmetadata: loadedmetadata,
pointschange: pointschange,
seeked: seeked,
seeking: seeking
seeking: seeking,
sizechange: sizechange
}, self.options.progress ? {
progress: progress
} : {}))
@ -516,6 +519,22 @@ Ox.VideoPlayer = function(options, self) {
})
.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') {
self.$playButton = Ox.Button({
@ -609,6 +628,22 @@ Ox.VideoPlayer = function(options, self) {
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') {
self.$resolutionButton = Ox.Element({
@ -801,7 +836,7 @@ Ox.VideoPlayer = function(options, self) {
.html('0')
.appendTo(self.$find);
self.$previousButton = Ox.Button({
self.$previousResultButton = Ox.Button({
disabled: true,
style: 'symbol',
title: 'arrowLeft',
@ -816,7 +851,7 @@ Ox.VideoPlayer = function(options, self) {
})
.appendTo(self.$find);
self.$nextButton = Ox.Button({
self.$nextResultButton = Ox.Button({
disabled: true,
style: 'symbol',
title: 'arrowRight',
@ -1316,6 +1351,28 @@ Ox.VideoPlayer = function(options, self) {
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() {
that.triggerEvent('gotopoint');
}
@ -1416,10 +1473,10 @@ Ox.VideoPlayer = function(options, self) {
});
self.out = self.options.playInToOut &&
self.options.out < self.$video.duration() ?
self.options.out : self.$video.duration();
self.out < self.$video.duration() ?
self.out : self.$video.duration();
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.$video.currentTime(self.options.position);
@ -1475,13 +1532,17 @@ Ox.VideoPlayer = function(options, self) {
function playing() {
self.options.position = self.$video.currentTime();
if (
(self.options.playInToOut || self.playInToOut) &&
self.options.position >= self.options.out
(self.options.playInToOut || self.playInToOut || self.isPlaylist) &&
self.options.position >= self.out
) {
togglePaused();
setPosition(self.options.out/*, 'video'*/);
//ended();
self.playInToOut = false;
if (self.isPlaylist) {
self.$video.playNext();
} else {
togglePaused();
setPosition(self.out/*, 'video'*/);
//ended();
self.playInToOut = false;
}
} else {
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() {
var buffered = self.$video.buffered();
for (var i = 0; i < buffered.length; i++) {
@ -1555,7 +1625,7 @@ Ox.VideoPlayer = function(options, self) {
}
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(
position * self.options.fps
@ -1686,6 +1756,13 @@ Ox.VideoPlayer = function(options, self) {
//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) {
showVolume();
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);
if (self.$find) {
self.$results.html(self.results.length);
self.$previousButton.options({
self.$previousResultButton.options({
disabled: self.results.length <= 1
});
self.$nextButton.options({
self.$nextResultButton.options({
disabled: self.results.length <= 1
});
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() {
self.$positionInput.hide();
self.$position.html('').show();