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.
Showing
3 changed files
with
86 additions
and
28 deletions
... | @@ -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, | ... | ... |
-
Please register or sign in to post a comment