9fa0bf90 by David LaPalomento

Integrate peg-based parser into qunit tests

Add the peg parser generation step to the gruntfile. Include the generated parser in the test harness page. Update many of the m3u8 tests to work with the new parser. There are a number of tests still failing. I believe this is because parts of the grammar are not sufficiently flexible to handle some optional parameters. For instance, #EXT-X-BYTE-RANGE is being glommed incorrectly into the #EXTINF definition and that's throwing off parsing. This commit is a progress checkpoint; things are definitely not working correctly.
1 parent 3f975913
1 'use strict'; 1 'use strict';
2 2
3 var peg = require('pegjs');
4
3 module.exports = function(grunt) { 5 module.exports = function(grunt) {
4 6
5 // Project configuration. 7 // Project configuration.
...@@ -12,7 +14,7 @@ module.exports = function(grunt) { ...@@ -12,7 +14,7 @@ module.exports = function(grunt) {
12 ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', 14 ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
13 // Task configuration. 15 // Task configuration.
14 clean: { 16 clean: {
15 files: ['dist'] 17 files: ['build', 'dist']
16 }, 18 },
17 concat: { 19 concat: {
18 options: { 20 options: {
...@@ -95,8 +97,19 @@ module.exports = function(grunt) { ...@@ -95,8 +97,19 @@ module.exports = function(grunt) {
95 grunt.loadNpmTasks('grunt-contrib-jshint'); 97 grunt.loadNpmTasks('grunt-contrib-jshint');
96 grunt.loadNpmTasks('grunt-contrib-watch'); 98 grunt.loadNpmTasks('grunt-contrib-watch');
97 99
100 grunt.registerTask('peg', 'generate the manifest parser', function() {
101 var parser = peg.buildParser(grunt.file.read('src/m3u8/m3u8.pegjs'));
102 grunt.file.write('build/m3u8-parser.js',
103 'window.videojs.hls.M3U8Parser = ' + parser.toSource());
104 });
105
98 // Default task. 106 // Default task.
99 grunt.registerTask('default', 107 grunt.registerTask('default',
100 ['jshint', 'qunit', 'clean', 'concat', 'uglify']); 108 ['peg',
109 'jshint',
110 'qunit',
111 'clean',
112 'concat',
113 'uglify']);
101 114
102 }; 115 };
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 "grunt-contrib-jshint": "~0.6.0", 15 "grunt-contrib-jshint": "~0.6.0",
16 "grunt-contrib-qunit": "~0.2.0", 16 "grunt-contrib-qunit": "~0.2.0",
17 "grunt-contrib-concat": "~0.3.0", 17 "grunt-contrib-concat": "~0.3.0",
18 "grunt-contrib-nodeunit": "~0.1.2",
18 "grunt-contrib-uglify": "~0.2.0", 19 "grunt-contrib-uglify": "~0.2.0",
19 "grunt-contrib-watch": "~0.4.0", 20 "grunt-contrib-watch": "~0.4.0",
20 "grunt-contrib-clean": "~0.4.0", 21 "grunt-contrib-clean": "~0.4.0",
......
...@@ -269,13 +269,13 @@ number "number" ...@@ -269,13 +269,13 @@ number "number"
269 / parts:(int) _ { return parts; } 269 / parts:(int) _ { return parts; }
270 270
271 resolution 271 resolution
272 = width:int "x" height:int { return {resolution: {width: width, height: height}}; } 272 = width:int "x" height:int { return {width: width, height: height}; }
273 273
274 int 274 int
275 = first:digit19 rest:digits { return parseInt(first + rest.join(''), 10); } 275 = first:digit19 rest:digits { return parseInt(first + rest.join(''), 10); }
276 / digit:digit { return parseInt(digit, 10); } 276 / digit:digit { return parseInt(digit, 10); }
277 / neg:"-" first:digit19 rest:digits { return parseInt(neg + first + rest.join(''), 10); } 277 / neg:"-" first:digit19 rest:digits { return parseInt(neg + first + rest.join(''), 10); }
278 / neg:"-" digit { return parseInt(neg + digit, 10); } 278 / neg:"-" digit:digit { return parseInt(neg + digit, 10); }
279 279
280 hexint 280 hexint
281 = "0x" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); } 281 = "0x" hexDigits:hexDigit+ { return '0x' + hexDigits.join(''); }
......
1 (function (window) { 1 (function (window) {
2 var 2
3 M3U8Parser = window.videojs.hls.M3U8Parser; 3 var parser = window.videojs.hls.M3U8Parser;
4 4
5 window.videojs.hls.ManifestController = function() { 5 window.videojs.hls.ManifestController = function() {
6 var self = this; 6 var self = this;
...@@ -22,12 +22,8 @@ ...@@ -22,12 +22,8 @@
22 window.vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); 22 window.vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError);
23 }; 23 };
24 24
25 self.parseManifest = function(dataAsString) { 25 self.parseManifest = function(manifest) {
26 self.parser = new M3U8Parser(); 26 return parser.parse(manifest);
27 self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1];
28 self.data = self.parser.parse(dataAsString);
29
30 return self.data;
31 }; 27 };
32 28
33 self.onManifestLoadComplete = function(response) { 29 self.onManifestLoadComplete = function(response) {
......
1 (function(window) { 1 (function(window, console) {
2 var 2 var
3 Handlebars = this.Handlebars, 3 Handlebars = this.Handlebars,
4 manifestController = this.manifestController, 4 manifestController = this.manifestController,
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
12 }); 12 });
13 13
14 /* 14 /*
15 Manfiest controller 15 Manifest controller
16 */ 16 */
17 17
18 module('manifest controller', { 18 module('manifest controller', {
...@@ -36,14 +36,14 @@ ...@@ -36,14 +36,14 @@
36 var data = manifestController.parseManifest(window.brightcove_playlist_data); 36 var data = manifestController.parseManifest(window.brightcove_playlist_data);
37 37
38 ok(data); 38 ok(data);
39 equal(data.playlistItems.length, 4, 'Has correct rendition count'); 39 equal(data.playlists.length, 4, 'Has correct rendition count');
40 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct'); 40 equal(data.playlists[0].attributes.bandwidth, 240000, 'First rendition index bandwidth is correct');
41 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct'); 41 equal(data.playlists[0].attributes.programId, 1, 'First rendition index program-id is correct');
42 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct'); 42 equal(data.playlists[0].attributes.resolution.width, 396, 'First rendition index resolution width is correct');
43 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct'); 43 equal(data.playlists[0].attributes.resolution.height, 224, 'First rendition index resolution height is correct');
44 }); 44 });
45 45
46 test('should get a manifest from hermes', function() { 46 test('should get a manifest from an external URL', function() {
47 manifestController.loadManifest('http://example.com/16x9-master.m3u8', 47 manifestController.loadManifest('http://example.com/16x9-master.m3u8',
48 function(responseData) { 48 function(responseData) {
49 ok(responseData); 49 ok(responseData);
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
60 60
61 module('m3u8 parser', { 61 module('m3u8 parser', {
62 setup: function() { 62 setup: function() {
63 m3u8parser = new window.videojs.hls.M3U8Parser(); 63 m3u8parser = window.videojs.hls.M3U8Parser;
64 } 64 }
65 }); 65 });
66 66
...@@ -77,16 +77,14 @@ ...@@ -77,16 +77,14 @@
77 var data = m3u8parser.parse(window.playlistData); 77 var data = m3u8parser.parse(window.playlistData);
78 78
79 notEqual(data, null, 'data is not NULL'); 79 notEqual(data, null, 'data is not NULL');
80 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 80 equal(data.openTag, true, 'data has valid EXTM3U');
81 equal(data.hasValidM3UTag, true, 'data has valid EXTM3U');
82 equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); 81 equal(data.targetDuration, 10, 'data has correct TARGET DURATION');
83 equal(data.allowCache, "NO", 'acceptable ALLOW CACHE'); 82 equal(data.allowCache, undefined, 'ALLOW-CACHE is not present in the manifest');
84 equal(data.isPlaylist, false, 'data is parsed as a PLAYLIST as expected');
85 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); 83 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE');
86 equal(data.mediaItems.length, 16, 'acceptable mediaItem count'); 84 equal(data.segments.length, 17, 'there are 17 segments in the manifest');
87 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); 85 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct');
88 equal(data.totalDuration, -1, "ZEN TOTAL DURATION is unknown as expected"); 86 equal(data.totalDuration, undefined, "no total duration is specified");
89 equal(data.hasEndTag, true, 'should have ENDLIST tag'); 87 equal(data.closeTag, true, 'should have ENDLIST tag');
90 }); 88 });
91 89
92 /*3.4.7. EXT-X-PLAYLIST-TYPE 90 /*3.4.7. EXT-X-PLAYLIST-TYPE
...@@ -111,7 +109,7 @@ ...@@ -111,7 +109,7 @@
111 data = m3u8parser.parse(playlistData); 109 data = m3u8parser.parse(playlistData);
112 110
113 notEqual(data, null, 'data is not NULL'); 111 notEqual(data, null, 'data is not NULL');
114 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 112 //equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
115 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); 113 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE');
116 }); 114 });
117 115
...@@ -122,11 +120,11 @@ ...@@ -122,11 +120,11 @@
122 playlistData = playlistTemplate(testData), 120 playlistData = playlistTemplate(testData),
123 data = m3u8parser.parse(playlistData); 121 data = m3u8parser.parse(playlistData);
124 notEqual(data, null, 'data is not NULL'); 122 notEqual(data, null, 'data is not NULL');
125 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 123 //equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
126 equal(data.playlistType, "EVENT", 'acceptable PLAYLIST TYPE'); 124 equal(data.playlistType, "EVENT", 'acceptable PLAYLIST TYPE');
127 }); 125 });
128 126
129 test('should have assumed VOD playlist type if not defined', function() { 127 test('handles a missing playlist type', function() {
130 var 128 var
131 playlistTemplate = Handlebars.compile(window.playlist_type_template), 129 playlistTemplate = Handlebars.compile(window.playlist_type_template),
132 testData = {}, 130 testData = {},
...@@ -134,9 +132,9 @@ ...@@ -134,9 +132,9 @@
134 data = m3u8parser.parse(playlistData); 132 data = m3u8parser.parse(playlistData);
135 133
136 notEqual(data, null, 'data is not NULL'); 134 notEqual(data, null, 'data is not NULL');
137 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 135 //equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
138 equal(data.warnings, 'EXT-X-PLAYLIST-TYPE was empty or missing. Assuming VOD'); 136 //equal(data.warnings, 'EXT-X-PLAYLIST-TYPE was empty or missing. Assuming VOD');
139 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); 137 equal(data.playlistType, undefined, 'no PLAYLIST TYPE present');
140 }); 138 });
141 139
142 test('should have an invalid reason due to invalid playlist type', function() { 140 test('should have an invalid reason due to invalid playlist type', function() {
...@@ -146,20 +144,20 @@ ...@@ -146,20 +144,20 @@
146 playlistData = playlistTemplate(testData), 144 playlistData = playlistTemplate(testData),
147 data = m3u8parser.parse(playlistData); 145 data = m3u8parser.parse(playlistData);
148 notEqual(data, null, 'data is not NULL'); 146 notEqual(data, null, 'data is not NULL');
149 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 147 //equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
150 equal(data.invalidReasons[0], 'Invalid Playlist Type Value: \'baklsdhfajsdf\''); 148 //equal(data.invalidReasons[0], 'Invalid Playlist Type Value: \'baklsdhfajsdf\'');
151 }); 149 });
152 150
153 test('should have assumed VOD playlist type is empty', function() { 151 test('handles an empty playlist type', function() {
154 var 152 var
155 playlistTemplate = Handlebars.compile(window.playlist_type_template), 153 playlistTemplate = Handlebars.compile(window.playlist_type_template),
156 testData = {playlistType: ''}, 154 testData = {playlistType: ''},
157 playlistData = playlistTemplate(testData), 155 playlistData = playlistTemplate(testData),
158 data = m3u8parser.parse(playlistData); 156 data = m3u8parser.parse(playlistData);
159 notEqual(data, null, 'data is not NULL'); 157 notEqual(data, null, 'data is not NULL');
160 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 158 //equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
161 equal(data.warnings, 'EXT-X-PLAYLIST-TYPE was empty or missing. Assuming VOD'); 159 //equal(data.warnings, 'EXT-X-PLAYLIST-TYPE was empty or missing. Assuming VOD');
162 equal(data.playlistType, "VOD", 'acceptable PLAYLIST TYPE'); 160 equal(data.playlistType, '', 'PLAYLIST TYPE is the empty string');
163 }); 161 });
164 162
165 /*3.4.2. EXT-X-TARGETDURATION 163 /*3.4.2. EXT-X-TARGETDURATION
...@@ -187,7 +185,7 @@ ...@@ -187,7 +185,7 @@
187 data = m3u8parser.parse(playlistData); 185 data = m3u8parser.parse(playlistData);
188 notEqual(data, null, 'data is not NULL'); 186 notEqual(data, null, 'data is not NULL');
189 equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); 187 equal(data.targetDuration, 10, 'data has correct TARGET DURATION');
190 equal(data.invalidReasons.length, 0, 'data has 1 invalid reasons'); 188 //equal(data.invalidReasons.length, 0, 'data has 1 invalid reasons');
191 }); 189 });
192 190
193 test('NaN target duration', function() { 191 test('NaN target duration', function() {
...@@ -199,9 +197,9 @@ ...@@ -199,9 +197,9 @@
199 console.log(playlistData); 197 console.log(playlistData);
200 console.log(data.targetDuration); 198 console.log(data.targetDuration);
201 notEqual(data, null, 'data is not NULL'); 199 notEqual(data, null, 'data is not NULL');
202 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 200 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
203 equal(data.invalidReasons.length, 1, 'data has 0 invalid reasons'); 201 // equal(data.invalidReasons.length, 1, 'data has 0 invalid reasons');
204 equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'NaN\''); 202 // equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'NaN\'');
205 }); 203 });
206 204
207 test('empty target duration', function() { 205 test('empty target duration', function() {
...@@ -213,9 +211,9 @@ ...@@ -213,9 +211,9 @@
213 console.log(playlistData); 211 console.log(playlistData);
214 console.log(data.targetDuration); 212 console.log(data.targetDuration);
215 notEqual(data, null, 'data is not NULL'); 213 notEqual(data, null, 'data is not NULL');
216 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 214 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
217 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 215 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
218 equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'NaN\''); 216 // equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'NaN\'');
219 }); 217 });
220 218
221 test('undefined target duration', function() { 219 test('undefined target duration', function() {
...@@ -227,9 +225,9 @@ ...@@ -227,9 +225,9 @@
227 console.log(playlistData); 225 console.log(playlistData);
228 console.log(data.targetDuration); 226 console.log(data.targetDuration);
229 notEqual(data, null, 'data is not NULL'); 227 notEqual(data, null, 'data is not NULL');
230 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 228 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
231 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 229 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
232 equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'undefined\''); 230 // equal(data.invalidReasons[0], 'Invalid Target Duration Value: \'undefined\'');
233 231
234 }); 232 });
235 233
...@@ -241,9 +239,9 @@ ...@@ -241,9 +239,9 @@
241 data = m3u8parser.parse(playlistData); 239 data = m3u8parser.parse(playlistData);
242 240
243 notEqual(data, null, 'data is not NULL'); 241 notEqual(data, null, 'data is not NULL');
244 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 242 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
245 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 243 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
246 equal(data.invalidReasons[0], 'Invalid Target Duration Value: 4 is lower than segments'); 244 // equal(data.invalidReasons[0], 'Invalid Target Duration Value: 4 is lower than segments');
247 }); 245 });
248 246
249 /*3.4.3. EXT-X-MEDIA-SEQUENCE 247 /*3.4.3. EXT-X-MEDIA-SEQUENCE
...@@ -277,8 +275,8 @@ ...@@ -277,8 +275,8 @@
277 data = m3u8parser.parse(playlistData); 275 data = m3u8parser.parse(playlistData);
278 276
279 notEqual(data, null, 'data is not NULL'); 277 notEqual(data, null, 'data is not NULL');
280 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 278 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
281 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 279 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
282 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct'); 280 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE is correct');
283 }); 281 });
284 282
...@@ -290,8 +288,8 @@ ...@@ -290,8 +288,8 @@
290 data = m3u8parser.parse(playlistData); 288 data = m3u8parser.parse(playlistData);
291 289
292 notEqual(data, null, 'data is not NULL'); 290 notEqual(data, null, 'data is not NULL');
293 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 291 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
294 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 292 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
295 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE tags after the first should be ignored'); 293 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE tags after the first should be ignored');
296 }); 294 });
297 295
...@@ -303,9 +301,9 @@ ...@@ -303,9 +301,9 @@
303 data = m3u8parser.parse(playlistData); 301 data = m3u8parser.parse(playlistData);
304 302
305 notEqual(data, null, 'data is not NULL'); 303 notEqual(data, null, 'data is not NULL');
306 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 304 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
307 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 305 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
308 equal(data.mediaSequence, 0, 'MEDIA SEQUENCE should default to 0 when not present.'); 306 equal(data.mediaSequence, undefined, 'MEDIA SEQUENCE is undefined');
309 }); 307 });
310 308
311 test('media sequence is empty in the playlist', function() { 309 test('media sequence is empty in the playlist', function() {
...@@ -316,9 +314,9 @@ ...@@ -316,9 +314,9 @@
316 data = m3u8parser.parse(playlistData); 314 data = m3u8parser.parse(playlistData);
317 315
318 notEqual(data, null, 'data is not NULL'); 316 notEqual(data, null, 'data is not NULL');
319 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 317 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
320 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 318 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
321 equal(data.mediaSequence, 0, 'Invalid Media Sequence Value: \'\''); 319 equal(data.mediaSequence, '', 'media sequence is the empty string');
322 }); 320 });
323 321
324 test('media sequence is high (non-zero in first file) in the playlist', function() { 322 test('media sequence is high (non-zero in first file) in the playlist', function() {
...@@ -329,12 +327,12 @@ ...@@ -329,12 +327,12 @@
329 data = m3u8parser.parse(playlistData); 327 data = m3u8parser.parse(playlistData);
330 328
331 notEqual(data, null, 'data is not NULL'); 329 notEqual(data, null, 'data is not NULL');
332 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 330 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
333 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 331 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
334 equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'1\''); 332 // equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'1\'');
335 }); 333 });
336 334
337 test('media sequence (-1) in the playlist', function() { 335 test('handles invalid media sequence numbers in the playlist', function() {
338 var 336 var
339 playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template), 337 playlistTemplate = Handlebars.compile(window.playlist_media_sequence_template),
340 testData = {mediaSequence: '-1'}, 338 testData = {mediaSequence: '-1'},
...@@ -342,9 +340,10 @@ ...@@ -342,9 +340,10 @@
342 data = m3u8parser.parse(playlistData); 340 data = m3u8parser.parse(playlistData);
343 341
344 notEqual(data, null, 'data is not NULL'); 342 notEqual(data, null, 'data is not NULL');
345 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 343 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
346 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 344 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
347 equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'-1\''); 345 // equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'-1\'');
346 equal(data.mediaSequence, -1, 'negative media sequence numbers don\'t break parsing');
348 }); 347 });
349 348
350 test('media sequence invalid (string) in the playlist', function() { 349 test('media sequence invalid (string) in the playlist', function() {
...@@ -355,27 +354,30 @@ ...@@ -355,27 +354,30 @@
355 data = m3u8parser.parse(playlistData); 354 data = m3u8parser.parse(playlistData);
356 355
357 notEqual(data, null, 'data is not NULL'); 356 notEqual(data, null, 'data is not NULL');
358 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 357 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
359 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 358 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
360 equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'asdfkasdkfl\''); 359 // equal(data.invalidReasons[0], 'Invalid Media Sequence Value: \'asdfkasdkfl\'');
361 }); 360 });
362 361
363 module('brightcove playlist', { 362 module('Representative Playlist', {
364 setup: function() { 363 setup: function() {
365 m3u8parser = new window.videojs.hls.M3U8Parser(); 364 m3u8parser = window.videojs.hls.M3U8Parser;
366 } 365 }
367 }); 366 });
368 367
369 test('should parse a brightcove manifest data', function() { 368 test('should parse real manifest data', function() {
370 var data = m3u8parser.parse(window.brightcove_playlist_data); 369 var data = m3u8parser.parse(window.brightcove_playlist_data);
371 370
372 ok(data); 371 ok(data);
373 equal(data.playlistItems.length, 4, 'Has correct rendition count'); 372 equal(data.playlists.length, 4, 'has correct playlist count');
374 equal(data.isPlaylist, true, 'data is parsed as a PLAYLIST as expected'); 373 equal(data.playlists[0].attributes.bandwidth, 240000, 'first rendition index bandwidth is correct');
375 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct'); 374 equal(data.playlists[0].attributes.programId, 1, 'first rendition index program-id is correct');
376 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct'); 375 equal(data.playlists[0].attributes.resolution.width,
377 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct'); 376 396,
378 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct'); 377 'first rendition index resolution width is correct');
378 equal(data.playlists[0].attributes.resolution.height,
379 224,
380 'first rendition index resolution height is correct');
379 381
380 }); 382 });
381 383
...@@ -410,8 +412,8 @@ ...@@ -410,8 +412,8 @@
410 data = m3u8parser.parse(playlistData); 412 data = m3u8parser.parse(playlistData);
411 413
412 notEqual(data, null, 'data is not NULL'); 414 notEqual(data, null, 'data is not NULL');
413 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 415 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
414 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 416 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
415 }); 417 });
416 418
417 test('test valid extinf without associated segment in playlist', function() { 419 test('test valid extinf without associated segment in playlist', function() {
...@@ -422,8 +424,8 @@ ...@@ -422,8 +424,8 @@
422 data = m3u8parser.parse(playlistData); 424 data = m3u8parser.parse(playlistData);
423 425
424 notEqual(data, null, 'data is not NULL'); 426 notEqual(data, null, 'data is not NULL');
425 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 427 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
426 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 428 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
427 //equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF missing segment\''); 429 //equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF missing segment\'');
428 }); 430 });
429 431
...@@ -436,8 +438,8 @@ ...@@ -436,8 +438,8 @@
436 data = m3u8parser.parse(playlistData); 438 data = m3u8parser.parse(playlistData);
437 439
438 notEqual(data, null, 'data is not NULL'); 440 notEqual(data, null, 'data is not NULL');
439 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 441 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
440 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 442 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
441 }); 443 });
442 444
443 //its best practice that every extinf have the same value, but its not required 445 //its best practice that every extinf have the same value, but its not required
...@@ -449,8 +451,8 @@ ...@@ -449,8 +451,8 @@
449 data = m3u8parser.parse(playlistData); 451 data = m3u8parser.parse(playlistData);
450 452
451 notEqual(data, null, 'data is not NULL'); 453 notEqual(data, null, 'data is not NULL');
452 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 454 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
453 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 455 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
454 }); 456 });
455 457
456 //extinf values must be below the target duration 458 //extinf values must be below the target duration
...@@ -462,9 +464,9 @@ ...@@ -462,9 +464,9 @@
462 data = m3u8parser.parse(playlistData); 464 data = m3u8parser.parse(playlistData);
463 465
464 notEqual(data, null, 'data is not NULL'); 466 notEqual(data, null, 'data is not NULL');
465 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 467 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
466 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 468 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
467 equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value higher than #TARGETDURATION\''); 469 // equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value higher than #TARGETDURATION\'');
468 }); 470 });
469 471
470 //extinf values must be below the target duration 472 //extinf values must be below the target duration
...@@ -476,9 +478,9 @@ ...@@ -476,9 +478,9 @@
476 data = m3u8parser.parse(playlistData); 478 data = m3u8parser.parse(playlistData);
477 479
478 notEqual(data, null, 'data is not NULL'); 480 notEqual(data, null, 'data is not NULL');
479 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 481 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
480 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 482 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
481 equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value not an integer\''); 483 // equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value not an integer\'');
482 }); 484 });
483 485
484 //extinf values must be below the target duration 486 //extinf values must be below the target duration
...@@ -490,8 +492,8 @@ ...@@ -490,8 +492,8 @@
490 data = m3u8parser.parse(playlistData); 492 data = m3u8parser.parse(playlistData);
491 493
492 notEqual(data, null, 'data is not NULL'); 494 notEqual(data, null, 'data is not NULL');
493 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 495 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
494 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 496 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
495 }); 497 });
496 498
497 //extinf values must be below the target duration 499 //extinf values must be below the target duration
...@@ -503,9 +505,9 @@ ...@@ -503,9 +505,9 @@
503 data = m3u8parser.parse(playlistData); 505 data = m3u8parser.parse(playlistData);
504 506
505 notEqual(data, null, 'data is not NULL'); 507 notEqual(data, null, 'data is not NULL');
506 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 508 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
507 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 509 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
508 equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value empty\''); 510 // equal(data.invalidReasons[0], 'Invalid Segment Data: \'#EXTINF value empty\'');
509 }); 511 });
510 512
511 /* 513 /*
...@@ -528,8 +530,8 @@ ...@@ -528,8 +530,8 @@
528 data = m3u8parser.parse(playlistData); 530 data = m3u8parser.parse(playlistData);
529 531
530 notEqual(data, null, 'data is not NULL'); 532 notEqual(data, null, 'data is not NULL');
531 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 533 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
532 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 534 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
533 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should be YES'); 535 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should be YES');
534 }); 536 });
535 537
...@@ -541,8 +543,8 @@ ...@@ -541,8 +543,8 @@
541 data = m3u8parser.parse(playlistData); 543 data = m3u8parser.parse(playlistData);
542 544
543 notEqual(data, null, 'data is not NULL'); 545 notEqual(data, null, 'data is not NULL');
544 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 546 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
545 equal(data.invalidReasons.length, 0, 'Errors object should not be empty.'); 547 // equal(data.invalidReasons.length, 0, 'Errors object should not be empty.');
546 equal(data.allowCache, 'NO', 'EXT-X-ALLOW-CACHE should be NO'); 548 equal(data.allowCache, 'NO', 'EXT-X-ALLOW-CACHE should be NO');
547 }); 549 });
548 550
...@@ -554,9 +556,9 @@ ...@@ -554,9 +556,9 @@
554 data = m3u8parser.parse(playlistData); 556 data = m3u8parser.parse(playlistData);
555 557
556 notEqual(data, null, 'data is not NULL'); 558 notEqual(data, null, 'data is not NULL');
557 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 559 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
558 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 560 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
559 equal(data.invalidReasons[0], 'Invalid EXT-X-ALLOW-CACHE value: \'YESTERDAYNO\''); 561 // equal(data.invalidReasons[0], 'Invalid EXT-X-ALLOW-CACHE value: \'YESTERDAYNO\'');
560 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES.'); 562 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES.');
561 }); 563 });
562 564
...@@ -568,9 +570,9 @@ ...@@ -568,9 +570,9 @@
568 data = m3u8parser.parse(playlistData); 570 data = m3u8parser.parse(playlistData);
569 571
570 notEqual(data, null, 'data is not NULL'); 572 notEqual(data, null, 'data is not NULL');
571 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 573 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
572 equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons'); 574 // equal(data.invalidReasons.length, 1, 'data has 1 invalid reasons');
573 equal(data.invalidReasons[0], 'Invalid EXT-X-ALLOW-CACHE value: \'\''); 575 // equal(data.invalidReasons[0], 'Invalid EXT-X-ALLOW-CACHE value: \'\'');
574 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES.'); 576 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES.');
575 }); 577 });
576 578
...@@ -582,8 +584,8 @@ ...@@ -582,8 +584,8 @@
582 data = m3u8parser.parse(playlistData); 584 data = m3u8parser.parse(playlistData);
583 585
584 notEqual(data, null, 'data is not NULL'); 586 notEqual(data, null, 'data is not NULL');
585 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 587 // notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
586 equal(data.invalidReasons.length, 1, 'No EXT-X-ALLOW-CACHE specified. Default: YES.'); 588 // equal(data.invalidReasons.length, 1, 'No EXT-X-ALLOW-CACHE specified. Default: YES.');
587 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES'); 589 equal(data.allowCache, 'YES', 'EXT-X-ALLOW-CACHE should default to YES');
588 }); 590 });
589 591
...@@ -595,13 +597,13 @@ ...@@ -595,13 +597,13 @@
595 data = m3u8parser.parse(playlistData); 597 data = m3u8parser.parse(playlistData);
596 598
597 notEqual(data, null, 'data is not NULL'); 599 notEqual(data, null, 'data is not NULL');
598 notEqual(data.invalidReasons, null, 'invalidReasons is not NULL'); 600 //notEqual(data.invalidReasons, null, 'invalidReasons is not NULL');
599 equal(data.invalidReasons.length, 0, 'Errors object should be empty.'); 601 //equal(data.invalidReasons.length, 0, 'Errors object should be empty.');
600 //TODO: Validate the byteRange info 602 //TODO: Validate the byteRange info
601 equal(data.mediaItems.length, 16, '16 segments should have been parsed.'); 603 equal(data.segments.length, 16, '16 segments should have been parsed.');
602 equal(data.mediaItems[0].byterange, testData.byteRange, 'byteRange incorrect.'); 604 equal(data.segments[0].byterange, testData.byteRange, 'byteRange incorrect.');
603 equal(data.mediaItems[1].byterange, testData.byteRange1, 'byteRange1 incorrect.'); 605 equal(data.segments[1].byterange, testData.byteRange1, 'byteRange1 incorrect.');
604 equal(data.mediaItems[15].byterange, testData.byteRange2, 'byteRange2 incorrect.'); 606 equal(data.segments[15].byterange, testData.byteRange2, 'byteRange2 incorrect.');
605 }); 607 });
606 608
607 test('test EXT-X-BYTERANGE used but version is < 4', function() { 609 test('test EXT-X-BYTERANGE used but version is < 4', function() {
...@@ -612,11 +614,11 @@ ...@@ -612,11 +614,11 @@
612 data = m3u8parser.parse(playlistData); 614 data = m3u8parser.parse(playlistData);
613 615
614 notEqual(data, null, 'data is not NULL'); 616 notEqual(data, null, 'data is not NULL');
615 equal(data.mediaItems.length, 16, '16 segments should have been parsed.'); 617 equal(data.segments.length, 16, '16 segments should have been parsed.');
616 notEqual(data.invalidReasons, null, 'there should be an error'); 618 // notEqual(data.invalidReasons, null, 'there should be an error');
617 equal(data.invalidReasons.length, 1, 'there should be 1 error'); 619 // equal(data.invalidReasons.length, 1, 'there should be 1 error');
618 //TODO: Validate the byteRange info 620 // //TODO: Validate the byteRange info
619 equal(data.invalidReasons[0], 'EXT-X-BYTERANGE used but version is < 4.') 621 // equal(data.invalidReasons[0], 'EXT-X-BYTERANGE used but version is < 4.');x
620 }); 622 });
621 623
622 })(this);
...\ No newline at end of file ...\ No newline at end of file
624 })(window, window.console);
......
...@@ -44,7 +44,7 @@ window.playlistData = '#EXTM3U\n'+ ...@@ -44,7 +44,7 @@ window.playlistData = '#EXTM3U\n'+
44 'hls_450k_video.ts\n' + 44 'hls_450k_video.ts\n' +
45 '#EXTINF:10,\n' + 45 '#EXTINF:10,\n' +
46 '#EXT-X-BYTERANGE:468684@7108092\n' + 46 '#EXT-X-BYTERANGE:468684@7108092\n' +
47 'hls_450k_video.ts' + 47 'hls_450k_video.ts\n' +
48 '#EXTINF:10,\n' + 48 '#EXTINF:10,\n' +
49 '#EXT-X-BYTERANGE:444996@7576776\n' + 49 '#EXT-X-BYTERANGE:444996@7576776\n' +
50 'hls_450k_video.ts\n' + 50 'hls_450k_video.ts\n' +
......
...@@ -46,7 +46,7 @@ window.playlist_byte_range = '#EXTM3U\n'+ ...@@ -46,7 +46,7 @@ window.playlist_byte_range = '#EXTM3U\n'+
46 'hls_450k_video.ts\n' + 46 'hls_450k_video.ts\n' +
47 '#EXTINF:10,\n' + 47 '#EXTINF:10,\n' +
48 '#EXT-X-BYTERANGE:468684@7108092\n' + 48 '#EXT-X-BYTERANGE:468684@7108092\n' +
49 'hls_450k_video.ts' + 49 'hls_450k_video.ts\n' +
50 '#EXTINF:10,\n' + 50 '#EXTINF:10,\n' +
51 '#EXT-X-BYTERANGE:444996@7576776\n' + 51 '#EXT-X-BYTERANGE:444996@7576776\n' +
52 'hls_450k_video.ts\n' + 52 'hls_450k_video.ts\n' +
......
1 var fs = require('fs');
2 var path = require('path');
3 var manifest = fs.readFileSync(__dirname + '/fixtures/prog_index.m3u8').toString();
4 var parser = require('../src/m3u8/m3u8-generated.js');
5 var parsed = parser.parse(manifest);
6 console.log(parsed);
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
24 <!-- M3U8 --> 24 <!-- M3U8 -->
25 <script src="../src/m3u8/m3u8.js"></script> 25 <script src="../src/m3u8/m3u8.js"></script>
26 <script src="../src/m3u8/m3u8-tag-types.js"></script> 26 <script src="../src/m3u8/m3u8-tag-types.js"></script>
27 <script src="../src/m3u8/m3u8-parser.js"></script> 27 <script src="../build/m3u8-parser.js"></script>
28 <script src="../src/manifest-controller.js"></script> 28 <script src="../src/manifest-controller.js"></script>
29 <!-- M3U8 TEST DATA --> 29 <!-- M3U8 TEST DATA -->
30 <script src="manifest/playlistM3U8data.js"></script> 30 <script src="manifest/playlistM3U8data.js"></script>
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
43 43
44 <script src="../src/bin-utils.js"></script> 44 <script src="../src/bin-utils.js"></script>
45 45
46 <!-- Test cases -->
46 <script src="video-js-hls_test.js"></script> 47 <script src="video-js-hls_test.js"></script>
47 <script src="exp-golomb_test.js"></script> 48 <script src="exp-golomb_test.js"></script>
48 <script src="flv-tag_test.js"></script> 49 <script src="flv-tag_test.js"></script>
......