Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(FEC-8390, FEC-8246): support 608/708 captions #67

Merged
merged 10 commits into from
Jul 17, 2018
95 changes: 90 additions & 5 deletions src/hls-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
* @private
*/
_onRecoveredCallback: ?Function;
_onAddTrack: Function;

/**
* Factory method to create media source adapter.
Expand Down Expand Up @@ -142,6 +143,11 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
if (Utils.Object.hasPropertyPath(config, 'playback.useNativeTextTrack')) {
adapterConfig.subtitleDisplay = Utils.Object.getPropertyPath(config, 'playback.useNativeTextTrack');
}
adapterConfig.hlsConfig.enableCEA708Captions = config.playback.enableCEA708Captions;
adapterConfig.hlsConfig.captionsTextTrack1Label = config.playback.captionsTextTrack1Label;
adapterConfig.hlsConfig.captionsTextTrack1LanguageCode = config.playback.captionsTextTrack1LanguageCode;
adapterConfig.hlsConfig.captionsTextTrack2Label = config.playback.captionsTextTrack2Label;
adapterConfig.hlsConfig.captionsTextTrack2LanguageCode = config.playback.captionsTextTrack2LanguageCode;
return new this(videoElement, source, adapterConfig);
}

Expand Down Expand Up @@ -213,6 +219,21 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
this._onRecoveredCallback = () => this._onRecovered();
this._onVideoErrorCallback = e => this._onVideoError(e);
this._videoElement.addEventListener(EventType.ERROR, this._onVideoErrorCallback);
this._onAddTrack = this._onAddTrack.bind(this);
this._videoElement.addEventListener('addtrack', this._onAddTrack);
this._videoElement.textTracks.onaddtrack = this._onAddTrack;
}

_onAddTrack(event: any) {
if (!this._hls.subtitleTracks.length) {
// parse CEA 608/708 captions that not exposed on hls.subtitleTracks API
const CEATextTrack = this._parseCEATextTrack(event.track);
if (CEATextTrack) {
HlsAdapter._logger.debug('A CEA 608/708 caption has found', CEATextTrack);
this._playerTracks.push(CEATextTrack);
this._trigger(EventType.TRACKS_CHANGED, {tracks: this._playerTracks});
}
}
}

/**
Expand Down Expand Up @@ -438,6 +459,28 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
return textTracks;
}

/**
* Parse a CEA 608/708 text track which not expose on hlsjs api into player text tracks.
* @param {Object} CEATextTrack - A video element text track.
* @returns {?TextTrack} - A parsed text track if the param is a CEA 608/708 caption.
* @private
*/
_parseCEATextTrack(CEATextTrack: Object): ?TextTrack {
let textTrack = null;
if (CEATextTrack.kind === 'captions') {
const settings = {
id: CEATextTrack.id,
active: CEATextTrack.mode === 'showing',
label: CEATextTrack.label,
kind: CEATextTrack.kind,
language: CEATextTrack.language,
index: this._playerTracks.filter(track => track instanceof TextTrack).length
};
textTrack = new TextTrack(settings);
}
return textTrack;
}

/**
* Select an audio track.
* @function selectAudioTrack
Expand Down Expand Up @@ -475,20 +518,59 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
* @public
*/
selectTextTrack(textTrack: TextTrack): void {
if (textTrack instanceof TextTrack && !textTrack.active && this._videoElement.textTracks) {
this._hls.subtitleTrack = textTrack.index;
HlsAdapter._logger.debug('Text track changed', textTrack);
this._onTrackChanged(textTrack);
if (textTrack instanceof TextTrack && !textTrack.active) {
if (this._hls.subtitleTracks.length) {
this._hls.subtitleTrack = textTrack.id;
this._notifyTrackChanged(textTrack);
} else {
this._selectNativeTextTrack(textTrack);
}
}
}

/**
* Select a video element text track.
* @function _selectNativeTextTrack
* @param {TextTrack} textTrack - the track to select.
* @returns {void}
* @private
*/
_selectNativeTextTrack(textTrack: TextTrack): void {
const selectedTrack = Array.from(this._videoElement.textTracks).find(track => track.language === textTrack.language);
if (selectedTrack) {
this._disableNativeTextTracks();
selectedTrack.mode = this._config.subtitleDisplay ? 'showing' : 'hidden';
this._notifyTrackChanged(textTrack);
}
}

_notifyTrackChanged(textTrack: TextTrack): void {
HlsAdapter._logger.debug('Text track changed', textTrack);
this._onTrackChanged(textTrack);
}

/**
* Disables all the video element text tracks.
* @private
* @returns {void}
*/
_disableNativeTextTracks(): void {
Array.from(this._videoElement.textTracks).forEach(track => {
track.mode = 'disabled';
});
}

/** Hide the text track
* @function hideTextTrack
* @returns {void}
* @public
*/
hideTextTrack(): void {
this._hls.subtitleTrack = -1;
if (this._hls.subtitleTracks.length) {
this._hls.subtitleTrack = -1;
} else {
this._disableNativeTextTracks();
}
}

/**
Expand Down Expand Up @@ -766,6 +848,9 @@ export default class HlsAdapter extends BaseMediaSourceAdapter {
this._hls.off(Hlsjs.Events.ERROR, this._onError);
this._hls.off(Hlsjs.Events.LEVEL_SWITCHED, this._onLevelSwitched);
this._hls.off(Hlsjs.Events.AUDIO_TRACK_SWITCHED, this._onAudioTrackSwitched);
this._hls.off(Hlsjs.Events.MANIFEST_LOADED, this._onManifestLoaded);
this._videoElement.textTracks.onaddtrack = null;
this._videoElement.removeEventListener('addtrack', this._onAddTrack);
this._removeRecoveredCallbackListener();
this._removeVideoErrorListener();
this._removeLoadedMetadataListener();
Expand Down