diff --git a/docs/multiple-alternative-audio-tracks.md b/docs/multiple-alternative-audio-tracks.md index aa4fe6544..f19c321fe 100644 --- a/docs/multiple-alternative-audio-tracks.md +++ b/docs/multiple-alternative-audio-tracks.md @@ -15,7 +15,7 @@ The mapping between `AudioTrack` and the parsed m3u8 file is fairly straight fo As you can see m3u8's do not have a property for `AudioTrack.id`, which means that we let `video.js` randomly generate the id for `AudioTrack`s. This will have no real impact on any part of the system as we do not use the `id` anywhere. -The other property that does not have a mapping in the m3u8 is `AudioTrack.kind`. It was decided that we would set the `kind` to `main` when `default` is set to `true` and in all other cases we set it to `alternative` +The other property that does not have a mapping in the m3u8 is `AudioTrack.kind`. It was decided that we would set the `kind` to `main` when `default` is set to `true` and in other cases we set it to `alternative` unless the track has `characteristics` which include `public.accessibility.describes-video`, in which case we set it to `main-desc` (note that this `kind` indicates that the track is a mix of the main track and description, so it can be played *instead* of the main track; a track with kind `description` *only* has the description, not the main track). Below is a basic example of a mapping m3u8 layout @@ -27,8 +27,13 @@ m3u8 layout lang: 'eng' }, 'audio-track-2': { - default: true, + default: false, lang: 'fr' + }, + 'audio-track-3': { + default: false, + lang: 'eng', + characteristics: 'public.accessibility.describes-video' } }] } @@ -48,6 +53,12 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have language: 'fr', kind: 'alternative', id: 'random' +}, { + label: 'audio-tracks-3', + enabled: false, + language: 'eng', + kind: 'main-desc', + id: 'random' }] ``` @@ -83,4 +94,3 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have 1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio 1. `MasterPlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (master or audio only) 1. `MediaSource`/`mux.js` determine how to mux - diff --git a/package.json b/package.json index 6723d5b26..ef2bf838e 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "dependencies": { "aes-decrypter": "1.0.3", "global": "^4.3.0", - "m3u8-parser": "2.0.1", + "m3u8-parser": "2.1.0", "mux.js": "4.1.1", "url-toolkit": "1.0.9", "video.js": "^5.17.0", diff --git a/src/master-playlist-controller.js b/src/master-playlist-controller.js index 919145b28..4864821a8 100644 --- a/src/master-playlist-controller.js +++ b/src/master-playlist-controller.js @@ -557,7 +557,7 @@ export class MasterPlaylistController extends videojs.EventTarget { let properties = mediaGroups.AUDIO[mediaGroup][label]; let track = new videojs.AudioTrack({ id: label, - kind: properties.default ? 'main' : 'alternative', + kind: this.audioTrackKind_(properties), enabled: false, language: properties.language, label @@ -574,6 +574,22 @@ export class MasterPlaylistController extends videojs.EventTarget { })[0] || this.activeAudioGroup()[0]).enabled = true; } + /** + * Convert the properties of an HLS track into an audioTrackKind. + * + * @private + */ + audioTrackKind_(properties) { + let kind = properties.default ? 'main' : 'alternative'; + + if (properties.characteristics && + properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) { + kind = 'main-desc'; + } + + return kind; + } + /** * Call load on our SegmentLoaders */ diff --git a/test/master-playlist-controller.test.js b/test/master-playlist-controller.test.js index f839db530..2134c2d22 100644 --- a/test/master-playlist-controller.test.js +++ b/test/master-playlist-controller.test.js @@ -990,6 +990,40 @@ QUnit.test('sends decrypter messages to correct segment loader', function(assert assert.deepEqual(mainHandleDecryptedCalls[0], { source: 'main' }, 'sent data'); }); +QUnit.test('correctly sets alternate audio track kinds', function(assert) { + this.requests.length = 0; + this.player = createPlayer(); + this.player.src({ + src: 'manifest/alternate-audio-accessibility.m3u8', + type: 'application/vnd.apple.mpegurl' + }); + + // master + this.standardXHRResponse(this.requests.shift()); + // media - required for loadedmetadata + this.standardXHRResponse(this.requests.shift()); + + const audioTracks = this.player.tech_.audioTracks(); + + assert.equal(audioTracks.length, 4, 'added 4 audio tracks'); + assert.equal(audioTracks[0].id, 'English', 'contains english track'); + assert.equal(audioTracks[0].kind, 'main', 'english track\'s kind is "main"'); + assert.equal(audioTracks[1].id, + 'English Descriptions', + 'contains english descriptions track'); + assert.equal(audioTracks[1].kind, + 'main-desc', + 'english descriptions track\'s kind is "main-desc"'); + assert.equal(audioTracks[2].id, 'Français', 'contains french track'); + assert.equal(audioTracks[2].kind, + 'alternative', + 'french track\'s kind is "alternative"'); + assert.equal(audioTracks[3].id, 'Espanol', 'contains spanish track'); + assert.equal(audioTracks[3].kind, + 'alternative', + 'spanish track\'s kind is "alternative"'); +}); + QUnit.module('Codec to MIME Type Conversion'); QUnit.test('recognizes muxed codec configurations', function(assert) { diff --git a/utils/manifest/alternate-audio-accessibility.m3u8 b/utils/manifest/alternate-audio-accessibility.m3u8 new file mode 100644 index 000000000..fd21e3a32 --- /dev/null +++ b/utils/manifest/alternate-audio-accessibility.m3u8 @@ -0,0 +1,10 @@ +#EXTM3U +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",LANGUAGE="eng",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",LANGUAGE="eng",NAME="English Descriptions",AUTOSELECT=YES,DEFAULT=NO,CHARACTERISTICS="public.accessibility.describes-video",URI="eng_desc/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",LANGUAGE="fre",NAME="Français",AUTOSELECT=YES,DEFAULT=NO,URI="fre/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",LANGUAGE="sp",NAME="Espanol",AUTOSELECT=YES,DEFAULT=NO,URI="sp/prog_index.m3u8" + +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=195023,CODECS="avc1.42e00a,mp4a.40.2",AUDIO="audio" +lo/prog_index.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=591680,CODECS="avc1.42e01e,mp4a.40.2",AUDIO="audio" +hi/prog_index.m3u8