import videojs from 'video.js';
import PlaylistLoader from './playlist-loader';
/**
* Convert the properties of an HLS track into an audioTrackKind.
*
* @private
*/
const audioTrackKind_ = (properties) => {
let kind = properties.default ? 'main' : 'alternative';
if (properties.characteristics &&
properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
kind = 'main-desc';
}
return kind;
};
const setupListeners = {
/**
* Setup event listeners for audio playlist loader
*/
AUDIO: (playlistLoader, masterPlaylistController) => {
if (!playlistLoader) {
// no playlist loader means audio will be muxed with the video
return;
}
const segmentLoader = masterPlaylistController.audioSegmentLoader_;
const tech = masterPlaylistController.tech_;
const options = masterPlaylistController.requestOptions_;
playlistLoader.on('loadedmetadata', () => {
const media = playlistLoader.media();
segmentLoader.playlist(media, options);
// if the video is already playing, or if this isn't a live video and preload
// permits, start downloading segments
if (!tech.paused() || (media.endList && tech.preload() !== 'none')) {
segmentLoader.load();
}
});
playlistLoader.on('loadedplaylist', () => {
segmentLoader.playlist(playlistLoader.media(), options);
});
playlistLoader.on('error', () => {
videojs.log.warn('Problem encountered loading the alternate audio track' +
'. Switching back to default.');
segmentLoader.abort();
});
},
/**
* Setup event listeners for subtitle playlist loader
*/
SUBTITLES: (playlistLoader, masterPlaylistController) => {
const segmentLoader = masterPlaylistController.subtitleSegmentLoader_;
const tech = masterPlaylistController.tech_;
const options = masterPlaylistController.requestOptions_;
playlistLoader.on('loadedmetadata', () => {
const media = playlistLoader.media();
segmentLoader.playlist(media, options);
segmentLoader.track(masterPlaylistController.activeSubtitleTrack_());
// if the video is already playing, or if this isn't a live video and preload
// permits, start downloading segments
if (!tech.paused() || (media.endList && tech.preload() !== 'none')) {
segmentLoader.load();
}
});
playlistLoader.on('loadedplaylist', () => {
segmentLoader.playlist(playlistLoader.media(), options);
});
playlistLoader.on('error', () => {
masterPlaylistController.handleSubtitleError_();
});
}
};
const initialize = {
/**
* Setup playlist loaders and tracks for audio groups
*/
AUDIO: (masterGroups, groups, tracks, masterPlaylistController) => {
// force a default if we have none or we are not
// in html5 mode (the only mode to support more than one
// audio track)
if (!masterGroups.AUDIO ||
Object.keys(masterGroups.AUDIO).length === 0 ||
masterPlaylistController.mode_ !== 'html5') {
masterGroups.AUDIO = { main: { default: { defualt: true } } };
}
for (let masterGroup in masterGroups.AUDIO) {
if (!groups[masterGroup]) {
groups[masterGroup] = [];
}
for (let label in masterGroups.AUDIO[masterGroup]) {
let properties = masterGroups.AUDIO[masterGroup][label];
let playlistLoader;
if (properties.resolvedUri) {
playlistLoader = new PlaylistLoader(properties.resolvedUri,
masterPlaylistController.hls_,
masterPlaylistController.withCredentials);
} else {
// no resolvedUri means the audio is muxed with the video when using this
// audio track
playlistLoader = null;
}
properties = videojs.mergeOptions({ id: label, playlistLoader }, properties);
setupListeners.AUDIO(properties.playlistLoader, masterPlaylistController);
groups[masterGroup].push(properties);
if (typeof tracks[label] === 'undefined') {
const track = new videojs.AudioTrack({
id: label,
kind: audioTrackKind_(properties),
enabled: false,
language: properties.language,
default: properties.default,
label
});
tracks[label] = track;
}
}
}
},
/**
* Setup playlist loaders and tracks for subtitle groups
*/
SUBTITLES: (masterGroups, groups, tracks, masterPlaylistController) => {
for (let masterGroup in masterGroups.SUBTITLES) {
if (!groups[masterGroup]) {
groups[masterGroup] = [];
}
for (let label in masterGroups.SUBTITLES[masterGroup]) {
if (masterGroups.SUBTITLES[masterGroup][label].forced) {
continue;
}
let properties = masterGroups.SUBTITLES[masterGroup][label];
properties = videojs.mergeOptions({
id: label,
playlistLoader: new PlaylistLoader(properties.resolvedUri,
masterPlaylistController.hls_,
masterPlaylistController.withCredentials)
}, properties);
setupListeners.SUBTITLES(properties.playlistLoader, masterPlaylistController);
groups[masterGroup].push(properties);
if (typeof tracks[label] === 'undefined') {
const track = masterPlaylistController.tech_.addRemoteTextTrack({
id: label,
kind: 'subtitles',
enabled: false,
language: properties.language,
label
}, false).track;
tracks[label] = track;
}
}
}
},
/**
* Setup tracks for closed-caption groups
*/
'CLOSED-CAPTIONS': (masterGroups, groups, tracks, masterPlaylistController) => {
for (let masterGroup in masterGroups['CLOSED-CAPTIONS']) {
if (!groups[masterGroup]) {
groups[masterGroup] = [];
}
for (let label in masterGroups['CLOSED-CAPTIONS'][masterGroup]) {
let properties = masterGroups['CLOSED-CAPTIONS'][masterGroup][label];
// We only support CEA608 captions for now, so ignore anything that
// doesn't use a CCx INSTREAM-ID
if (properties.instreamId.match(/CC\d/)) {
continue;
}
groups[masterGroup].push(videojs.mergeOptions({ id: label }, properties));
if (typeof tracks[label] === 'undefined') {
const track = masterPlaylistController.tech_.addRemoteTextTrack({
id: properties.instreamId,
kind: 'captions',
enabled: false,
language: properties.language,
label
}, false).track;
tracks[label] = track;
}
}
}
}
};
const initializeMediaGroup = (
type,
master,
mediaGroup,
masterPlaylistController
) => {
const masterGroups = master.mediaGroups || {};
return initialize[type](masterGroups,
mediaGroup.groups,
mediaGroup.tracks,
masterPlaylistController);
};
export default initializeMediaGroup;