ea13a59a by David LaPalomento

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.
1 parent e8371f66
...@@ -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) {
......
...@@ -207,7 +207,7 @@ ...@@ -207,7 +207,7 @@
207 'the title is parsed'); 207 'the title is parsed');
208 }); 208 });
209 test('parses #EXTINF tags with carriage returns', function() { 209 test('parses #EXTINF tags with carriage returns', function() {
210 var 210 var
211 manifest = '#EXTINF:13,Does anyone really use the title attribute?\r\n', 211 manifest = '#EXTINF:13,Does anyone really use the title attribute?\r\n',
212 element; 212 element;
213 parseStream.on('data', function(elem) { 213 parseStream.on('data', function(elem) {
...@@ -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
......