diff --git a/player/videojs.coffee b/player/videojs.coffee
index 1b50a086..b904d324 100644
--- a/player/videojs.coffee
+++ b/player/videojs.coffee
@@ -46,6 +46,10 @@ hasAnyTextTracks = (data) ->
ntracks = data?.meta?.textTracks?.length ? 0
return ntracks > 0
+hasAnyAudioTracks = (data) ->
+ ntracks = data?.meta?.audioTracks?.length ? 0
+ return ntracks > 0
+
window.VideoJSPlayer = class VideoJSPlayer extends Player
constructor: (data) ->
if not (this instanceof VideoJSPlayer)
@@ -108,13 +112,21 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
$('').attr(attrs).appendTo(video)
)
+ pluginData =
+ videoJsResolutionSwitcher:
+ default: @sources[0].res
+
+ if hasAnyAudioTracks(data)
+ pluginData.audioSwitch =
+ audioTracks: data.meta.audioTracks,
+ volume: VOLUME
+
@player = videojs(video[0],
# https://github.com/Dash-Industry-Forum/dash.js/issues/2184
autoplay: @sources[0].type != 'application/dash+xml',
controls: true,
- plugins:
- videoJsResolutionSwitcher:
- default: @sources[0].res
+ plugins: pluginData
+
)
@player.ready(=>
# Have to use updateSrc instead of tags
diff --git a/src/custom-media.js b/src/custom-media.js
index 53c87572..8de50841 100644
--- a/src/custom-media.js
+++ b/src/custom-media.js
@@ -22,8 +22,9 @@ const SOURCE_CONTENT_TYPES = new Set([
'application/dash+xml',
'application/x-mpegURL',
'audio/aac',
- 'audio/ogg',
+ 'audio/mp4',
'audio/mpeg',
+ 'audio/ogg',
'audio/opus',
'video/mp4',
'video/ogg',
@@ -36,8 +37,9 @@ const LIVE_ONLY_CONTENT_TYPES = new Set([
const AUDIO_ONLY_CONTENT_TYPES = new Set([
'audio/aac',
- 'audio/ogg',
+ 'audio/mp4',
'audio/mpeg',
+ 'audio/ogg',
'audio/opus'
]);
@@ -142,6 +144,7 @@ export function convert(id, data) {
const meta = {
direct: sources,
+ audioTracks: data.audioTracks,
textTracks: data.textTracks,
thumbnail: data.thumbnail, // Currently ignored by Media
live: !!data.live // Currently ignored by Media
@@ -170,11 +173,12 @@ export function validate(data) {
validateURL(data.thumbnail);
}
- validateSources(data.sources);
+ validateSources(data.sources, data);
+ validateAudioTracks(data.audioTracks);
validateTextTracks(data.textTracks);
}
-function validateSources(sources) {
+function validateSources(sources, data) {
if (!Array.isArray(sources))
throw new ValidationError('sources must be a list');
if (sources.length === 0)
@@ -210,6 +214,45 @@ function validateSources(sources) {
}
}
+function validateAudioTracks(audioTracks) {
+ if (typeof audioTracks === 'undefined') {
+ return;
+ }
+
+ if (!Array.isArray(audioTracks)){
+ throw new ValidationError('audioTracks must be a list');
+ }
+
+ for (let track of audioTracks) {
+ if (typeof track.url !== 'string'){
+ throw new ValidationError('audio track URL must be a string');
+ }
+ validateURL(track.url);
+
+ if (!AUDIO_ONLY_CONTENT_TYPES.has(track.contentType)){
+ throw new ValidationError(
+ `unacceptable audio track contentType "${track.contentType}"`
+ );
+ }
+ if (typeof track.label !== 'string'){
+ throw new ValidationError('audio track label must be a string');
+ }
+ if (!track.label){
+ throw new ValidationError('audio track label must be nonempty');
+ }
+
+ if (typeof track.language !== 'string'){
+ throw new ValidationError('audio track language must be a string');
+ }
+ if (!track.language){
+ throw new ValidationError('audio track language must be nonempty');
+ }
+ if (!/^[a-z]{2,3}$/.test(track.language)){
+ throw new ValidationError('audio track language must be a two or three letter IETF BCP 47 subtag');
+ }
+ }
+}
+
function validateTextTracks(textTracks) {
if (typeof textTracks === 'undefined') {
return;
diff --git a/src/media.js b/src/media.js
index f486a853..70df36ae 100644
--- a/src/media.js
+++ b/src/media.js
@@ -38,7 +38,8 @@ Media.prototype = {
scuri: this.meta.scuri,
embed: this.meta.embed,
gdrive_subtitles: this.meta.gdrive_subtitles,
- textTracks: this.meta.textTracks
+ textTracks: this.meta.textTracks,
+ audioTracks: this.meta.audioTracks
}
};