eda08662 by Tom Johnson Committed by David LaPalomento

toms working example base

1 parent 3b271282
......@@ -25,9 +25,11 @@
<!-- m3u8 handling -->
<script src="src/m3u8/m3u8.js"></script>
<script src="src/m3u8/m3u8-parser.js"></script>
<script src="src/m3u8/m3u8-tag-types.js"></script>
<script src="src/m3u8/m3u8-parser.js"></script>
<script src="src/manifest-controller.js"></script>
<script src="src/segment-controller.js"></script>
<script src="src/hls-playback-controller.js"></script>
<!-- example MPEG2-TS segments -->
<!-- bipbop -->
......@@ -48,51 +50,12 @@
// initialize the player
videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf';
video = videojs('video');
// create a media source
mediaSource = new videojs.MediaSource();
mediaSource.addEventListener('sourceopen', function(event){
var
parser = new videojs.hls.SegmentParser(),
sourceBuffer = mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"');
everything = [];
// feed parsed bytes into the player
everything.push(parser.getFlvHeader());
sourceBuffer.appendBuffer(everything[everything.length - 1], video);
parser.parseSegmentBinaryData(window.bcSegment);
while (parser.tagsAvailable()) {
everything.push(parser.getNextTag().bytes);
sourceBuffer.appendBuffer(everything[everything.length - 1], video);
}
parser.flushTags();
while (parser.tagsAvailable()) {
everything.push(parser.getNextTag().bytes);
sourceBuffer.appendBuffer(everything[everything.length - 1], video);
}
var iframe = document.createElement('iframe');
iframe.src = 'data:video/x-flv;base64,' + window.btoa((Array.prototype.map.call(everything.reduce(function(result, next) {
var array = new Uint8Array(result.byteLength + next.byteLength);
array.set(result);
array.set(next, result.length);
return array;
}), function(byte) {
return String.fromCharCode(byte);
})).join(''));
//console.log(iframe);
// document.body.appendChild(iframe);
}, false);
url = videojs.URL.createObjectURL(mediaSource);
video.src({
src: url,
type: "video/flv"
video = videojs('video',{},function(){
this.playbackController = new window.videojs.hls.HLSPlaybackController(this);
this.playbackController.loadManifest("http://localhost:7070/test/basic-playback/zencoder/gogo/manifest.m3u8",function(data){console.log(data)});
});
</script>
</body>
</html>
......
(function (window) {
window.videojs.hls.HLSPlaybackController = function (vjsPlayerReference) {
var ManifestController = window.videojs.hls.ManifestController;
var SegmentController = window.videojs.hls.SegmentController;
var MediaSource = window.videojs.MediaSource;
var SegmentParser = window.videojs.hls.SegmentParser;
var M3U8 = window.videojs.hls.M3U8;
var self = this;
self.player = vjsPlayerReference;
self.mediaSource = new MediaSource();
self.parser = new SegmentParser();
self.manifestController = null;
self.segmentController = null;
self.manifestLoaded = false;
self.currentSegment = 0;
self.currentManifest = null;
self.currentPlaylist = null;
self.currentRendition = null;
// Register Externall Callbacks
self.manifestLoadCompleteCallback;
self.player.on('timeupdate', function () {
console.log(self.player.currentTime());
});
self.player.on('onsrcchange', function () {
console.log('src change', self.player.currentSrc());
//if src.url.m3u8 -- loadManifest.url
});
self.rendition = function (rendition) {
self.currentRendition = rendition;
self.loadManifest(self.currentRendition.url, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update);
};
self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) {
self.mediaSource.addEventListener('sourceopen', function (event) {
console.log('source open here');
// feed parsed bytes into the player
self.sourceBuffer = self.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"');
self.parser = new SegmentParser();
self.sourceBuffer.appendBuffer(self.parser.getFlvHeader(), video);
if( onDataCallback )
{
self.manifestLoadCompleteCallback = onDataCallback;
}
self.manifestController = new ManifestController();
self.manifestController.loadManifest(manifestUrl, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update);
}, false);
self.player.src({
src: videojs.URL.createObjectURL(self.mediaSource),
type: "video/flv"
});
};
self.onM3U8LoadComplete = function (m3u8) {
if (m3u8.invalidReasons.length == 0) {
if(m3u8.isPlaylist)
{
self.currentPlaylist = m3u8;
self.rendition(self.currentPlaylist.playlistItems[0]);
} else {
self.currentManifest = m3u8;
self.manifestLoaded = true;
self.loadSegment(self.currentManifest.mediaItems[0]);
if(self.manifestLoadCompleteCallback)
{
self.manifestLoadCompleteCallback(m3u8);
}
}
}
};
self.onM3U8LoadError = function (error) {
};
self.onM3U8Update = function (m3u8) {
};
self.loadSegment = function(segment) {
self.segmentController = new SegmentController();
self.segmentController.loadSegment(segment.url, self.onSegmentLoadComplete, self.onSegmentLoadError);
};
self.onSegmentLoadComplete = function (segment) {
self.parser.parseSegmentBinaryData(segment.binaryData);
while (self.parser.tagsAvailable()) {
self.sourceBuffer.appendBuffer(self.parser.getNextTag().bytes, self.player);
};
console.log('load another',self.currentSegment,self.currentManifest.mediaItems.length);
if(self.currentSegment < self.currentManifest.mediaItems.length-1)
{
console.log('load another');
self.loadNextSegment();
}
};
self.loadNextSegment = function () {
self.currentSegment++;
self.loadSegment(self.currentManifest.mediaItems[self.currentSegment]);
}
self.onSegmentLoadError = function (error) {
};
};
})(this);
\ No newline at end of file
......@@ -4,6 +4,8 @@
window.videojs.hls.M3U8Parser = function() {
var self = this;
self.directory;
var tagTypes = window.videojs.hls.m3u8TagType;
var lines = [];
var data;
......@@ -31,6 +33,11 @@
self.parse = function( rawDataString ) {
data = new M3U8();
if(self.directory)
{
data.directory = self.directory;
}
if( rawDataString != undefined && rawDataString.toString().length > 0 )
{
lines = rawDataString.split('\n');
......@@ -72,6 +79,15 @@
segment.url = lines[index+1];
}
if(segment.url.indexOf("http")===-1 && self.directory)
{
if(data.directory[data.directory.length-1] === segment.url[0] && segment.url[0] === "/")
{
segment.url = segment.url.substr(1);
}
segment.url = self.directory + segment.url;
}
data.mediaItems.push(segment);
break;
......@@ -116,7 +132,7 @@
break;
case tagTypes.ZEN_TOTAL_DURATION:
data.totalDuration = self.getTagValue(value);
data.totalDuration = Number(self.getTagValue(value));
break;
case tagTypes.VERSION:
......
(function(window) {
window.videojs.hls.M3U8 = function() {
this.directory = "";
this.allowCache = "NO";
this.playlistItems = [];
this.mediaItems = [];
......
(function(window) {
(function (window) {
var M3U8 = window.videojs.hls.M3U8;
var M3U8Parser = window.videojs.hls.M3U8Parser;
window.videojs.hls.ManifestController = function(){
window.videojs.hls.ManifestController = function () {
var self = this;
var parser;
var data;
var onDataCallback;
var onErrorCallback;
var onUpdateCallback;
self.parser;
self.data;
self.url;
self.onDataCallback;
self.onErrorCallback;
self.onUpdateCallback;
self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) {
self.url = manifestUrl;
self.loadManifest = function ( manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback ) {
if (onDataCallback) {
self.onDataCallback = onDataCallback;
}
if (onErrorCallback) {
self.onErrorCallback = onErrorCallback;
}
if (onUpdateCallback) {
self.onUpdateCallback = onUpdateCallback;
}
vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError);
};
self.parseManifest = function ( dataAsString ) {
self.parseManifest = function (dataAsString) {
self.parser = new M3U8Parser();
self.data = self.parser.parse( dataAsString );
self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1];
self.data = self.parser.parse(dataAsString);
return self.data;
};
self.onManifestLoadComplete = function(response) {
self.onManifestLoadComplete = function (response) {
var output = self.parseManifest(response);
if(self.onDataCallback != undefined)
{
if (self.onDataCallback != undefined) {
self.onDataCallback(output);
}
};
self.onManifestLoadError = function(err) {
if(err)
{
console.log(err.message);
}
if(self.onErrorCallback != undefined)
{
onErrorCallback((err != undefined) ? err : null);
self.onManifestLoadError = function (err) {
if (self.onErrorCallback != undefined) {
self.onErrorCallback((err != undefined) ? err : null);
}
};
}
......
(function(window) {
var SegmentParser = window.videojs.hls.SegmentParser;
window.videojs.hls.SegmentController = function(){
var self = this;
var url;
var parser;
var requestTimestamp;
var responseTimestamp;
var data;
var onDataCallback;
var onErrorCallback;
var onUpdateCallback;
self.url;
self.requestTimestamp;
self.responseTimestamp;
self.data;
self.onDataCallback;
self.onErrorCallback;
self.onUpdateCallback;
self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) {
self.url = segmentUrl;
......@@ -30,21 +29,23 @@
};
req.send(null);
//vjs.get(segmentUrl, self.onSegmentLoadComplete, self.onSegmentLoadError);
};
self.parseSegment = function ( incomingData ) {
// Add David's code later //
console.log('got segment data', incomingData.byteLength);
self.data = {
whatever: incomingData
};
self.data = {};
self.data.binaryData = incomingData;
self.data.url = self.url;
self.data.isCached = false;
self.data.requestTimestamp = self.requestTimestamp;
self.data.responseTimestamp = self.responseTimestamp;
self.data.byteLength = incomingData.byteLength;
self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 );
self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp)
self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp);
return self.data;
};
......
(function(window) {
(function (window) {
/*
======== A Handy Little QUnit Reference ========
http://api.qunitjs.com/
......@@ -38,18 +38,18 @@
module('environment');
test('is sane', function() {
test('is sane', function () {
expect(1);
ok(true);
});
module('segment parser', {
setup: function() {
setup: function () {
parser = new window.videojs.hls.SegmentParser();
}
});
test('creates an flv header', function() {
test('creates an flv header', function () {
var header = Array.prototype.slice.call(parser.getFlvHeader());
ok(header, 'the header is truthy');
equal(9 + 4, header.length, 'the header length is correct');
......@@ -60,7 +60,7 @@
deepEqual(expectedHeader, header, 'the rest of the header is correct');
});
test('parses the first bipbop segment', function() {
test('parses the first bipbop segment', function () {
var tag, bytes, i;
parser.parseSegmentBinaryData(window.bcSegment);
......@@ -70,7 +70,7 @@
'aac tags:', parser.stats.aacTags());
});
testAudioTag = function(tag) {
testAudioTag = function (tag) {
var
byte = tag.bytes[11],
format = (byte & 0xF0) >>> 4,
......@@ -87,7 +87,7 @@
ok(aacPacketType === 0 || aacPacketType === 1, 'aac packets should have a valid type');
};
testVideoTag = function(tag) {
testVideoTag = function (tag) {
var
byte = tag.bytes[11],
frameType = (byte & 0xF0) >>> 4,
......@@ -120,7 +120,7 @@
}
};
testNalUnit = function(bytes) {
testNalUnit = function (bytes) {
var
nalHeader = bytes[0],
unitType = nalHeader & 0x1F;
......@@ -132,7 +132,7 @@
};
asciiFromBytes = function(bytes) {
asciiFromBytes = function (bytes) {
var
string = [],
i = bytes.byteLength;
......@@ -143,7 +143,7 @@
return string.join('');
};
testScriptString = function(tag, offset, expected) {
testScriptString = function (tag, offset, expected) {
var type = tag.bytes[offset],
stringLength = tag.view.getUint16(offset + 1),
string,
......@@ -156,7 +156,7 @@
equal(expected, string, 'the string value is "' + expected + '"');
};
testScriptEcmaArray = function(tag, start) {
testScriptEcmaArray = function (tag, start) {
var
numItems = tag.view.getUint32(start),
i = numItems,
......@@ -190,7 +190,7 @@
equal(tag.bytes[offset + 2], 9, 'the property array terminator is valid');
};
testScriptTag = function(tag) {
testScriptTag = function (tag) {
testScriptString(tag, 11, 'onMetaData');
// the onMetaData object is stored as an 'ecma array', an array with non-
......@@ -199,7 +199,7 @@
testScriptEcmaArray(tag, 25);
};
test('the flv tags are well-formed', function() {
test('the flv tags are well-formed', function () {
var
tag,
byte,
......@@ -241,27 +241,27 @@
*/
module('m3u8 parser', {
setup: function() {
setup: function () {
m3u8parser = new window.videojs.hls.M3U8Parser();
}
});
test('should create my parser', function() {
test('should create my parser', function () {
ok(m3u8parser != undefined);
}
);
test('should successfully parse manifest data', function() {
test('should successfully parse manifest data', function () {
var parsedData = m3u8parser.parse(window.playlistData);
ok(parsedData);
}
);
test('test for expected results', function() {
test('test for expected results', function () {
var data = m3u8parser.parse(window.playlistData);
notEqual(data, null, 'data is not NULL');
equal(data.invalidReasons.length, 0,'data has 0 invalid reasons');
equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons');
equal(data.hasValidM3UTag, true, 'data has valid EXTM3U');
equal(data.targetDuration, 10, 'data has correct TARGET DURATION');
equal(data.allowCache, "NO", 'acceptable ALLOW CACHE');
......@@ -275,109 +275,109 @@
);
module('brightcove playlist', {
setup: function() {
setup: function () {
m3u8parser = new window.videojs.hls.M3U8Parser();
}
});
test('should parse a brightcove manifest data', function() {
test('should parse a brightcove manifest data', function () {
var data = m3u8parser.parse(window.brightcove_playlist_data);
ok(data);
equal(data.playlistItems.length, 4, 'Has correct rendition count');
equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' );
equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' );
equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' );
equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' );
equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct');
equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct');
equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct');
equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct');
}
);
module('manifest controller', {
setup: function() {
setup: function () {
manifestController = new window.videojs.hls.ManifestController();
this.vjsget = vjs.get;
vjs.get = function(url, success, error){
vjs.get = function (url, success, error) {
console.log(url);
success(window.brightcove_playlist_data);
};
},
teardown: function() {
teardown: function () {
vjs.get = this.vjsget;
}
});
test('should create', function() {
test('should create', function () {
ok(manifestController);
});
test('should return a parsed object', function() {
test('should return a parsed object', function () {
var data = manifestController.parseManifest(window.brightcove_playlist_data);
ok(data);
equal(data.playlistItems.length, 4, 'Has correct rendition count');
equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' );
equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' );
equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' );
equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' );
equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct');
equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct');
equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct');
equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct');
})
test('should get a manifest from hermes', function() {
test('should get a manifest from hermes', function () {
var hermesUrl = "http://localhost:7070/test/basic-playback/brightcove/16x9-master.m3u8";
manifestController.loadManifest(
hermesUrl,
function(responseData){
function (responseData) {
ok(true);
},
function(errorData){
function (errorData) {
console.log('got error data');
},
function(updateData){
function (updateData) {
console.log('got update data');
}
)
});
module('segment controller', {
setup: function() {
setup: function () {
segmentController = new window.videojs.hls.SegmentController();
this.vjsget = vjs.get;
vjs.get = function(url, success, error){
vjs.get = function (url, success, error) {
console.log('load segment url', url);
success(window.bcSegment);
};
},
teardown: function() {
teardown: function () {
vjs.get = this.vjsget;
}
});
test('should get a segment data', function() {
test('should get a segment data', function () {
ok(true);
var hermesUrl = "http://localhost:7070/test/ts-files/brightcove/s-1.ts";
segmentController.loadSegment(
hermesUrl,
function(responseData){
function (responseData) {
console.log('got response from segment controller');
ok(true);
},
function(errorData){
function (errorData) {
console.log('got error data');
},
function(updateData){
function (updateData) {
console.log('got update data');
}
)
}
)
test('bandwidth calulation test', function() {
var multiSecondData = segmentController.calculateThroughput(10000,1000,2000);
var subSecondData = segmentController.calculateThroughput(10000,1000,1500);
test('bandwidth calulation test', function () {
var multiSecondData = segmentController.calculateThroughput(10000, 1000, 2000);
var subSecondData = segmentController.calculateThroughput(10000, 1000, 1500);
equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation');
equal(subSecondData, 160000, 'SUB-Second bits per second calculation');
......