Pass codecs along into source buffer creation
If a codecs attribute is present on a variant stream, use it when adding a source buffer. Wait to create the source buffer until the variant playlist is downloaded and the media source is open.
Showing
3 changed files
with
77 additions
and
24 deletions
... | @@ -180,6 +180,8 @@ videojs.Hls.prototype.src = function(src) { | ... | @@ -180,6 +180,8 @@ videojs.Hls.prototype.src = function(src) { |
180 | }); | 180 | }); |
181 | } | 181 | } |
182 | 182 | ||
183 | this.setupSourceBuffer_(); | ||
184 | |||
183 | selectedPlaylist = this.selectPlaylist(); | 185 | selectedPlaylist = this.selectPlaylist(); |
184 | oldBitrate = oldMediaPlaylist.attributes && | 186 | oldBitrate = oldMediaPlaylist.attributes && |
185 | oldMediaPlaylist.attributes.BANDWIDTH || 0; | 187 | oldMediaPlaylist.attributes.BANDWIDTH || 0; |
... | @@ -281,16 +283,7 @@ videojs.Hls.getMediaIndexForLive_ = function(selectedPlaylist) { | ... | @@ -281,16 +283,7 @@ videojs.Hls.getMediaIndexForLive_ = function(selectedPlaylist) { |
281 | }; | 283 | }; |
282 | 284 | ||
283 | videojs.Hls.prototype.handleSourceOpen = function() { | 285 | videojs.Hls.prototype.handleSourceOpen = function() { |
284 | this.sourceBuffer = this.mediaSource.addSourceBuffer('video/mp2t'); | 286 | this.setupSourceBuffer_(); |
285 | |||
286 | // transition the sourcebuffer to the ended state if we've hit the end of | ||
287 | // the playlist | ||
288 | this.sourceBuffer.addEventListener('updateend', function() { | ||
289 | if (this.duration() !== Infinity && | ||
290 | this.mediaIndex === this.playlists.media().segments.length) { | ||
291 | this.mediaSource.endOfStream(); | ||
292 | } | ||
293 | }.bind(this)); | ||
294 | 287 | ||
295 | // if autoplay is enabled, begin playback. This is duplicative of | 288 | // if autoplay is enabled, begin playback. This is duplicative of |
296 | // code in video.js but is required because play() must be invoked | 289 | // code in video.js but is required because play() must be invoked |
... | @@ -303,6 +296,31 @@ videojs.Hls.prototype.handleSourceOpen = function() { | ... | @@ -303,6 +296,31 @@ videojs.Hls.prototype.handleSourceOpen = function() { |
303 | } | 296 | } |
304 | }; | 297 | }; |
305 | 298 | ||
299 | videojs.Hls.prototype.setupSourceBuffer_ = function() { | ||
300 | var media = this.playlists.media(), mimeType; | ||
301 | |||
302 | // wait until a media playlist is available and the Media Source is | ||
303 | // attached | ||
304 | if (!media || this.mediaSource.readyState !== 'open') { | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | mimeType = 'video/mp2t'; | ||
309 | if (media.attributes && media.attributes.CODECS) { | ||
310 | mimeType += '; codecs="' + media.attributes.CODECS + '"'; | ||
311 | } | ||
312 | this.sourceBuffer = this.mediaSource.addSourceBuffer(mimeType); | ||
313 | |||
314 | // transition the sourcebuffer to the ended state if we've hit the end of | ||
315 | // the playlist | ||
316 | this.sourceBuffer.addEventListener('updateend', function() { | ||
317 | if (this.duration() !== Infinity && | ||
318 | this.mediaIndex === this.playlists.media().segments.length) { | ||
319 | this.mediaSource.endOfStream(); | ||
320 | } | ||
321 | }.bind(this)); | ||
322 | }; | ||
323 | |||
306 | // register event listeners to transform in-band metadata events into | 324 | // register event listeners to transform in-band metadata events into |
307 | // VTTCues on a text track | 325 | // VTTCues on a text track |
308 | videojs.Hls.prototype.setupMetadataCueTranslation_ = function() { | 326 | videojs.Hls.prototype.setupMetadataCueTranslation_ = function() { |
... | @@ -310,12 +328,6 @@ videojs.Hls.prototype.setupMetadataCueTranslation_ = function() { | ... | @@ -310,12 +328,6 @@ videojs.Hls.prototype.setupMetadataCueTranslation_ = function() { |
310 | metadataStream = this.segmentParser_.metadataStream, | 328 | metadataStream = this.segmentParser_.metadataStream, |
311 | textTrack; | 329 | textTrack; |
312 | 330 | ||
313 | // only expose metadata tracks to video.js versions that support | ||
314 | // dynamic text tracks (4.12+) | ||
315 | if (!this.tech_.addTextTrack) { | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | // add a metadata cue whenever a metadata event is triggered during | 331 | // add a metadata cue whenever a metadata event is triggered during |
320 | // segment parsing | 332 | // segment parsing |
321 | metadataStream.on('data', function(metadata) { | 333 | metadataStream.on('data', function(metadata) { | ... | ... |
... | @@ -480,6 +480,16 @@ | ... | @@ -480,6 +480,16 @@ |
480 | strictEqual(element.tagType, 'stream-inf', 'the tag type is stream-inf'); | 480 | strictEqual(element.tagType, 'stream-inf', 'the tag type is stream-inf'); |
481 | strictEqual(element.attributes.RESOLUTION.width, 396, 'width is parsed'); | 481 | strictEqual(element.attributes.RESOLUTION.width, 396, 'width is parsed'); |
482 | strictEqual(element.attributes.RESOLUTION.height, 224, 'heigth is parsed'); | 482 | strictEqual(element.attributes.RESOLUTION.height, 224, 'heigth is parsed'); |
483 | |||
484 | manifest = '#EXT-X-STREAM-INF:CODECS="avc1.4d400d, mp4a.40.2"\n'; | ||
485 | lineStream.push(manifest); | ||
486 | |||
487 | ok(element, 'an event was triggered'); | ||
488 | strictEqual(element.type, 'tag', 'the line type is tag'); | ||
489 | strictEqual(element.tagType, 'stream-inf', 'the tag type is stream-inf'); | ||
490 | strictEqual(element.attributes.CODECS, | ||
491 | 'avc1.4d400d, mp4a.40.2', | ||
492 | 'codecs are parsed'); | ||
483 | }); | 493 | }); |
484 | test('parses #EXT-X-STREAM-INF with arbitrary attributes', function() { | 494 | test('parses #EXT-X-STREAM-INF with arbitrary attributes', function() { |
485 | var | 495 | var | ... | ... |
... | @@ -383,6 +383,26 @@ test('duration is set when the source opens after the playlist is loaded', funct | ... | @@ -383,6 +383,26 @@ test('duration is set when the source opens after the playlist is loaded', funct |
383 | equal(player.tech.hls.mediaSource.duration , 40, 'set the duration'); | 383 | equal(player.tech.hls.mediaSource.duration , 40, 'set the duration'); |
384 | }); | 384 | }); |
385 | 385 | ||
386 | test('codecs are passed to the source buffer', function() { | ||
387 | var codecs = []; | ||
388 | player.src({ | ||
389 | src: 'custom-codecs.m3u8', | ||
390 | type: 'application/vnd.apple.mpegurl' | ||
391 | }); | ||
392 | openMediaSource(player); | ||
393 | player.tech.hls.mediaSource.addSourceBuffer = function(codec) { | ||
394 | codecs.push(codec); | ||
395 | }; | ||
396 | |||
397 | requests.shift().respond(200, null, | ||
398 | '#EXTM3U\n' + | ||
399 | '#EXT-X-STREAM-INF:CODECS="video, audio"\n' + | ||
400 | 'media.m3u8\n'); | ||
401 | standardXHRResponse(requests.shift()); | ||
402 | equal(codecs.length, 1, 'created a source buffer'); | ||
403 | equal(codecs[0], 'video/mp2t; codecs="video, audio"', 'specified the codecs'); | ||
404 | }); | ||
405 | |||
386 | test('including HLS as a tech does not error', function() { | 406 | test('including HLS as a tech does not error', function() { |
387 | var player = createPlayer({ | 407 | var player = createPlayer({ |
388 | techOrder: ['hls', 'html5'] | 408 | techOrder: ['hls', 'html5'] |
... | @@ -565,11 +585,11 @@ test('re-initializes the handler for each source', function() { | ... | @@ -565,11 +585,11 @@ test('re-initializes the handler for each source', function() { |
565 | openMediaSource(player); | 585 | openMediaSource(player); |
566 | firstPlaylists = player.tech.hls.playlists; | 586 | firstPlaylists = player.tech.hls.playlists; |
567 | firstMSE = player.tech.hls.mediaSource; | 587 | firstMSE = player.tech.hls.mediaSource; |
588 | standardXHRResponse(requests.shift()); | ||
589 | standardXHRResponse(requests.shift()); | ||
568 | player.tech.hls.sourceBuffer.abort = function() { | 590 | player.tech.hls.sourceBuffer.abort = function() { |
569 | aborts++; | 591 | aborts++; |
570 | }; | 592 | }; |
571 | standardXHRResponse(requests.shift()); | ||
572 | standardXHRResponse(requests.shift()); | ||
573 | 593 | ||
574 | player.src({ | 594 | player.src({ |
575 | src: 'manifest/master.m3u8', | 595 | src: 'manifest/master.m3u8', |
... | @@ -2346,6 +2366,17 @@ test('aborts the source buffer on disposal', function() { | ... | @@ -2346,6 +2366,17 @@ test('aborts the source buffer on disposal', function() { |
2346 | type: 'application/vnd.apple.mpegurl' | 2366 | type: 'application/vnd.apple.mpegurl' |
2347 | }); | 2367 | }); |
2348 | openMediaSource(player); | 2368 | openMediaSource(player); |
2369 | player.dispose(); | ||
2370 | ok(true, 'disposed before creating the source buffer'); | ||
2371 | requests.length = 0; | ||
2372 | |||
2373 | player = createPlayer(); | ||
2374 | player.src({ | ||
2375 | src: 'manifest/media.m3u8', | ||
2376 | type: 'application/vnd.apple.mpegurl' | ||
2377 | }); | ||
2378 | openMediaSource(player); | ||
2379 | standardXHRResponse(requests.shift()); | ||
2349 | player.tech.hls.sourceBuffer.abort = function() { | 2380 | player.tech.hls.sourceBuffer.abort = function() { |
2350 | aborts++; | 2381 | aborts++; |
2351 | }; | 2382 | }; |
... | @@ -2708,9 +2739,6 @@ test('skip segments if key requests fail more than once', function() { | ... | @@ -2708,9 +2739,6 @@ test('skip segments if key requests fail more than once', function() { |
2708 | type: 'application/vnd.apple.mpegurl' | 2739 | type: 'application/vnd.apple.mpegurl' |
2709 | }); | 2740 | }); |
2710 | openMediaSource(player); | 2741 | openMediaSource(player); |
2711 | player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { | ||
2712 | bytes.push(chunk); | ||
2713 | }; | ||
2714 | player.tech.trigger('play'); | 2742 | player.tech.trigger('play'); |
2715 | 2743 | ||
2716 | requests.shift().respond(200, null, | 2744 | requests.shift().respond(200, null, |
... | @@ -2721,6 +2749,9 @@ test('skip segments if key requests fail more than once', function() { | ... | @@ -2721,6 +2749,9 @@ test('skip segments if key requests fail more than once', function() { |
2721 | '#EXT-X-KEY:METHOD=AES-128,URI="htts://priv.example.com/key.php?r=53"\n' + | 2749 | '#EXT-X-KEY:METHOD=AES-128,URI="htts://priv.example.com/key.php?r=53"\n' + |
2722 | '#EXTINF:15.0,\n' + | 2750 | '#EXTINF:15.0,\n' + |
2723 | 'http://media.example.com/fileSequence53-A.ts\n'); | 2751 | 'http://media.example.com/fileSequence53-A.ts\n'); |
2752 | player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { | ||
2753 | bytes.push(chunk); | ||
2754 | }; | ||
2724 | standardXHRResponse(requests.shift()); // segment 1 | 2755 | standardXHRResponse(requests.shift()); // segment 1 |
2725 | requests.shift().respond(404); // fail key | 2756 | requests.shift().respond(404); // fail key |
2726 | requests.shift().respond(404); // fail key, again | 2757 | requests.shift().respond(404); // fail key, again |
... | @@ -2878,9 +2909,6 @@ test('treats invalid keys as a key request failure', function() { | ... | @@ -2878,9 +2909,6 @@ test('treats invalid keys as a key request failure', function() { |
2878 | type: 'application/vnd.apple.mpegurl' | 2909 | type: 'application/vnd.apple.mpegurl' |
2879 | }); | 2910 | }); |
2880 | openMediaSource(player); | 2911 | openMediaSource(player); |
2881 | player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { | ||
2882 | bytes.push(chunk); | ||
2883 | }; | ||
2884 | player.tech.trigger('play'); | 2912 | player.tech.trigger('play'); |
2885 | requests.shift().respond(200, null, | 2913 | requests.shift().respond(200, null, |
2886 | '#EXTM3U\n' + | 2914 | '#EXTM3U\n' + |
... | @@ -2891,6 +2919,9 @@ test('treats invalid keys as a key request failure', function() { | ... | @@ -2891,6 +2919,9 @@ test('treats invalid keys as a key request failure', function() { |
2891 | '#EXT-X-KEY:METHOD=NONE\n' + | 2919 | '#EXT-X-KEY:METHOD=NONE\n' + |
2892 | '#EXTINF:15.0,\n' + | 2920 | '#EXTINF:15.0,\n' + |
2893 | 'http://media.example.com/fileSequence52-B.ts\n'); | 2921 | 'http://media.example.com/fileSequence52-B.ts\n'); |
2922 | player.tech.hls.sourceBuffer.appendBuffer = function(chunk) { | ||
2923 | bytes.push(chunk); | ||
2924 | }; | ||
2894 | // segment request | 2925 | // segment request |
2895 | standardXHRResponse(requests.shift()); | 2926 | standardXHRResponse(requests.shift()); |
2896 | // keys should be 16 bytes long | 2927 | // keys should be 16 bytes long | ... | ... |
-
Please register or sign in to post a comment