2f979027 by David LaPalomento

Try a whole bunch of places to figure out if the HLS plugin should activate

Instead of only supporting the src attribute, check options.sources for HLS on init. This means re-implementing something that looks a lot like video.js's source selection algorithm. That's another reason to consider converting this plugin into a tech but I'm deferring that for now.
1 parent 16b9cb9d
...@@ -42,8 +42,10 @@ ...@@ -42,8 +42,10 @@
42 class="video-js vjs-default-skin" 42 class="video-js vjs-default-skin"
43 height="300" 43 height="300"
44 width="600" 44 width="600"
45 src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8"
46 controls> 45 controls>
46 <source
47 src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8"
48 type="application/x-mpegURL">
47 </video> 49 </video>
48 <script> 50 <script>
49 videojs.options.flash.swf = 'node_modules/video.js/dist/video-js/video-js.swf'; 51 videojs.options.flash.swf = 'node_modules/video.js/dist/video-js/video-js.swf';
......
...@@ -182,7 +182,6 @@ var ...@@ -182,7 +182,6 @@ var
182 mediaSource = new videojs.MediaSource(), 182 mediaSource = new videojs.MediaSource(),
183 segmentParser = new videojs.hls.SegmentParser(), 183 segmentParser = new videojs.hls.SegmentParser(),
184 player = this, 184 player = this,
185 currentSrc,
186 extname, 185 extname,
187 srcUrl, 186 srcUrl,
188 187
...@@ -195,23 +194,60 @@ var ...@@ -195,23 +194,60 @@ var
195 return; 194 return;
196 } 195 }
197 196
198 currentSrc = player.currentSrc(); 197 srcUrl = (function() {
199 // when the video element is initializing, currentSrc may be undefined 198 var
200 // grab the src from the video element because video.js doesn't currently 199 extname,
201 // expose it 200 i = 0,
202 if (!currentSrc) { 201 j = 0,
203 currentSrc = player.el().querySelector('.vjs-tech').src; 202 src = player.el().querySelector('.vjs-tech').src,
204 } 203 sources = player.options().sources,
204 techName,
205 length = sources.length;
206
207 // use the URL specified in options if one was provided
208 if (typeof options === 'string') {
209 return options;
210 } else if (options) {
211 return options.url;
212 }
205 213
206 extname = (/[^#?]*(?:\/[^#?]*\.([^#?]*))/).exec(currentSrc); 214 // src attributes take precedence over source children
207 if (typeof options === 'string') { 215 if (src) {
208 srcUrl = options; 216
209 } else if (options) { 217 // assume files with the m3u8 extension are HLS
210 srcUrl = options.url; 218 extname = (/[^#?]*(?:\/[^#?]*\.([^#?]*))/).exec(src);
211 } else if (extname && extname[1] === 'm3u8') { 219 if (extname && extname[1] === 'm3u8') {
212 // if the currentSrc looks like an m3u8, attempt to use it 220 return src;
213 srcUrl = currentSrc; 221 }
214 } else { 222 return;
223 }
224
225 // find the first playable source
226 for (; i < length; i++) {
227
228 // ignore sources without a specified type
229 if (!sources[i].type) {
230 continue;
231 }
232
233 // do nothing if the source is handled by one of the standard techs
234 for (j in player.options().techOrder) {
235 techName = player.options().techOrder[j];
236 techName = techName[0].toUpperCase() + techName.substring(1);
237 if (videojs[techName].canPlaySource({ type: sources[i].type })) {
238 return;
239 }
240 }
241
242 // use the plugin if the MIME type specifies HLS
243 if ((/application\/x-mpegURL/).test(sources[i].type) ||
244 (/application\/vnd\.apple\.mpegURL/).test(sources[i].type)) {
245 return sources[i].src;
246 }
247 }
248 })();
249
250 if (!srcUrl) {
215 // do nothing until the plugin is initialized with a valid URL 251 // do nothing until the plugin is initialized with a valid URL
216 videojs.log('hls: no valid playlist URL specified'); 252 videojs.log('hls: no valid playlist URL specified');
217 return; 253 return;
...@@ -546,10 +582,10 @@ var ...@@ -546,10 +582,10 @@ var
546 player.hls.mediaIndex = 0; 582 player.hls.mediaIndex = 0;
547 downloadPlaylist(srcUrl); 583 downloadPlaylist(srcUrl);
548 }); 584 });
549 player.src({ 585 player.src([{
550 src: videojs.URL.createObjectURL(mediaSource), 586 src: videojs.URL.createObjectURL(mediaSource),
551 type: "video/flv" 587 type: "video/flv"
552 }); 588 }]);
553 }; 589 };
554 590
555 videojs.plugin('hls', function() { 591 videojs.plugin('hls', function() {
......
...@@ -506,9 +506,9 @@ test('only makes one segment request at a time', function() { ...@@ -506,9 +506,9 @@ test('only makes one segment request at a time', function() {
506 strictEqual(1, openedXhrs, 'only one XHR is made'); 506 strictEqual(1, openedXhrs, 'only one XHR is made');
507 }); 507 });
508 508
509 test('uses the currentSrc if no options are provided and it ends in ".m3u8"', function() { 509 test('uses the src attribute if no options are provided and it ends in ".m3u8"', function() {
510 var url = 'http://example.com/services/mobile/streaming/index/master.m3u8?videoId=1824650741001'; 510 var url = 'http://example.com/services/mobile/streaming/index/master.m3u8?videoId=1824650741001';
511 player.src(url); 511 player.el().querySelector('.vjs-tech').src = url;
512 player.hls(); 512 player.hls();
513 videojs.mediaSources[player.currentSrc()].trigger({ 513 videojs.mediaSources[player.currentSrc()].trigger({
514 type: 'sourceopen' 514 type: 'sourceopen'
...@@ -517,33 +517,53 @@ test('uses the currentSrc if no options are provided and it ends in ".m3u8"', fu ...@@ -517,33 +517,53 @@ test('uses the currentSrc if no options are provided and it ends in ".m3u8"', fu
517 strictEqual(url, xhrUrls[0], 'currentSrc is used'); 517 strictEqual(url, xhrUrls[0], 'currentSrc is used');
518 }); 518 });
519 519
520 test('ignores currentSrc if it doesn\'t have the "m3u8" extension', function() { 520 test('ignores src attribute if it doesn\'t have the "m3u8" extension', function() {
521 player.src('basdfasdfasdfliel//.m3u9'); 521 var tech = player.el().querySelector('.vjs-tech');
522 tech.src = 'basdfasdfasdfliel//.m3u9';
522 player.hls(); 523 player.hls();
523 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created'); 524 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created');
524 strictEqual(xhrUrls.length, 0, 'no request is made'); 525 strictEqual(xhrUrls.length, 0, 'no request is made');
525 526
526 player.src(''); 527 tech.src = '';
527 player.hls(); 528 player.hls();
528 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created'); 529 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created');
529 strictEqual(xhrUrls.length, 0, 'no request is made'); 530 strictEqual(xhrUrls.length, 0, 'no request is made');
530 531
531 player.src('http://example.com/movie.mp4?q=why.m3u8'); 532 tech.src = 'http://example.com/movie.mp4?q=why.m3u8';
532 player.hls(); 533 player.hls();
533 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created'); 534 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created');
534 strictEqual(xhrUrls.length, 0, 'no request is made'); 535 strictEqual(xhrUrls.length, 0, 'no request is made');
535 536
536 player.src('http://example.m3u8/movie.mp4'); 537 tech.src = 'http://example.m3u8/movie.mp4';
537 player.hls(); 538 player.hls();
538 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created'); 539 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created');
539 strictEqual(xhrUrls.length, 0, 'no request is made'); 540 strictEqual(xhrUrls.length, 0, 'no request is made');
540 541
541 player.src('//example.com/movie.mp4#http://tricky.com/master.m3u8'); 542 tech.src = '//example.com/movie.mp4#http://tricky.com/master.m3u8';
542 player.hls(); 543 player.hls();
543 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created'); 544 ok(!(player.currentSrc() in videojs.mediaSources), 'no media source is created');
544 strictEqual(xhrUrls.length, 0, 'no request is made'); 545 strictEqual(xhrUrls.length, 0, 'no request is made');
545 }); 546 });
546 547
548 test('activates if the first playable source is HLS', function() {
549 document.querySelector('#qunit-fixture').innerHTML =
550 '<video controls>' +
551 '<source type="slartibartfast$%" src="movie.slarti">' +
552 '<source type="application/x-mpegURL" src="movie.m3u8">' +
553 '<source type="video/mp4" src="movie.mp4">' +
554 '</video>';
555 video = document.querySelector('#qunit-fixture video');
556 player = videojs(video, {
557 flash: {
558 swf: '../node_modules/video.js/dist/video-js/video-js.swf'
559 },
560 techOrder: ['flash']
561 });
562 player.hls();
563
564 ok(player.currentSrc() in videojs.mediaSources, 'media source created');
565 });
566
547 test('cancels outstanding XHRs when seeking', function() { 567 test('cancels outstanding XHRs when seeking', function() {
548 var 568 var
549 aborted = false, 569 aborted = false,
......