Merge pull request #4 from dlapalomento/feature/m3u8s
HLS playback support cleanup
Showing
19 changed files
with
305 additions
and
298 deletions
... | @@ -10,5 +10,11 @@ | ... | @@ -10,5 +10,11 @@ |
10 | "unused": true, | 10 | "unused": true, |
11 | "boss": true, | 11 | "boss": true, |
12 | "eqnull": true, | 12 | "eqnull": true, |
13 | "node": true | 13 | "node": true, |
14 | |||
15 | "camelcase": true, | ||
16 | "nonew": true, | ||
17 | "quotmark": "single", | ||
18 | "trailing": true, | ||
19 | "maxlen": 80 | ||
14 | } | 20 | } | ... | ... |
... | @@ -20,7 +20,19 @@ module.exports = function(grunt) { | ... | @@ -20,7 +20,19 @@ module.exports = function(grunt) { |
20 | stripBanners: true | 20 | stripBanners: true |
21 | }, | 21 | }, |
22 | dist: { | 22 | dist: { |
23 | src: ['src/*.js'], | 23 | src: ['src/video-js-hls.js', |
24 | 'src/flv-tag.js', | ||
25 | 'src/exp-golomb.js', | ||
26 | 'src/h264-stream.js', | ||
27 | 'src/aac-stream.js', | ||
28 | 'src/segment-parser.js', | ||
29 | 'src/segment-controller.js', | ||
30 | 'src/m3u8/m3u8.js', | ||
31 | 'src/m3u8/m3u8-tag-types.js', | ||
32 | 'src/m3u8/m3u8-parser.js', | ||
33 | 'src/manifest-controller.js', | ||
34 | 'src/segment-controller.js', | ||
35 | 'src/hls-playback-controller.js'], | ||
24 | dest: 'dist/videojs.hls.js' | 36 | dest: 'dist/videojs.hls.js' |
25 | }, | 37 | }, |
26 | }, | 38 | }, |
... | @@ -53,7 +65,7 @@ module.exports = function(grunt) { | ... | @@ -53,7 +65,7 @@ module.exports = function(grunt) { |
53 | options: { | 65 | options: { |
54 | jshintrc: 'test/.jshintrc' | 66 | jshintrc: 'test/.jshintrc' |
55 | }, | 67 | }, |
56 | src: ['test/**/*.js', '!test/tsSegment.js'] | 68 | src: ['test/**/*.js', '!test/tsSegment.js', '!test/fixtures/*.js'] |
57 | }, | 69 | }, |
58 | }, | 70 | }, |
59 | watch: { | 71 | watch: { |
... | @@ -81,6 +93,7 @@ module.exports = function(grunt) { | ... | @@ -81,6 +93,7 @@ module.exports = function(grunt) { |
81 | grunt.loadNpmTasks('grunt-contrib-watch'); | 93 | grunt.loadNpmTasks('grunt-contrib-watch'); |
82 | 94 | ||
83 | // Default task. | 95 | // Default task. |
84 | grunt.registerTask('default', ['jshint', 'qunit', 'clean', 'concat', 'uglify']); | 96 | grunt.registerTask('default', |
97 | ['jshint', 'qunit', 'clean', 'concat', 'uglify']); | ||
85 | 98 | ||
86 | }; | 99 | }; | ... | ... |
... | @@ -25,9 +25,11 @@ | ... | @@ -25,9 +25,11 @@ |
25 | 25 | ||
26 | <!-- m3u8 handling --> | 26 | <!-- m3u8 handling --> |
27 | <script src="src/m3u8/m3u8.js"></script> | 27 | <script src="src/m3u8/m3u8.js"></script> |
28 | <script src="src/m3u8/m3u8-parser.js"></script> | ||
29 | <script src="src/m3u8/m3u8-tag-types.js"></script> | 28 | <script src="src/m3u8/m3u8-tag-types.js"></script> |
29 | <script src="src/m3u8/m3u8-parser.js"></script> | ||
30 | <script src="src/manifest-controller.js"></script> | 30 | <script src="src/manifest-controller.js"></script> |
31 | <script src="src/segment-controller.js"></script> | ||
32 | <script src="src/hls-playback-controller.js"></script> | ||
31 | 33 | ||
32 | <!-- example MPEG2-TS segments --> | 34 | <!-- example MPEG2-TS segments --> |
33 | <!-- bipbop --> | 35 | <!-- bipbop --> |
... | @@ -48,51 +50,14 @@ | ... | @@ -48,51 +50,14 @@ |
48 | 50 | ||
49 | // initialize the player | 51 | // initialize the player |
50 | videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf'; | 52 | videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf'; |
51 | video = videojs('video'); | 53 | video = videojs('video',{},function(){ |
52 | 54 | this.playbackController = new window.videojs.hls.HLSPlaybackController(this); | |
53 | // create a media source | 55 | this.playbackController.loadManifest('test/fixtures/bipbop.m3u8', function(data) { |
54 | mediaSource = new videojs.MediaSource(); | 56 | console.log(data); |
55 | mediaSource.addEventListener('sourceopen', function(event){ | 57 | }); |
56 | var | ||
57 | parser = new videojs.hls.SegmentParser(), | ||
58 | sourceBuffer = mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); | ||
59 | everything = []; | ||
60 | |||
61 | // feed parsed bytes into the player | ||
62 | everything.push(parser.getFlvHeader()); | ||
63 | sourceBuffer.appendBuffer(everything[everything.length - 1], video); | ||
64 | |||
65 | parser.parseSegmentBinaryData(window.bcSegment); | ||
66 | |||
67 | while (parser.tagsAvailable()) { | ||
68 | everything.push(parser.getNextTag().bytes); | ||
69 | sourceBuffer.appendBuffer(everything[everything.length - 1], video); | ||
70 | } | ||
71 | parser.flushTags(); | ||
72 | while (parser.tagsAvailable()) { | ||
73 | everything.push(parser.getNextTag().bytes); | ||
74 | sourceBuffer.appendBuffer(everything[everything.length - 1], video); | ||
75 | } | ||
76 | |||
77 | var iframe = document.createElement('iframe'); | ||
78 | iframe.src = 'data:video/x-flv;base64,' + window.btoa((Array.prototype.map.call(everything.reduce(function(result, next) { | ||
79 | var array = new Uint8Array(result.byteLength + next.byteLength); | ||
80 | array.set(result); | ||
81 | array.set(next, result.length); | ||
82 | return array; | ||
83 | }), function(byte) { | ||
84 | return String.fromCharCode(byte); | ||
85 | })).join('')); | ||
86 | //console.log(iframe); | ||
87 | // document.body.appendChild(iframe); | ||
88 | }, false); | ||
89 | |||
90 | url = videojs.URL.createObjectURL(mediaSource); | ||
91 | |||
92 | video.src({ | ||
93 | src: url, | ||
94 | type: "video/flv" | ||
95 | }); | 58 | }); |
96 | </script> | 59 | </script> |
60 | |||
61 | |||
97 | </body> | 62 | </body> |
98 | </html> | 63 | </html> | ... | ... |
... | @@ -25,10 +25,6 @@ window.videojs.hls.ExpGolomb = function(workingData) { | ... | @@ -25,10 +25,6 @@ window.videojs.hls.ExpGolomb = function(workingData) { |
25 | return (8 * workingBytesAvailable) + workingBitsAvailable; | 25 | return (8 * workingBytesAvailable) + workingBitsAvailable; |
26 | }; | 26 | }; |
27 | 27 | ||
28 | this.logStuff = function() { | ||
29 | console.log('bits', workingBitsAvailable, 'word', (workingWord >>> 0)); | ||
30 | }; | ||
31 | |||
32 | // ():void | 28 | // ():void |
33 | this.loadWord = function() { | 29 | this.loadWord = function() { |
34 | var | 30 | var | ... | ... |
... | @@ -47,7 +47,6 @@ hls.FlvTag = function(type, extraData) { | ... | @@ -47,7 +47,6 @@ hls.FlvTag = function(type, extraData) { |
47 | try { | 47 | try { |
48 | this.bytes.set(bytes.subarray(offset, offset + length), this.position); | 48 | this.bytes.set(bytes.subarray(offset, offset + length), this.position); |
49 | } catch(e) { | 49 | } catch(e) { |
50 | console.log(e); | ||
51 | throw e; | 50 | throw e; |
52 | } | 51 | } |
53 | this.position += length; | 52 | this.position += length; | ... | ... |
... | @@ -90,8 +90,6 @@ | ... | @@ -90,8 +90,6 @@ |
90 | } else if (0 !== sps0[offset + 0]) { | 90 | } else if (0 !== sps0[offset + 0]) { |
91 | offset += 1; | 91 | offset += 1; |
92 | } else { | 92 | } else { |
93 | console.log('found emulation bytes'); | ||
94 | |||
95 | rbsp.set([0x00, 0x00], rbspCount); | 93 | rbsp.set([0x00, 0x00], rbspCount); |
96 | spsCount += 2; | 94 | spsCount += 2; |
97 | rbspCount += 2; | 95 | rbspCount += 2; |
... | @@ -311,7 +309,6 @@ | ... | @@ -311,7 +309,6 @@ |
311 | }; | 309 | }; |
312 | 310 | ||
313 | this.finishFrame = function() { | 311 | this.finishFrame = function() { |
314 | console.log('finish frame'); | ||
315 | if (h264Frame) { | 312 | if (h264Frame) { |
316 | // Push SPS before EVERY IDR frame fo seeking | 313 | // Push SPS before EVERY IDR frame fo seeking |
317 | if (newExtraData.extraDataExists()) { | 314 | if (newExtraData.extraDataExists()) { | ... | ... |
src/hls-playback-controller.js
0 → 100644
1 | (function(window) { | ||
2 | var | ||
3 | ManifestController = window.videojs.hls.ManifestController, | ||
4 | SegmentController = window.videojs.hls.SegmentController, | ||
5 | MediaSource = window.videojs.MediaSource, | ||
6 | SegmentParser = window.videojs.hls.SegmentParser; | ||
7 | |||
8 | |||
9 | window.videojs.hls.HLSPlaybackController = function(player) { | ||
10 | |||
11 | var self = this; | ||
12 | |||
13 | self.player = player; | ||
14 | self.mediaSource = new MediaSource(); | ||
15 | self.parser = new SegmentParser(); | ||
16 | |||
17 | self.manifestLoaded = false; | ||
18 | self.currentSegment = 0; | ||
19 | |||
20 | // register external callbacks | ||
21 | self.rendition = function(rendition) { | ||
22 | self.currentRendition = rendition; | ||
23 | self.loadManifest(self.currentRendition.url, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); | ||
24 | }; | ||
25 | |||
26 | self.loadManifest = function(manifestUrl, onDataCallback) { | ||
27 | self.mediaSource.addEventListener('sourceopen', function() { | ||
28 | // feed parsed bytes into the player | ||
29 | self.sourceBuffer = self.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); | ||
30 | |||
31 | self.parser = new SegmentParser(); | ||
32 | |||
33 | self.sourceBuffer.appendBuffer(self.parser.getFlvHeader(), self.player); | ||
34 | |||
35 | if (onDataCallback) { | ||
36 | self.manifestLoadCompleteCallback = onDataCallback; | ||
37 | } | ||
38 | |||
39 | self.manifestController = new ManifestController(); | ||
40 | self.manifestController.loadManifest(manifestUrl, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update); | ||
41 | |||
42 | }, false); | ||
43 | |||
44 | self.player.src({ | ||
45 | src: window.videojs.URL.createObjectURL(self.mediaSource), | ||
46 | type: "video/flv" | ||
47 | }); | ||
48 | }; | ||
49 | |||
50 | self.onM3U8LoadComplete = function(m3u8) { | ||
51 | if (m3u8.invalidReasons.length === 0) { | ||
52 | if (m3u8.isPlaylist) { | ||
53 | self.currentPlaylist = m3u8; | ||
54 | self.rendition(self.currentPlaylist.playlistItems[0]); | ||
55 | } else { | ||
56 | self.currentManifest = m3u8; | ||
57 | self.manifestLoaded = true; | ||
58 | |||
59 | self.loadSegment(self.currentManifest.mediaItems[0]); | ||
60 | |||
61 | if (self.manifestLoadCompleteCallback) { | ||
62 | self.manifestLoadCompleteCallback(m3u8); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | }; | ||
67 | |||
68 | self.onM3U8LoadError = function() {}; | ||
69 | self.onM3U8Update = function() {}; | ||
70 | |||
71 | self.loadSegment = function(segment) { | ||
72 | self.segmentController = new SegmentController(); | ||
73 | self.segmentController.loadSegment(segment.url, self.onSegmentLoadComplete, self.onSegmentLoadError); | ||
74 | }; | ||
75 | |||
76 | self.onSegmentLoadComplete = function(segment) { | ||
77 | self.parser.parseSegmentBinaryData(segment.binaryData); | ||
78 | |||
79 | while (self.parser.tagsAvailable()) { | ||
80 | self.sourceBuffer.appendBuffer(self.parser.getNextTag().bytes, self.player); | ||
81 | } | ||
82 | |||
83 | if (self.currentSegment < self.currentManifest.mediaItems.length-1) { | ||
84 | self.loadNextSegment(); | ||
85 | } | ||
86 | }; | ||
87 | |||
88 | self.loadNextSegment = function() { | ||
89 | self.currentSegment++; | ||
90 | self.loadSegment(self.currentManifest.mediaItems[self.currentSegment]); | ||
91 | }; | ||
92 | |||
93 | self.onSegmentLoadError = function() {}; | ||
94 | |||
95 | }; | ||
96 | })(this); |
... | @@ -2,47 +2,48 @@ | ... | @@ -2,47 +2,48 @@ |
2 | var M3U8 = window.videojs.hls.M3U8; | 2 | var M3U8 = window.videojs.hls.M3U8; |
3 | 3 | ||
4 | window.videojs.hls.M3U8Parser = function() { | 4 | window.videojs.hls.M3U8Parser = function() { |
5 | 5 | var | |
6 | var self = this; | 6 | self = this, |
7 | var tagTypes = window.videojs.hls.m3u8TagType; | 7 | tagTypes = window.videojs.hls.m3u8TagType, |
8 | var lines = []; | 8 | lines = [], |
9 | var data; | 9 | data; |
10 | 10 | ||
11 | self.getTagType = function( lineData ) { | 11 | self.getTagType = function(lineData) { |
12 | for ( var s in tagTypes ) | 12 | for (var s in tagTypes) { |
13 | { | 13 | if (lineData.indexOf(tagTypes[s]) === 0) { |
14 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
15 | { | ||
16 | return tagTypes[s]; | 14 | return tagTypes[s]; |
17 | } | 15 | } |
18 | } | 16 | } |
19 | } | 17 | }; |
20 | 18 | ||
21 | self.getTagValue = function ( lineData ) { | 19 | self.getTagValue = function(lineData) { |
22 | for ( var s in tagTypes ) | 20 | for (var s in tagTypes) { |
23 | { | 21 | if (lineData.indexOf(tagTypes[s]) === 0) { |
24 | if (lineData.indexOf(tagTypes[s]) == 0) | ||
25 | { | ||
26 | return lineData.substr(tagTypes[s].length); | 22 | return lineData.substr(tagTypes[s].length); |
27 | } | 23 | } |
28 | } | 24 | } |
29 | } | 25 | }; |
30 | 26 | ||
31 | self.parse = function( rawDataString ) { | 27 | self.parse = function(rawDataString) { |
32 | data = new M3U8(); | 28 | data = new M3U8(); |
33 | 29 | ||
34 | if( rawDataString != undefined && rawDataString.toString().length > 0 ) | 30 | if (self.directory) { |
35 | { | 31 | data.directory = self.directory; |
32 | } | ||
33 | |||
34 | if (rawDataString === undefined || rawDataString.length <= 0) { | ||
35 | data.invalidReasons.push("Empty Manifest"); | ||
36 | return; | ||
37 | } | ||
36 | lines = rawDataString.split('\n'); | 38 | lines = rawDataString.split('\n'); |
37 | 39 | ||
38 | lines.forEach( | 40 | lines.forEach(function(value,index) { |
39 | function(value,index) { | 41 | var segment, rendition, attributes; |
40 | switch( self.getTagType(value) ) | 42 | |
41 | { | 43 | switch (self.getTagType(value)) { |
42 | case tagTypes.EXTM3U: | 44 | case tagTypes.EXTM3U: |
43 | data.hasValidM3UTag = (index == 0); | 45 | data.hasValidM3UTag = (index === 0); |
44 | if(!data.hasValidM3UTag) | 46 | if (!data.hasValidM3UTag) { |
45 | { | ||
46 | data.invalidReasons.push("Invalid EXTM3U Tag"); | 47 | data.invalidReasons.push("Invalid EXTM3U Tag"); |
47 | } | 48 | } |
48 | break; | 49 | break; |
... | @@ -51,8 +52,8 @@ | ... | @@ -51,8 +52,8 @@ |
51 | break; | 52 | break; |
52 | 53 | ||
53 | case tagTypes.PLAYLIST_TYPE: | 54 | case tagTypes.PLAYLIST_TYPE: |
54 | if(self.getTagValue(value) == "VOD" || self.getTagValue(value) == "EVENT") | 55 | if (self.getTagValue(value) === "VOD" || |
55 | { | 56 | self.getTagValue(value) === "EVENT") { |
56 | data.playlistType = self.getTagValue(value); | 57 | data.playlistType = self.getTagValue(value); |
57 | data.isPlaylist = true; | 58 | data.isPlaylist = true; |
58 | } else { | 59 | } else { |
... | @@ -61,50 +62,53 @@ | ... | @@ -61,50 +62,53 @@ |
61 | break; | 62 | break; |
62 | 63 | ||
63 | case tagTypes.EXTINF: | 64 | case tagTypes.EXTINF: |
64 | var segment = {url: "unknown", byterange: -1, targetDuration: data.targetDuration }; | 65 | segment = { |
66 | url: "unknown", | ||
67 | byterange: -1, | ||
68 | targetDuration: data.targetDuration | ||
69 | }; | ||
65 | 70 | ||
66 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | 71 | if (self.getTagType(lines[index + 1]) === tagTypes.BYTERANGE) { |
67 | { | 72 | segment.byterange = self.getTagValue(lines[index + 1]).split('@'); |
68 | segment.byterange = self.getTagValue(lines[index+1]).split('@'); | 73 | segment.url = lines[index + 2]; |
69 | segment.url = lines[index+2]; | 74 | } else { |
70 | } else | 75 | segment.url = lines[index + 1]; |
71 | { | ||
72 | segment.url = lines[index+1]; | ||
73 | } | 76 | } |
74 | 77 | ||
78 | if (segment.url.indexOf("http") === -1 && self.directory) { | ||
79 | if (data.directory[data.directory.length-1] === segment.url[0] && | ||
80 | segment.url[0] === "/") { | ||
81 | segment.url = segment.url.substr(1); | ||
82 | } | ||
83 | segment.url = self.directory + segment.url; | ||
84 | } | ||
75 | data.mediaItems.push(segment); | 85 | data.mediaItems.push(segment); |
76 | |||
77 | break; | 86 | break; |
78 | 87 | ||
79 | case tagTypes.STREAM_INF: | 88 | case tagTypes.STREAM_INF: |
80 | var rendition = {}; | 89 | rendition = {}; |
81 | var attributes = value.substr(tagTypes.STREAM_INF.length).split(','); | 90 | attributes = value.substr(tagTypes.STREAM_INF.length).split(','); |
82 | 91 | ||
83 | attributes.forEach(function(attr_value,attr_index) { | 92 | attributes.forEach(function(attrValue) { |
84 | if(isNaN(attr_value.split('=')[1])){ | 93 | if (isNaN(attrValue.split('=')[1])) { |
85 | rendition[attr_value.split('=')[0].toLowerCase()] = attr_value.split('=')[1]; | 94 | rendition[attrValue.split('=')[0].toLowerCase()] = attrValue.split('=')[1]; |
86 | 95 | ||
87 | if(rendition[attr_value.split('=')[0].toLowerCase()].split('x').length = 2) | 96 | if (rendition[attrValue.split('=')[0].toLowerCase()].split('x').length === 2) { |
88 | { | ||
89 | rendition.resolution = { | 97 | rendition.resolution = { |
90 | width: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[0]), | 98 | width: parseInt(rendition[attrValue.split('=')[0].toLowerCase()].split('x')[0],10), |
91 | height: Number(rendition[attr_value.split('=')[0].toLowerCase()].split('x')[1]) | 99 | height: parseInt(rendition[attrValue.split('=')[0].toLowerCase()].split('x')[1],10) |
92 | } | 100 | }; |
93 | } | 101 | } |
94 | |||
95 | } else { | 102 | } else { |
96 | rendition[attr_value.split('=')[0].toLowerCase()] = Number(attr_value.split('=')[1]); | 103 | rendition[attrValue.split('=')[0].toLowerCase()] = parseInt(attrValue.split('=')[1],10); |
97 | } | 104 | } |
98 | }); | 105 | }); |
99 | 106 | ||
100 | 107 | if (self.getTagType(lines[index + 1]) === tagTypes.BYTERANGE) { | |
101 | if( self.getTagType(lines[index+1]) == tagTypes.BYTERANGE ) | 108 | rendition.byterange = self.getTagValue(lines[index + 1]).split('@'); |
102 | { | 109 | rendition.url = lines[index + 2]; |
103 | rendition.byterange = self.getTagValue(lines[index+1]).split('@'); | 110 | } else { |
104 | rendition.url = lines[index+2]; | 111 | rendition.url = lines[index + 1]; |
105 | } else | ||
106 | { | ||
107 | rendition.url = lines[index+1]; | ||
108 | } | 112 | } |
109 | 113 | ||
110 | data.isPlaylist = true; | 114 | data.isPlaylist = true; |
... | @@ -112,24 +116,23 @@ | ... | @@ -112,24 +116,23 @@ |
112 | break; | 116 | break; |
113 | 117 | ||
114 | case tagTypes.TARGETDURATION: | 118 | case tagTypes.TARGETDURATION: |
115 | data.targetDuration = Number(self.getTagValue(value).split(',')[0]); | 119 | data.targetDuration = parseFloat(self.getTagValue(value).split(',')[0]); |
116 | break; | 120 | break; |
117 | 121 | ||
118 | case tagTypes.ZEN_TOTAL_DURATION: | 122 | case tagTypes.ZEN_TOTAL_DURATION: |
119 | data.totalDuration = self.getTagValue(value); | 123 | data.totalDuration = parseFloat(self.getTagValue(value)); |
120 | break; | 124 | break; |
121 | 125 | ||
122 | case tagTypes.VERSION: | 126 | case tagTypes.VERSION: |
123 | data.version = Number(self.getTagValue(value)); | 127 | data.version = parseFloat(self.getTagValue(value)); |
124 | break; | 128 | break; |
125 | 129 | ||
126 | case tagTypes.MEDIA_SEQUENCE: | 130 | case tagTypes.MEDIA_SEQUENCE: |
127 | data.mediaSequence = parseInt(self.getTagValue(value)); | 131 | data.mediaSequence = parseInt(self.getTagValue(value),10); |
128 | break; | 132 | break; |
129 | 133 | ||
130 | case tagTypes.ALLOW_CACHE: | 134 | case tagTypes.ALLOW_CACHE: |
131 | if(self.getTagValue(value) == "YES" || self.getTagValue(value) == "NO") | 135 | if (self.getTagValue(value) === "YES" || self.getTagValue(value) === "NO") { |
132 | { | ||
133 | data.allowCache = self.getTagValue(value); | 136 | data.allowCache = self.getTagValue(value); |
134 | } else { | 137 | } else { |
135 | data.invalidReasons.push("Invalid ALLOW_CACHE Value"); | 138 | data.invalidReasons.push("Invalid ALLOW_CACHE Value"); |
... | @@ -140,15 +143,9 @@ | ... | @@ -140,15 +143,9 @@ |
140 | data.hasEndTag = true; | 143 | data.hasEndTag = true; |
141 | break; | 144 | break; |
142 | } | 145 | } |
143 | } | 146 | }); |
144 | ) | ||
145 | } else { | ||
146 | data.invalidReasons.push("Empty Manifest"); | ||
147 | } | ||
148 | 147 | ||
149 | return data; | 148 | return data; |
150 | |||
151 | }; | 149 | }; |
152 | }; | 150 | }; |
153 | |||
154 | })(this); | 151 | })(this); | ... | ... |
1 | window.videojs.hls.m3u8TagType = { | 1 | (function(window) { |
2 | window.videojs.hls.m3u8TagType = { | ||
2 | /* | 3 | /* |
3 | * Derived from V8: http://tools.ietf.org/html/draft-pantos-http-live-streaming-08 | 4 | * Derived from the HTTP Live Streaming Spec V8 |
5 | * http://tools.ietf.org/html/draft-pantos-http-live-streaming-08 | ||
4 | */ | 6 | */ |
5 | 7 | ||
6 | /** | 8 | /** |
... | @@ -108,4 +110,5 @@ window.videojs.hls.m3u8TagType = { | ... | @@ -108,4 +110,5 @@ window.videojs.hls.m3u8TagType = { |
108 | */ | 110 | */ |
109 | ZEN_TOTAL_DURATION: "#ZEN-TOTAL-DURATION:" | 111 | ZEN_TOTAL_DURATION: "#ZEN-TOTAL-DURATION:" |
110 | 112 | ||
111 | }; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
113 | }; | ||
114 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function (window) { |
2 | window.videojs.hls.M3U8 = function() { | 2 | window.videojs.hls.M3U8 = function () { |
3 | this.directory = ""; | ||
3 | this.allowCache = "NO"; | 4 | this.allowCache = "NO"; |
4 | this.playlistItems = []; | 5 | this.playlistItems = []; |
5 | this.mediaItems = []; | 6 | this.mediaItems = []; |
... | @@ -13,5 +14,5 @@ | ... | @@ -13,5 +14,5 @@ |
13 | this.playlistType = ""; | 14 | this.playlistType = ""; |
14 | this.mediaSequence = -1; | 15 | this.mediaSequence = -1; |
15 | this.version = -1; | 16 | this.version = -1; |
16 | } | 17 | }; |
17 | })(this); | 18 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function (window) { |
2 | var M3U8 = window.videojs.hls.M3U8; | 2 | var |
3 | var M3U8Parser = window.videojs.hls.M3U8Parser; | 3 | M3U8Parser = window.videojs.hls.M3U8Parser; |
4 | |||
5 | window.videojs.hls.ManifestController = function(){ | ||
6 | 4 | ||
5 | window.videojs.hls.ManifestController = function() { | ||
7 | var self = this; | 6 | var self = this; |
8 | var parser; | ||
9 | var data; | ||
10 | 7 | ||
11 | var onDataCallback; | 8 | self.loadManifest = function(manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) { |
12 | var onErrorCallback; | 9 | self.url = manifestUrl; |
13 | var onUpdateCallback; | ||
14 | 10 | ||
15 | self.loadManifest = function ( manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { | 11 | if (onDataCallback) { |
16 | self.onDataCallback = onDataCallback; | 12 | self.onDataCallback = onDataCallback; |
13 | } | ||
14 | if (onErrorCallback) { | ||
17 | self.onErrorCallback = onErrorCallback; | 15 | self.onErrorCallback = onErrorCallback; |
16 | } | ||
17 | |||
18 | if (onUpdateCallback) { | ||
18 | self.onUpdateCallback = onUpdateCallback; | 19 | self.onUpdateCallback = onUpdateCallback; |
20 | } | ||
19 | 21 | ||
20 | vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); | 22 | window.vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); |
21 | }; | 23 | }; |
22 | 24 | ||
23 | self.parseManifest = function ( dataAsString ) { | 25 | self.parseManifest = function(dataAsString) { |
24 | self.parser = new M3U8Parser(); | 26 | self.parser = new M3U8Parser(); |
25 | self.data = self.parser.parse( dataAsString ); | 27 | self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1]; |
28 | self.data = self.parser.parse(dataAsString); | ||
26 | 29 | ||
27 | return self.data; | 30 | return self.data; |
28 | }; | 31 | }; |
... | @@ -30,22 +33,15 @@ | ... | @@ -30,22 +33,15 @@ |
30 | self.onManifestLoadComplete = function(response) { | 33 | self.onManifestLoadComplete = function(response) { |
31 | var output = self.parseManifest(response); | 34 | var output = self.parseManifest(response); |
32 | 35 | ||
33 | if(self.onDataCallback != undefined) | 36 | if (self.onDataCallback !== undefined) { |
34 | { | ||
35 | self.onDataCallback(output); | 37 | self.onDataCallback(output); |
36 | } | 38 | } |
37 | }; | 39 | }; |
38 | 40 | ||
39 | self.onManifestLoadError = function(err) { | 41 | self.onManifestLoadError = function(err) { |
40 | if(err) | 42 | if (self.onErrorCallback !== undefined) { |
41 | { | 43 | self.onErrorCallback((err !== undefined) ? err : null); |
42 | console.log(err.message); | ||
43 | } | ||
44 | |||
45 | if(self.onErrorCallback != undefined) | ||
46 | { | ||
47 | onErrorCallback((err != undefined) ? err : null); | ||
48 | } | 44 | } |
49 | }; | 45 | }; |
50 | } | 46 | }; |
51 | })(this); | 47 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function(window) { |
2 | 2 | ||
3 | var SegmentParser = window.videojs.hls.SegmentParser; | 3 | window.videojs.hls.SegmentController = function() { |
4 | |||
5 | window.videojs.hls.SegmentController = function(){ | ||
6 | |||
7 | var self = this; | 4 | var self = this; |
8 | var url; | ||
9 | var parser; | ||
10 | var requestTimestamp; | ||
11 | var responseTimestamp; | ||
12 | var data; | ||
13 | 5 | ||
14 | var onDataCallback; | 6 | self.loadSegment = function(segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback) { |
15 | var onErrorCallback; | 7 | var request = new XMLHttpRequest(); |
16 | var onUpdateCallback; | ||
17 | 8 | ||
18 | self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { | ||
19 | self.url = segmentUrl; | 9 | self.url = segmentUrl; |
20 | self.onDataCallback = onDataCallback; | 10 | self.onDataCallback = onDataCallback; |
21 | self.onErrorCallback = onErrorCallback; | 11 | self.onErrorCallback = onErrorCallback; |
22 | self.onUpdateCallback = onUpdateCallback; | 12 | self.onUpdateCallback = onUpdateCallback; |
23 | self.requestTimestamp = new Date().getTime(); | 13 | self.requestTimestamp = +new Date(); |
24 | 14 | ||
25 | var req = new XMLHttpRequest(); | 15 | request.open('GET', segmentUrl, true); |
26 | req.open('GET', segmentUrl, true); | 16 | request.responseType = 'arraybuffer'; |
27 | req.responseType = 'arraybuffer'; | 17 | request.onload = function() { |
28 | req.onload = function(response) { | 18 | self.onSegmentLoadComplete(new Uint8Array(request.response)); |
29 | self.onSegmentLoadComplete(new Uint8Array(req.response)); | ||
30 | }; | 19 | }; |
31 | 20 | ||
32 | req.send(null); | 21 | request.send(null); |
33 | }; | 22 | }; |
34 | 23 | ||
35 | self.parseSegment = function ( incomingData ) { | 24 | self.parseSegment = function(incomingData) { |
36 | // Add David's code later // | 25 | self.data = {}; |
37 | 26 | self.data.binaryData = incomingData; | |
38 | self.data = { | ||
39 | whatever: incomingData | ||
40 | }; | ||
41 | self.data.url = self.url; | 27 | self.data.url = self.url; |
42 | self.data.isCached = false; | 28 | self.data.isCached = false; |
43 | self.data.requestTimestamp = self.requestTimestamp; | 29 | self.data.requestTimestamp = self.requestTimestamp; |
44 | self.data.responseTimestamp = self.responseTimestamp; | 30 | self.data.responseTimestamp = self.responseTimestamp; |
45 | self.data.byteLength = incomingData.byteLength; | 31 | self.data.byteLength = incomingData.byteLength; |
46 | self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 ); | 32 | self.data.isCached = parseInt(self.responseTimestamp - self.requestTimestamp,10) < 75; |
47 | self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp) | 33 | self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp); |
48 | 34 | ||
49 | return self.data; | 35 | return self.data; |
50 | }; | 36 | }; |
51 | 37 | ||
52 | self.calculateThroughput = function(dataAmount, startTime, endTime) { | 38 | self.calculateThroughput = function(dataAmount, startTime, endTime) { |
53 | return Math.round(dataAmount/(endTime-startTime)*1000)*8; | 39 | return Math.round(dataAmount / (endTime - startTime) * 1000) * 8; |
54 | } | 40 | }; |
55 | 41 | ||
56 | self.onSegmentLoadComplete = function(response) { | 42 | self.onSegmentLoadComplete = function(response) { |
57 | self.responseTimestamp = new Date().getTime(); | 43 | var output; |
44 | |||
45 | self.responseTimestamp = +new Date(); | ||
58 | 46 | ||
59 | var output = self.parseSegment(response); | 47 | output = self.parseSegment(response); |
60 | 48 | ||
61 | if(self.onDataCallback != undefined) | 49 | if (self.onDataCallback !== undefined) { |
62 | { | ||
63 | self.onDataCallback(output); | 50 | self.onDataCallback(output); |
64 | } | 51 | } |
65 | }; | 52 | }; |
66 | 53 | ||
67 | self.onSegmentLoadError = function(err) { | 54 | self.onSegmentLoadError = function(error) { |
68 | if(err) | 55 | if (error) { |
69 | { | 56 | throw error; |
70 | console.log(err.message); | ||
71 | } | 57 | } |
72 | 58 | ||
73 | if(self.onErrorCallback != undefined) | 59 | if (self.onErrorCallback !== undefined) { |
74 | { | 60 | self.onErrorCallback(error); |
75 | onErrorCallback((err != undefined) ? err : null); | ||
76 | } | 61 | } |
77 | }; | 62 | }; |
78 | } | 63 | }; |
79 | })(this); | 64 | })(this); | ... | ... |
1 | (function(window) { | 1 | (function(window) { |
2 | var | 2 | var |
3 | FlvTag = window.videojs.hls.FlvTag, | 3 | videojs = window.videojs, |
4 | H264Stream = window.videojs.hls.H264Stream, | 4 | FlvTag = videojs.hls.FlvTag, |
5 | AacStream = window.videojs.hls.AacStream, | 5 | H264Stream = videojs.hls.H264Stream, |
6 | AacStream = videojs.hls.AacStream, | ||
6 | m2tsPacketSize = 188; | 7 | m2tsPacketSize = 188; |
7 | 8 | ||
8 | console.assert(H264Stream); | 9 | console.assert(H264Stream); |
... | @@ -150,7 +151,7 @@ | ... | @@ -150,7 +151,7 @@ |
150 | // until we receive more | 151 | // until we receive more |
151 | 152 | ||
152 | // ?? this seems to append streamBuffer onto data and then just give up. I'm not sure why that would be interesting. | 153 | // ?? this seems to append streamBuffer onto data and then just give up. I'm not sure why that would be interesting. |
153 | console.log('data.length + streamBuffer.length < m2tsPacketSize ??'); | 154 | videojs.log('data.length + streamBuffer.length < m2tsPacketSize ??'); |
154 | streamBuffer.readBytes(data, data.length, streamBuffer.length); | 155 | streamBuffer.readBytes(data, data.length, streamBuffer.length); |
155 | return; | 156 | return; |
156 | } else { | 157 | } else { |
... | @@ -194,7 +195,7 @@ | ... | @@ -194,7 +195,7 @@ |
194 | // If there was an error parsing a TS packet. it could be | 195 | // If there was an error parsing a TS packet. it could be |
195 | // because we are not TS packet aligned. Step one forward by | 196 | // because we are not TS packet aligned. Step one forward by |
196 | // one byte and allow the code above to find the next | 197 | // one byte and allow the code above to find the next |
197 | console.log('error parsing m2ts packet, attempting to re-align'); | 198 | videojs.log('error parsing m2ts packet, attempting to re-align'); |
198 | dataPosition++; | 199 | dataPosition++; |
199 | } | 200 | } |
200 | } | 201 | } |
... | @@ -382,7 +383,7 @@ | ... | @@ -382,7 +383,7 @@ |
382 | } else if (0x1FFF === pid) { | 383 | } else if (0x1FFF === pid) { |
383 | // NULL packet | 384 | // NULL packet |
384 | } else { | 385 | } else { |
385 | console.log("Unknown PID " + pid); | 386 | videojs.log("Unknown PID parsing TS packet: " + pid); |
386 | } | 387 | } |
387 | 388 | ||
388 | return true; | 389 | return true; | ... | ... |
... | @@ -6,4 +6,4 @@ window.brightcove_playlist_data = '#EXTM3U\n'+ | ... | @@ -6,4 +6,4 @@ window.brightcove_playlist_data = '#EXTM3U\n'+ |
6 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=440000,RESOLUTION=396x224\n'+ | 6 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=440000,RESOLUTION=396x224\n'+ |
7 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001\n'+ | 7 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824686593001&videoId=1824650741001\n'+ |
8 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1928000,RESOLUTION=960x540\n'+ | 8 | '#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1928000,RESOLUTION=960x540\n'+ |
9 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001' | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
9 | 'http://c.brightcove.com/services/mobile/streaming/index/rendition.m3u8?assetId=1824687660001&videoId=1824650741001'; | ... | ... |
... | @@ -34,7 +34,8 @@ | ... | @@ -34,7 +34,8 @@ |
34 | testScriptTag, | 34 | testScriptTag, |
35 | asciiFromBytes, | 35 | asciiFromBytes, |
36 | testScriptString, | 36 | testScriptString, |
37 | testScriptEcmaArray; | 37 | testScriptEcmaArray, |
38 | testNalUnit; | ||
38 | 39 | ||
39 | module('environment'); | 40 | module('environment'); |
40 | 41 | ||
... | @@ -61,13 +62,9 @@ | ... | @@ -61,13 +62,9 @@ |
61 | }); | 62 | }); |
62 | 63 | ||
63 | test('parses the first bipbop segment', function() { | 64 | test('parses the first bipbop segment', function() { |
64 | var tag, bytes, i; | ||
65 | parser.parseSegmentBinaryData(window.bcSegment); | 65 | parser.parseSegmentBinaryData(window.bcSegment); |
66 | 66 | ||
67 | ok(parser.tagsAvailable(), 'tags are available'); | 67 | ok(parser.tagsAvailable(), 'tags are available'); |
68 | |||
69 | console.log('h264 tags:', parser.stats.h264Tags(), | ||
70 | 'aac tags:', parser.stats.aacTags()); | ||
71 | }); | 68 | }); |
72 | 69 | ||
73 | testAudioTag = function(tag) { | 70 | testAudioTag = function(tag) { |
... | @@ -87,18 +84,16 @@ | ... | @@ -87,18 +84,16 @@ |
87 | ok(aacPacketType === 0 || aacPacketType === 1, 'aac packets should have a valid type'); | 84 | ok(aacPacketType === 0 || aacPacketType === 1, 'aac packets should have a valid type'); |
88 | }; | 85 | }; |
89 | 86 | ||
90 | testVideoTag = function(tag) { | 87 | testVideoTag = function (tag) { |
91 | var | 88 | var |
92 | byte = tag.bytes[11], | 89 | byte = tag.bytes[11], |
93 | frameType = (byte & 0xF0) >>> 4, | 90 | frameType = (byte & 0xF0) >>> 4, |
94 | codecId = byte & 0x0F, | 91 | codecId = byte & 0x0F, |
95 | packetType = tag.bytes[12], | 92 | packetType = tag.bytes[12], |
96 | compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8, | 93 | compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8; |
97 | nalHeader; | ||
98 | 94 | ||
99 | // payload starts at tag.bytes[16] | 95 | // payload starts at tag.bytes[16] |
100 | 96 | ||
101 | |||
102 | // XXX: I'm not sure that frame types 3-5 are invalid | 97 | // XXX: I'm not sure that frame types 3-5 are invalid |
103 | ok(frameType === 1 || frameType === 2, | 98 | ok(frameType === 1 || frameType === 2, |
104 | 'the frame type should be valid'); | 99 | 'the frame type should be valid'); |
... | @@ -122,8 +117,8 @@ | ... | @@ -122,8 +117,8 @@ |
122 | 117 | ||
123 | testNalUnit = function(bytes) { | 118 | testNalUnit = function(bytes) { |
124 | var | 119 | var |
125 | nalHeader = bytes[0], | 120 | nalHeader = bytes[0]; |
126 | unitType = nalHeader & 0x1F; | 121 | // unitType = nalHeader & 0x1F; |
127 | 122 | ||
128 | equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0'); | 123 | equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0'); |
129 | // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something'); | 124 | // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something'); |
... | @@ -144,10 +139,10 @@ | ... | @@ -144,10 +139,10 @@ |
144 | }; | 139 | }; |
145 | 140 | ||
146 | testScriptString = function(tag, offset, expected) { | 141 | testScriptString = function(tag, offset, expected) { |
147 | var type = tag.bytes[offset], | 142 | var |
143 | type = tag.bytes[offset], | ||
148 | stringLength = tag.view.getUint16(offset + 1), | 144 | stringLength = tag.view.getUint16(offset + 1), |
149 | string, | 145 | string; |
150 | i = expected.length; | ||
151 | 146 | ||
152 | equal(2, type, 'the script element is of string type'); | 147 | equal(2, type, 'the script element is of string type'); |
153 | equal(stringLength, expected.length, 'the script string length is correct'); | 148 | equal(stringLength, expected.length, 'the script string length is correct'); |
... | @@ -247,21 +242,19 @@ | ... | @@ -247,21 +242,19 @@ |
247 | }); | 242 | }); |
248 | 243 | ||
249 | test('should create my parser', function() { | 244 | test('should create my parser', function() { |
250 | ok(m3u8parser != undefined); | 245 | ok(m3u8parser !== undefined); |
251 | } | 246 | }); |
252 | ); | ||
253 | 247 | ||
254 | test('should successfully parse manifest data', function() { | 248 | test('should successfully parse manifest data', function() { |
255 | var parsedData = m3u8parser.parse(window.playlistData); | 249 | var parsedData = m3u8parser.parse(window.playlistData); |
256 | ok(parsedData); | 250 | ok(parsedData); |
257 | } | 251 | }); |
258 | ); | ||
259 | 252 | ||
260 | test('test for expected results', function() { | 253 | test('test for expected results', function() { |
261 | var data = m3u8parser.parse(window.playlistData); | 254 | var data = m3u8parser.parse(window.playlistData); |
262 | 255 | ||
263 | notEqual(data, null, 'data is not NULL'); | 256 | notEqual(data, null, 'data is not NULL'); |
264 | equal(data.invalidReasons.length, 0,'data has 0 invalid reasons'); | 257 | equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons'); |
265 | equal(data.hasValidM3UTag, true, 'data has valid EXTM3U'); | 258 | equal(data.hasValidM3UTag, true, 'data has valid EXTM3U'); |
266 | equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); | 259 | equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); |
267 | equal(data.allowCache, "NO", 'acceptable ALLOW CACHE'); | 260 | equal(data.allowCache, "NO", 'acceptable ALLOW CACHE'); |
... | @@ -271,8 +264,7 @@ | ... | @@ -271,8 +264,7 @@ |
271 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); | 264 | equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); |
272 | equal(data.totalDuration, -1, "ZEN TOTAL DURATION is unknown as expected"); | 265 | equal(data.totalDuration, -1, "ZEN TOTAL DURATION is unknown as expected"); |
273 | equal(data.hasEndTag, true, 'should have ENDLIST tag'); | 266 | equal(data.hasEndTag, true, 'should have ENDLIST tag'); |
274 | } | 267 | }); |
275 | ); | ||
276 | 268 | ||
277 | module('brightcove playlist', { | 269 | module('brightcove playlist', { |
278 | setup: function() { | 270 | setup: function() { |
... | @@ -285,10 +277,10 @@ | ... | @@ -285,10 +277,10 @@ |
285 | 277 | ||
286 | ok(data); | 278 | ok(data); |
287 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); | 279 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); |
288 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); | 280 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct'); |
289 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); | 281 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct'); |
290 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); | 282 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct'); |
291 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); | 283 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct'); |
292 | 284 | ||
293 | } | 285 | } |
294 | ); | 286 | ); |
... | @@ -296,14 +288,13 @@ | ... | @@ -296,14 +288,13 @@ |
296 | module('manifest controller', { | 288 | module('manifest controller', { |
297 | setup: function() { | 289 | setup: function() { |
298 | manifestController = new window.videojs.hls.ManifestController(); | 290 | manifestController = new window.videojs.hls.ManifestController(); |
299 | this.vjsget = vjs.get; | 291 | this.vjsget = window.videojs.get; |
300 | vjs.get = function(url, success, error){ | 292 | window.videojs.get = function(url, success) { |
301 | console.log(url); | ||
302 | success(window.brightcove_playlist_data); | 293 | success(window.brightcove_playlist_data); |
303 | }; | 294 | }; |
304 | }, | 295 | }, |
305 | teardown: function() { | 296 | teardown: function() { |
306 | vjs.get = this.vjsget; | 297 | window.videojs.get = this.vjsget; |
307 | } | 298 | } |
308 | }); | 299 | }); |
309 | 300 | ||
... | @@ -315,72 +306,42 @@ | ... | @@ -315,72 +306,42 @@ |
315 | var data = manifestController.parseManifest(window.brightcove_playlist_data); | 306 | var data = manifestController.parseManifest(window.brightcove_playlist_data); |
316 | 307 | ||
317 | ok(data); | 308 | ok(data); |
318 | |||
319 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); | 309 | equal(data.playlistItems.length, 4, 'Has correct rendition count'); |
320 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); | 310 | equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct'); |
321 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); | 311 | equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct'); |
322 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); | 312 | equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct'); |
323 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); | 313 | equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct'); |
324 | }) | 314 | }); |
325 | 315 | ||
326 | test('should get a manifest from hermes', function() { | 316 | test('should get a manifest from hermes', function() { |
327 | var hermesUrl = "http://localhost:7070/test/basic-playback/brightcove/16x9-master.m3u8"; | 317 | manifestController.loadManifest('http://example.com/16x9-master.m3u8', |
328 | 318 | function(responseData) { | |
329 | manifestController.loadManifest( | 319 | ok(responseData); |
330 | hermesUrl, | ||
331 | function(responseData){ | ||
332 | ok(true); | ||
333 | }, | 320 | }, |
334 | function(errorData){ | 321 | function() { |
335 | console.log('got error data'); | 322 | ok(false, 'does not error'); |
336 | }, | 323 | }, |
337 | function(updateData){ | 324 | function() {}); |
338 | console.log('got update data'); | ||
339 | } | ||
340 | ) | ||
341 | }); | 325 | }); |
342 | 326 | ||
343 | module('segment controller', { | 327 | module('segment controller', { |
344 | setup: function() { | 328 | setup: function() { |
345 | segmentController = new window.videojs.hls.SegmentController(); | 329 | segmentController = new window.videojs.hls.SegmentController(); |
346 | this.vjsget = vjs.get; | 330 | this.vjsget = window.videojs.get; |
347 | vjs.get = function(url, success, error){ | 331 | window.videojs.get = function(url, success) { |
348 | console.log('load segment url', url); | ||
349 | success(window.bcSegment); | 332 | success(window.bcSegment); |
350 | }; | 333 | }; |
351 | }, | 334 | }, |
352 | teardown: function() { | 335 | teardown: function() { |
353 | vjs.get = this.vjsget; | 336 | window.videojs.get = this.vjsget; |
354 | } | 337 | } |
355 | }); | 338 | }); |
356 | 339 | ||
357 | test('should get a segment data', function() { | ||
358 | ok(true); | ||
359 | var hermesUrl = "http://localhost:7070/test/ts-files/brightcove/s-1.ts"; | ||
360 | |||
361 | segmentController.loadSegment( | ||
362 | hermesUrl, | ||
363 | function(responseData){ | ||
364 | console.log('got response from segment controller'); | ||
365 | ok(true); | ||
366 | |||
367 | }, | ||
368 | function(errorData){ | ||
369 | console.log('got error data'); | ||
370 | }, | ||
371 | function(updateData){ | ||
372 | console.log('got update data'); | ||
373 | } | ||
374 | ) | ||
375 | } | ||
376 | ) | ||
377 | |||
378 | test('bandwidth calulation test', function() { | 340 | test('bandwidth calulation test', function() { |
379 | var multiSecondData = segmentController.calculateThroughput(10000,1000,2000); | 341 | var |
380 | var subSecondData = segmentController.calculateThroughput(10000,1000,1500); | 342 | multiSecondData = segmentController.calculateThroughput(10000, 1000, 2000), |
343 | subSecondData = segmentController.calculateThroughput(10000, 1000, 1500); | ||
381 | equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation'); | 344 | equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation'); |
382 | equal(subSecondData, 160000, 'SUB-Second bits per second calculation'); | 345 | equal(subSecondData, 160000, 'SUB-Second bits per second calculation'); |
383 | 346 | }); | |
384 | }) | ||
385 | |||
386 | })(this); | 347 | })(this); | ... | ... |
video-js-hls.iml
deleted
100644 → 0
-
Please register or sign in to post a comment