mirror of
https://github.com/videojs/video.js.git
synced 2025-07-15 01:34:23 +02:00
refactor: unify all Track and TrackList APIs (#3783)
Unify all track-related usage internally. BREAKING CHANGE: some externally accessibly functions for tracks are now private.
This commit is contained in:
committed by
Gary Katsevman
parent
7bafcc2a55
commit
49bed07039
@ -22,7 +22,7 @@ class AudioTrackButton extends TrackButton {
|
||||
* The key/value store of player options.
|
||||
*/
|
||||
constructor(player, options = {}) {
|
||||
options.tracks = player.audioTracks && player.audioTracks();
|
||||
options.tracks = player.audioTracks();
|
||||
|
||||
super(player, options);
|
||||
|
||||
@ -49,11 +49,7 @@ class AudioTrackButton extends TrackButton {
|
||||
* An array of menu items
|
||||
*/
|
||||
createItems(items = []) {
|
||||
const tracks = this.player_.audioTracks && this.player_.audioTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
const tracks = this.player_.audioTracks();
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
|
@ -33,14 +33,12 @@ class AudioTrackMenuItem extends MenuItem {
|
||||
|
||||
this.track = track;
|
||||
|
||||
if (tracks) {
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', () => {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', () => {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,10 +57,6 @@ class AudioTrackMenuItem extends MenuItem {
|
||||
|
||||
super.handleClick(event);
|
||||
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
|
||||
|
@ -29,15 +29,12 @@ class DescriptionsButton extends TextTrackButton {
|
||||
this.el_.setAttribute('aria-label', 'Descriptions Menu');
|
||||
|
||||
const tracks = player.textTracks();
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
if (tracks) {
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', function() {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', function() {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,10 +43,6 @@ class TextTrackButton extends TrackButton {
|
||||
|
||||
const tracks = this.player_.textTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return items;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
|
||||
|
@ -34,15 +34,12 @@ class TextTrackMenuItem extends MenuItem {
|
||||
super(player, options);
|
||||
|
||||
this.track = track;
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
if (tracks) {
|
||||
const changeHandler = Fn.bind(this, this.handleTracksChange);
|
||||
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', function() {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
}
|
||||
tracks.addEventListener('change', changeHandler);
|
||||
this.on('dispose', function() {
|
||||
tracks.removeEventListener('change', changeHandler);
|
||||
});
|
||||
|
||||
// iOS7 doesn't dispatch change events to TextTrackLists when an
|
||||
// associated track's mode changes. Without something like
|
||||
@ -50,7 +47,7 @@ class TextTrackMenuItem extends MenuItem {
|
||||
// possible to detect changes to the mode attribute and polyfill
|
||||
// the change event. As a poor substitute, we manually dispatch
|
||||
// change events whenever the controls modify the mode.
|
||||
if (tracks && tracks.onchange === undefined) {
|
||||
if (tracks.onchange === undefined) {
|
||||
let event;
|
||||
|
||||
this.on(['tap', 'click'], function() {
|
||||
|
186
src/js/player.js
186
src/js/player.js
@ -25,8 +25,7 @@ import mergeOptions from './utils/merge-options.js';
|
||||
import textTrackConverter from './tracks/text-track-list-converter.js';
|
||||
import ModalDialog from './modal-dialog';
|
||||
import Tech from './tech/tech.js';
|
||||
import AudioTrackList from './tracks/audio-track-list.js';
|
||||
import VideoTrackList from './tracks/video-track-list.js';
|
||||
import {ALL as TRACK_TYPES} from './tracks/track-types';
|
||||
|
||||
// The following imports are used only to ensure that the corresponding modules
|
||||
// are always included in the video.js package. Importing the modules will
|
||||
@ -809,14 +808,11 @@ class Player extends Component {
|
||||
this.isReady_ = false;
|
||||
|
||||
// Grab tech-specific options from player options and add source and parent element to use.
|
||||
const techOptions = assign({
|
||||
const techOptions = {
|
||||
source,
|
||||
'nativeControlsForTouch': this.options_.nativeControlsForTouch,
|
||||
'playerId': this.id(),
|
||||
'techId': `${this.id()}_${techName}_api`,
|
||||
'videoTracks': this.videoTracks_,
|
||||
'textTracks': this.textTracks_,
|
||||
'audioTracks': this.audioTracks_,
|
||||
'autoplay': this.options_.autoplay,
|
||||
'preload': this.options_.preload,
|
||||
'loop': this.options_.loop,
|
||||
@ -825,7 +821,15 @@ class Player extends Component {
|
||||
'language': this.language(),
|
||||
'playerElIngest': this.playerElIngest_ || false,
|
||||
'vtt.js': this.options_['vtt.js']
|
||||
}, this.options_[techName.toLowerCase()]);
|
||||
};
|
||||
|
||||
TRACK_TYPES.names.forEach((name) => {
|
||||
const props = TRACK_TYPES[name];
|
||||
|
||||
techOptions[props.getterName] = this[props.privateName];
|
||||
});
|
||||
|
||||
assign(techOptions, this.options_[techName.toLowerCase()]);
|
||||
|
||||
if (this.tag) {
|
||||
techOptions.tag = this.tag;
|
||||
@ -906,9 +910,11 @@ class Player extends Component {
|
||||
*/
|
||||
unloadTech_() {
|
||||
// Save the current text tracks so that we can reuse the same text tracks with the next tech
|
||||
this.videoTracks_ = this.videoTracks();
|
||||
this.textTracks_ = this.textTracks();
|
||||
this.audioTracks_ = this.audioTracks();
|
||||
TRACK_TYPES.names.forEach((name) => {
|
||||
const props = TRACK_TYPES[name];
|
||||
|
||||
this[props.privateName] = this[props.getterName]();
|
||||
});
|
||||
this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
|
||||
|
||||
this.isReady_ = false;
|
||||
@ -2841,102 +2847,6 @@ class Player extends Component {
|
||||
return !!this.isAudio_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link VideoTrackList}
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
|
||||
*
|
||||
* @return {VideoTrackList}
|
||||
* the current video track list
|
||||
*/
|
||||
videoTracks() {
|
||||
// if we have not yet loadTech_, we create videoTracks_
|
||||
// these will be passed to the tech during loading
|
||||
if (!this.tech_) {
|
||||
this.videoTracks_ = this.videoTracks_ || new VideoTrackList();
|
||||
return this.videoTracks_;
|
||||
}
|
||||
|
||||
return this.tech_.videoTracks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link AudioTrackList}
|
||||
*
|
||||
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
|
||||
*
|
||||
* @return {AudioTrackList}
|
||||
* the current audio track list
|
||||
*/
|
||||
audioTracks() {
|
||||
// if we have not yet loadTech_, we create videoTracks_
|
||||
// these will be passed to the tech during loading
|
||||
if (!this.tech_) {
|
||||
this.audioTracks_ = this.audioTracks_ || new AudioTrackList();
|
||||
return this.audioTracks_;
|
||||
}
|
||||
|
||||
return this.tech_.audioTracks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link TextTrackList}
|
||||
*
|
||||
* Text tracks are tracks of timed text events.
|
||||
* - Captions: text displayed over the video
|
||||
* for the hearing impaired
|
||||
* - Subtitles: text displayed over the video for
|
||||
* those who don't understand language in the video
|
||||
* - Chapters: text displayed in a menu allowing the user to jump
|
||||
* to particular points (chapters) in the video
|
||||
* - Descriptions: (not yet implemented) audio descriptions that are read back to
|
||||
* the user by a screen reading device
|
||||
*
|
||||
* @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
|
||||
*
|
||||
* @return {TextTrackList|undefined}
|
||||
* The current TextTrackList or undefined if
|
||||
* or undefined if we don't have a tech
|
||||
*/
|
||||
textTracks() {
|
||||
// cannot use techGet_ directly because it checks to see whether the tech is ready.
|
||||
// Flash is unlikely to be ready in time but textTracks should still work.
|
||||
if (this.tech_) {
|
||||
return this.tech_.textTracks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "remote" {@link TextTrackList}. Remote Text Tracks
|
||||
* are tracks that were added to the HTML video element and can
|
||||
* be removed, whereas normal texttracks cannot be removed.
|
||||
*
|
||||
*
|
||||
* @return {TextTrackList|undefined}
|
||||
* The current remote text track list or undefined
|
||||
* if we don't have a tech
|
||||
*/
|
||||
remoteTextTracks() {
|
||||
if (this.tech_) {
|
||||
return this.tech_.remoteTextTracks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "remote" {@link HTMLTrackElementList}.
|
||||
* This gives the user all of the DOM elements that match up
|
||||
* with the remote {@link TextTrackList}.
|
||||
*
|
||||
* @return {HTMLTrackElementList}
|
||||
* The current remote text track list elements
|
||||
* or undefined if we don't have a tech
|
||||
*/
|
||||
remoteTextTrackEls() {
|
||||
if (this.tech_) {
|
||||
return this.tech_.remoteTextTrackEls();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method for adding a {@link TextTrack} to our
|
||||
* {@link TextTrackList}.
|
||||
@ -3194,6 +3104,70 @@ class Player extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link VideoTrackList}
|
||||
* @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
|
||||
*
|
||||
* @return {VideoTrackList}
|
||||
* the current video track list
|
||||
*
|
||||
* @method Player.prototype.videoTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the {@link AudioTrackList}
|
||||
* @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
|
||||
*
|
||||
* @return {AudioTrackList}
|
||||
* the current audio track list
|
||||
*
|
||||
* @method Player.prototype.audioTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the {@link TextTrackList}
|
||||
*
|
||||
* @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
|
||||
*
|
||||
* @return {TextTrackList}
|
||||
* the current text track list
|
||||
*
|
||||
* @method Player.prototype.textTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the remote {@link TextTrackList}
|
||||
*
|
||||
* @return {TextTrackList}
|
||||
* The current remote text track list
|
||||
*
|
||||
* @method Player.prototype.textTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the remote {@link HTMLTrackElementList} tracks.
|
||||
*
|
||||
* @return {HTMLTrackElementList}
|
||||
* The current remote text track element list
|
||||
*
|
||||
* @method Player.prototype.remoteTextTrackEls
|
||||
*/
|
||||
|
||||
TRACK_TYPES.names.forEach(function(name) {
|
||||
const props = TRACK_TYPES[name];
|
||||
|
||||
Player.prototype[props.getterName] = function() {
|
||||
if (this.tech_) {
|
||||
return this.tech_[props.getterName]();
|
||||
}
|
||||
|
||||
// if we have not yet loadTech_, we create {video,audio,text}Tracks_
|
||||
// these will be passed to the tech during loading
|
||||
this[props.privateName] = this[props.privateName] || new props.ListClass();
|
||||
return this[props.privateName];
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Global player list
|
||||
*
|
||||
|
@ -4,7 +4,6 @@
|
||||
import Tech from './tech.js';
|
||||
import * as Dom from '../utils/dom.js';
|
||||
import * as Url from '../utils/url.js';
|
||||
import * as Fn from '../utils/fn.js';
|
||||
import log from '../utils/log.js';
|
||||
import tsml from 'tsml';
|
||||
import * as browser from '../utils/browser.js';
|
||||
@ -13,6 +12,7 @@ import window from 'global/window';
|
||||
import {assign} from '../utils/obj';
|
||||
import mergeOptions from '../utils/merge-options.js';
|
||||
import toTitleCase from '../utils/to-title-case.js';
|
||||
import {NORMAL as TRACK_TYPES} from '../tracks/track-types';
|
||||
|
||||
/**
|
||||
* HTML5 Media Controller - Wrapper for HTML5 Media API
|
||||
@ -67,7 +67,7 @@ class Html5 extends Tech {
|
||||
} else {
|
||||
// store HTMLTrackElement and TextTrack to remote list
|
||||
this.remoteTextTrackEls().addTrackElement_(node);
|
||||
this.remoteTextTracks().addTrack_(node.track);
|
||||
this.remoteTextTracks().addTrack(node.track);
|
||||
if (!crossoriginTracks &&
|
||||
!this.el_.hasAttribute('crossorigin') &&
|
||||
Url.isCrossOrigin(node.src)) {
|
||||
@ -82,52 +82,10 @@ class Html5 extends Tech {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add text tracks into this list
|
||||
const trackTypes = ['audio', 'video'];
|
||||
|
||||
// ProxyNative Video/Audio Track
|
||||
trackTypes.forEach((type) => {
|
||||
const elTracks = this.el()[`${type}Tracks`];
|
||||
const techTracks = this[`${type}Tracks`]();
|
||||
const capitalType = toTitleCase(type);
|
||||
|
||||
if (!this[`featuresNative${capitalType}Tracks`] ||
|
||||
!elTracks ||
|
||||
!elTracks.addEventListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
this[`handle${capitalType}TrackChange_`] = (e) => {
|
||||
techTracks.trigger({
|
||||
type: 'change',
|
||||
target: techTracks,
|
||||
currentTarget: techTracks,
|
||||
srcElement: techTracks
|
||||
});
|
||||
};
|
||||
|
||||
this[`handle${capitalType}TrackAdd_`] = (e) => techTracks.addTrack(e.track);
|
||||
this[`handle${capitalType}TrackRemove_`] = (e) => techTracks.removeTrack(e.track);
|
||||
|
||||
elTracks.addEventListener('change', this[`handle${capitalType}TrackChange_`]);
|
||||
elTracks.addEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]);
|
||||
elTracks.addEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]);
|
||||
this[`removeOld${capitalType}Tracks_`] = (e) => this.removeOldTracks_(techTracks, elTracks);
|
||||
|
||||
// Remove (native) tracks that are not used anymore
|
||||
this.on('loadstart', this[`removeOld${capitalType}Tracks_`]);
|
||||
});
|
||||
|
||||
if (this.featuresNativeTextTracks) {
|
||||
if (crossoriginTracks) {
|
||||
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
|
||||
this.proxyNativeTracks_();
|
||||
if (this.featuresNativeTextTracks && crossoriginTracks) {
|
||||
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
|
||||
This may prevent text tracks from loading.`);
|
||||
}
|
||||
|
||||
this.handleTextTrackChange_ = Fn.bind(this, this.handleTextTrackChange);
|
||||
this.handleTextTrackAdd_ = Fn.bind(this, this.handleTextTrackAdd);
|
||||
this.handleTextTrackRemove_ = Fn.bind(this, this.handleTextTrackRemove);
|
||||
this.proxyNativeTextTracks_();
|
||||
}
|
||||
|
||||
// Determine if native controls should be used
|
||||
@ -150,28 +108,81 @@ class Html5 extends Tech {
|
||||
* Dispose of `HTML5` media element and remove all tracks.
|
||||
*/
|
||||
dispose() {
|
||||
// Un-ProxyNativeTracks
|
||||
['audio', 'video', 'text'].forEach((type) => {
|
||||
const capitalType = toTitleCase(type);
|
||||
const tl = this.el_[`${type}Tracks`];
|
||||
|
||||
if (tl && tl.removeEventListener) {
|
||||
tl.removeEventListener('change', this[`handle${capitalType}TrackChange_`]);
|
||||
tl.removeEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]);
|
||||
tl.removeEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]);
|
||||
}
|
||||
|
||||
// Stop removing old text tracks
|
||||
if (tl) {
|
||||
this.off('loadstart', this[`removeOld${capitalType}Tracks_`]);
|
||||
}
|
||||
});
|
||||
|
||||
Html5.disposeMediaElement(this.el_);
|
||||
// tech will handle clearing of the emulated track list
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy all native track list events to our track lists if the browser we are playing
|
||||
* in supports that type of track list.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
proxyNativeTracks_() {
|
||||
TRACK_TYPES.names.forEach((name) => {
|
||||
const props = TRACK_TYPES[name];
|
||||
const elTracks = this.el()[props.getterName];
|
||||
const techTracks = this[props.getterName]();
|
||||
|
||||
if (!this[`featuresNative${props.capitalName}Tracks`] ||
|
||||
!elTracks ||
|
||||
!elTracks.addEventListener) {
|
||||
return;
|
||||
}
|
||||
const listeners = {
|
||||
change(e) {
|
||||
techTracks.trigger({
|
||||
type: 'change',
|
||||
target: techTracks,
|
||||
currentTarget: techTracks,
|
||||
srcElement: techTracks
|
||||
});
|
||||
},
|
||||
addtrack(e) {
|
||||
techTracks.addTrack(e.track);
|
||||
},
|
||||
removetrack(e) {
|
||||
techTracks.removeTrack(e.track);
|
||||
}
|
||||
};
|
||||
const removeOldTracks = function() {
|
||||
const removeTracks = [];
|
||||
|
||||
for (let i = 0; i < techTracks.length; i++) {
|
||||
let found = false;
|
||||
|
||||
for (let j = 0; j < elTracks.length; j++) {
|
||||
if (elTracks[j] === techTracks[i]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
removeTracks.push(techTracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
while (removeTracks.length) {
|
||||
techTracks.removeTrack(removeTracks.shift());
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(listeners).forEach((eventName) => {
|
||||
const listener = listeners[eventName];
|
||||
|
||||
elTracks.addEventListener(eventName, listener);
|
||||
this.on('dispose', (e) => elTracks.removeEventListener(eventName, listener));
|
||||
});
|
||||
|
||||
// Remove (native) tracks that are not used anymore
|
||||
this.on('loadstart', removeOldTracks);
|
||||
this.on('dispose', (e) => this.off('loadstart', removeOldTracks));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the `Html5` Tech's DOM element.
|
||||
*
|
||||
@ -331,130 +342,18 @@ class Html5 extends Tech {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listeners to native text track events. This adds the native text tracks
|
||||
* to our emulated {@link TextTrackList}.
|
||||
* Called by {@link Player#play} to play using the `Html5` `Tech`.
|
||||
*/
|
||||
proxyNativeTextTracks_() {
|
||||
const tt = this.el().textTracks;
|
||||
play() {
|
||||
const playPromise = this.el_.play();
|
||||
|
||||
if (tt) {
|
||||
// Add tracks - if player is initialised after DOM loaded, textTracks
|
||||
// will not trigger addtrack
|
||||
for (let i = 0; i < tt.length; i++) {
|
||||
this.textTracks().addTrack_(tt[i]);
|
||||
}
|
||||
|
||||
if (tt.addEventListener) {
|
||||
tt.addEventListener('change', this.handleTextTrackChange_);
|
||||
tt.addEventListener('addtrack', this.handleTextTrackAdd_);
|
||||
tt.addEventListener('removetrack', this.handleTextTrackRemove_);
|
||||
}
|
||||
|
||||
// Remove (native) texttracks that are not used anymore
|
||||
this.on('loadstart', this.removeOldTextTracks_);
|
||||
// Catch/silence error when a pause interrupts a play request
|
||||
// on browsers which return a promise
|
||||
if (playPromise !== undefined && typeof playPromise.then === 'function') {
|
||||
playPromise.then(null, (e) => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any {@link TextTrackList} `change` event.
|
||||
*
|
||||
* @param {EventTarget~Event} e
|
||||
* The `change` event that caused this to run.
|
||||
*
|
||||
* @listens TextTrackList#change
|
||||
*/
|
||||
handleTextTrackChange(e) {
|
||||
const tt = this.textTracks();
|
||||
|
||||
this.textTracks().trigger({
|
||||
type: 'change',
|
||||
target: tt,
|
||||
currentTarget: tt,
|
||||
srcElement: tt
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any {@link TextTrackList} `addtrack` event.
|
||||
*
|
||||
* @param {EventTarget~Event} e
|
||||
* The `addtrack` event that caused this to run.
|
||||
*
|
||||
* @listens TextTrackList#addtrack
|
||||
*/
|
||||
handleTextTrackAdd(e) {
|
||||
this.textTracks().addTrack_(e.track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any {@link TextTrackList} `removetrack` event.
|
||||
*
|
||||
* @param {EventTarget~Event} e
|
||||
* The `removetrack` event that caused this to run.
|
||||
*
|
||||
* @listens TextTrackList#removetrack
|
||||
*/
|
||||
handleTextTrackRemove(e) {
|
||||
this.textTracks().removeTrack_(e.track);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or
|
||||
* {@link TextTrack}s that are not in the media elements TrackList.
|
||||
*
|
||||
* @param {TrackList} techTracks
|
||||
* HTML5 Tech's TrackList to search through
|
||||
*
|
||||
* @param {TrackList} elTracks
|
||||
* HTML5 media elements TrackList to search trough.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
removeOldTracks_(techTracks, elTracks) {
|
||||
// This will loop over the techTracks and check if they are still used by the HTML5 media element
|
||||
// If not, they will be removed from the emulated list
|
||||
const removeTracks = [];
|
||||
|
||||
if (!elTracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < techTracks.length; i++) {
|
||||
const techTrack = techTracks[i];
|
||||
let found = false;
|
||||
|
||||
for (let j = 0; j < elTracks.length; j++) {
|
||||
if (elTracks[j] === techTrack) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
removeTracks.push(techTrack);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < removeTracks.length; i++) {
|
||||
const track = removeTracks[i];
|
||||
|
||||
techTracks.removeTrack_(track);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove {@link TextTrack}s that dont exist in the native track list from our
|
||||
* emulated {@link TextTrackList}.
|
||||
*
|
||||
* @listens Tech#loadstart
|
||||
*/
|
||||
removeOldTextTracks_(e) {
|
||||
const techTracks = this.textTracks();
|
||||
const elTracks = this.el().textTracks;
|
||||
|
||||
this.removeOldTracks_(techTracks, elTracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current time for the `HTML5` tech.
|
||||
*
|
||||
|
@ -3,13 +3,7 @@
|
||||
*/
|
||||
|
||||
import Component from '../component';
|
||||
import HTMLTrackElement from '../tracks/html-track-element';
|
||||
import HTMLTrackElementList from '../tracks/html-track-element-list';
|
||||
import mergeOptions from '../utils/merge-options.js';
|
||||
import TextTrack from '../tracks/text-track';
|
||||
import TextTrackList from '../tracks/text-track-list';
|
||||
import VideoTrackList from '../tracks/video-track-list';
|
||||
import AudioTrackList from '../tracks/audio-track-list';
|
||||
import * as Fn from '../utils/fn.js';
|
||||
import log from '../utils/log.js';
|
||||
import { createTimeRange } from '../utils/time-ranges.js';
|
||||
@ -18,6 +12,7 @@ import MediaError from '../media-error.js';
|
||||
import window from 'global/window';
|
||||
import document from 'global/document';
|
||||
import {isPlain} from '../utils/obj';
|
||||
import * as TRACK_TYPES from '../tracks/track-types';
|
||||
|
||||
/**
|
||||
* An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
|
||||
@ -68,9 +63,9 @@ function createTrackHelper(self, kind, label, language, options = {}) {
|
||||
}
|
||||
options.tech = self;
|
||||
|
||||
const track = new TextTrack(options);
|
||||
const track = new TRACK_TYPES.ALL.text.TrackClass(options);
|
||||
|
||||
tracks.addTrack_(track);
|
||||
tracks.addTrack(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
@ -108,9 +103,13 @@ class Tech extends Component {
|
||||
this.hasStarted_ = false;
|
||||
});
|
||||
|
||||
this.textTracks_ = options.textTracks;
|
||||
this.videoTracks_ = options.videoTracks;
|
||||
this.audioTracks_ = options.audioTracks;
|
||||
TRACK_TYPES.ALL.names.forEach((name) => {
|
||||
const props = TRACK_TYPES.ALL[name];
|
||||
|
||||
if (options && options[props.getterName]) {
|
||||
this[props.privateName] = options[props.getterName];
|
||||
}
|
||||
});
|
||||
|
||||
// Manually track progress in cases where the browser/flash player doesn't report it.
|
||||
if (!this.featuresProgressEvents) {
|
||||
@ -136,9 +135,8 @@ class Tech extends Component {
|
||||
this.emulateTextTracks();
|
||||
}
|
||||
|
||||
this.autoRemoteTextTracks_ = new TextTrackList();
|
||||
this.autoRemoteTextTracks_ = new TRACK_TYPES.ALL.text.ListClass();
|
||||
|
||||
this.initTextTrackListeners();
|
||||
this.initTrackListeners();
|
||||
|
||||
// Turn on component tap events only if not using native controls
|
||||
@ -332,7 +330,7 @@ class Tech extends Component {
|
||||
dispose() {
|
||||
|
||||
// clear out all tracks because we can't reuse them between techs
|
||||
this.clearTracks(['audio', 'video', 'text']);
|
||||
this.clearTracks(TRACK_TYPES.NORMAL.names);
|
||||
|
||||
// Turn off any manual progress or timeupdate tracking
|
||||
if (this.manualProgress) {
|
||||
@ -369,7 +367,7 @@ class Tech extends Component {
|
||||
if (type === 'text') {
|
||||
this.removeRemoteTextTrack(track);
|
||||
}
|
||||
list.removeTrack_(track);
|
||||
list.removeTrack(track);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -450,49 +448,16 @@ class Tech extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on listeners for {@link TextTrackList} events. This adds
|
||||
* {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and
|
||||
* `removetrack`.
|
||||
* Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
|
||||
* {@link TextTrackList} events.
|
||||
*
|
||||
* @fires Tech#texttrackchange
|
||||
*/
|
||||
initTextTrackListeners() {
|
||||
const textTrackListChanges = Fn.bind(this, function() {
|
||||
/**
|
||||
* Triggered when tracks are added or removed on the Tech {@link TextTrackList}
|
||||
*
|
||||
* @event Tech#texttrackchange
|
||||
* @type {EventTarget~Event}
|
||||
*/
|
||||
this.trigger('texttrackchange');
|
||||
});
|
||||
|
||||
const tracks = this.textTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
tracks.addEventListener('removetrack', textTrackListChanges);
|
||||
tracks.addEventListener('addtrack', textTrackListChanges);
|
||||
|
||||
this.on('dispose', Fn.bind(this, function() {
|
||||
tracks.removeEventListener('removetrack', textTrackListChanges);
|
||||
tracks.removeEventListener('addtrack', textTrackListChanges);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events.
|
||||
* This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.
|
||||
*
|
||||
* @fires Tech#audiotrackchange
|
||||
* @fires Tech#videotrackchange
|
||||
* @fires Tech#texttrackchange
|
||||
*/
|
||||
initTrackListeners() {
|
||||
const trackTypes = ['video', 'audio'];
|
||||
|
||||
trackTypes.forEach((type) => {
|
||||
/**
|
||||
* Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
|
||||
*
|
||||
@ -506,11 +471,20 @@ class Tech extends Component {
|
||||
* @event Tech#videotrackchange
|
||||
* @type {EventTarget~Event}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Triggered when tracks are added or removed on the Tech {@link TextTrackList}
|
||||
*
|
||||
* @event Tech#texttrackchange
|
||||
* @type {EventTarget~Event}
|
||||
*/
|
||||
TRACK_TYPES.NORMAL.names.forEach((name) => {
|
||||
const props = TRACK_TYPES.NORMAL[name];
|
||||
const trackListChanges = () => {
|
||||
this.trigger(`${type}trackchange`);
|
||||
this.trigger(`${name}trackchange`);
|
||||
};
|
||||
|
||||
const tracks = this[`${type}Tracks`]();
|
||||
const tracks = this[props.getterName]();
|
||||
|
||||
tracks.addEventListener('removetrack', trackListChanges);
|
||||
tracks.addEventListener('addtrack', trackListChanges);
|
||||
@ -585,16 +559,12 @@ class Tech extends Component {
|
||||
emulateTextTracks() {
|
||||
const tracks = this.textTracks();
|
||||
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.remoteTextTracks().on('addtrack', (e) => {
|
||||
this.textTracks().addTrack_(e.track);
|
||||
tracks.addTrack(e.track);
|
||||
});
|
||||
|
||||
this.remoteTextTracks().on('removetrack', (e) => {
|
||||
this.textTracks().removeTrack_(e.track);
|
||||
tracks.removeTrack(e.track);
|
||||
});
|
||||
|
||||
// Initially, Tech.el_ is a child of a dummy-div wait until the Component system
|
||||
@ -624,63 +594,6 @@ class Tech extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Tech`s {@link VideoTrackList}.
|
||||
*
|
||||
* @return {VideoTrackList}
|
||||
* The video track list that the Tech is currently using.
|
||||
*/
|
||||
videoTracks() {
|
||||
this.videoTracks_ = this.videoTracks_ || new VideoTrackList();
|
||||
return this.videoTracks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Tech`s {@link AudioTrackList}.
|
||||
*
|
||||
* @return {AudioTrackList}
|
||||
* The audio track list that the Tech is currently using.
|
||||
*/
|
||||
audioTracks() {
|
||||
this.audioTracks_ = this.audioTracks_ || new AudioTrackList();
|
||||
return this.audioTracks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Tech`s {@link TextTrackList}.
|
||||
*
|
||||
* @return {TextTrackList}
|
||||
* The text track list that the Tech is currently using.
|
||||
*/
|
||||
textTracks() {
|
||||
this.textTracks_ = this.textTracks_ || new TextTrackList();
|
||||
return this.textTracks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `Tech`s remote {@link TextTrackList}, which is created from elements
|
||||
* that were added to the DOM.
|
||||
*
|
||||
* @return {TextTrackList}
|
||||
* The remote text track list that the Tech is currently using.
|
||||
*/
|
||||
remoteTextTracks() {
|
||||
this.remoteTextTracks_ = this.remoteTextTracks_ || new TextTrackList();
|
||||
return this.remoteTextTracks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The `Tech`s {HTMLTrackElementList}, which are the elements in the DOM that are
|
||||
* being used as TextTracks.
|
||||
*
|
||||
* @return {HTMLTrackElementList}
|
||||
* The current HTML track elements that exist for the tech.
|
||||
*/
|
||||
remoteTextTrackEls() {
|
||||
this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new HTMLTrackElementList();
|
||||
return this.remoteTextTrackEls_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and returns a remote {@link TextTrack} object.
|
||||
*
|
||||
@ -730,7 +643,7 @@ class Tech extends Component {
|
||||
tech: this
|
||||
});
|
||||
|
||||
return new HTMLTrackElement(track);
|
||||
return new TRACK_TYPES.REMOTE.remoteTextEl.TrackClass(track);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -764,11 +677,11 @@ class Tech extends Component {
|
||||
|
||||
// store HTMLTrackElement and TextTrack to remote list
|
||||
this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
|
||||
this.remoteTextTracks().addTrack_(htmlTrackElement.track);
|
||||
this.remoteTextTracks().addTrack(htmlTrackElement.track);
|
||||
|
||||
if (manualCleanup !== true) {
|
||||
// create the TextTrackList if it doesn't exist
|
||||
this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track);
|
||||
this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
|
||||
}
|
||||
|
||||
return htmlTrackElement;
|
||||
@ -785,8 +698,8 @@ class Tech extends Component {
|
||||
|
||||
// remove HTMLTrackElement and TextTrack from remote list
|
||||
this.remoteTextTrackEls().removeTrackElement_(trackElement);
|
||||
this.remoteTextTracks().removeTrack_(track);
|
||||
this.autoRemoteTextTracks_.removeTrack_(track);
|
||||
this.remoteTextTracks().removeTrack(track);
|
||||
this.autoRemoteTextTracks_.removeTrack(track);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -878,28 +791,72 @@ class Tech extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* List of associated text tracks.
|
||||
* Get the {@link VideoTrackList}
|
||||
*
|
||||
* @returns {VideoTrackList}
|
||||
* @method Tech.prototype.videoTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the {@link AudioTrackList}
|
||||
*
|
||||
* @returns {AudioTrackList}
|
||||
* @method Tech.prototype.audioTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the {@link TextTrackList}
|
||||
*
|
||||
* @returns {TextTrackList}
|
||||
* @method Tech.prototype.textTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the remote element {@link TextTrackList}
|
||||
*
|
||||
* @returns {TextTrackList}
|
||||
* @method Tech.prototype.remoteTextTracks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the remote element {@link HTMLTrackElementList}
|
||||
*
|
||||
* @returns {HTMLTrackElementList}
|
||||
* @method Tech.prototype.remoteTextTrackEls
|
||||
*/
|
||||
|
||||
TRACK_TYPES.ALL.names.forEach(function(name) {
|
||||
const props = TRACK_TYPES.ALL[name];
|
||||
|
||||
Tech.prototype[props.getterName] = function() {
|
||||
this[props.privateName] = this[props.privateName] || new props.ListClass();
|
||||
return this[props.privateName];
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* List of associated text tracks
|
||||
*
|
||||
* @type {TextTrackList}
|
||||
* @private
|
||||
* @property Tech#textTracks_
|
||||
*/
|
||||
Tech.prototype.textTracks_; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* List of associated audio tracks.
|
||||
*
|
||||
* @type {AudioTrackList}
|
||||
* @private
|
||||
* @property Tech#audioTracks_
|
||||
*/
|
||||
Tech.prototype.audioTracks_; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* List of associated video tracks.
|
||||
*
|
||||
* @type {VideoTrackList}
|
||||
* @private
|
||||
* @property Tech#videoTracks_
|
||||
*/
|
||||
Tech.prototype.videoTracks_; // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Boolean indicating wether the `Tech` supports volume control.
|
||||
|
@ -81,15 +81,14 @@ class AudioTrackList extends TrackList {
|
||||
* @param {AudioTrack} track
|
||||
* The AudioTrack to add to the list
|
||||
*
|
||||
* @fires Track#addtrack
|
||||
* @private
|
||||
* @fires TrackList#addtrack
|
||||
*/
|
||||
addTrack_(track) {
|
||||
addTrack(track) {
|
||||
if (track.enabled) {
|
||||
disableOthers(this, track);
|
||||
}
|
||||
|
||||
super.addTrack_(track);
|
||||
super.addTrack(track);
|
||||
// native tracks don't have this
|
||||
if (!track.addEventListener) {
|
||||
return;
|
||||
@ -112,31 +111,6 @@ class AudioTrackList extends TrackList {
|
||||
this.trigger('change');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an {@link AudioTrack} to the `AudioTrackList`.
|
||||
*
|
||||
* @param {AudioTrack} track
|
||||
* The AudioTrack to add to the list
|
||||
*
|
||||
* @fires Track#addtrack
|
||||
*/
|
||||
addTrack(track) {
|
||||
this.addTrack_(track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an {@link AudioTrack} from the `AudioTrackList`.
|
||||
*
|
||||
* @param {AudioTrack} track
|
||||
* The AudioTrack to remove from the list
|
||||
*
|
||||
* @fires Track#removetrack
|
||||
*/
|
||||
removeTrack(track) {
|
||||
super.removeTrack_(track);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AudioTrackList;
|
||||
|
@ -114,28 +114,26 @@ class TextTrackDisplay extends Component {
|
||||
let firstDesc;
|
||||
let firstCaptions;
|
||||
|
||||
if (trackList) {
|
||||
for (let i = 0; i < trackList.length; i++) {
|
||||
const track = trackList[i];
|
||||
for (let i = 0; i < trackList.length; i++) {
|
||||
const track = trackList[i];
|
||||
|
||||
if (track.default) {
|
||||
if (track.kind === 'descriptions' && !firstDesc) {
|
||||
firstDesc = track;
|
||||
} else if (track.kind in modes && !firstCaptions) {
|
||||
firstCaptions = track;
|
||||
}
|
||||
if (track.default) {
|
||||
if (track.kind === 'descriptions' && !firstDesc) {
|
||||
firstDesc = track;
|
||||
} else if (track.kind in modes && !firstCaptions) {
|
||||
firstCaptions = track;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We want to show the first default track but captions and subtitles
|
||||
// take precedence over descriptions.
|
||||
// So, display the first default captions or subtitles track
|
||||
// and otherwise the first default descriptions track.
|
||||
if (firstCaptions) {
|
||||
firstCaptions.mode = 'showing';
|
||||
} else if (firstDesc) {
|
||||
firstDesc.mode = 'showing';
|
||||
}
|
||||
// We want to show the first default track but captions and subtitles
|
||||
// take precedence over descriptions.
|
||||
// So, display the first default captions or subtitles track
|
||||
// and otherwise the first default descriptions track.
|
||||
if (firstCaptions) {
|
||||
firstCaptions.mode = 'showing';
|
||||
} else if (firstDesc) {
|
||||
firstDesc.mode = 'showing';
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -192,17 +190,12 @@ class TextTrackDisplay extends Component {
|
||||
|
||||
this.clearDisplay();
|
||||
|
||||
if (!tracks) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Track display prioritization model: if multiple tracks are 'showing',
|
||||
// display the first 'subtitles' or 'captions' track which is 'showing',
|
||||
// otherwise display the first 'descriptions' track which is 'showing'
|
||||
|
||||
let descriptionsTrack = null;
|
||||
let captionsSubtitlesTrack = null;
|
||||
|
||||
let i = tracks.length;
|
||||
|
||||
while (i--) {
|
||||
|
@ -50,10 +50,9 @@ class TextTrackList extends TrackList {
|
||||
* The text track to add to the list.
|
||||
*
|
||||
* @fires TrackList#addtrack
|
||||
* @private
|
||||
*/
|
||||
addTrack_(track) {
|
||||
super.addTrack_(track);
|
||||
addTrack(track) {
|
||||
super.addTrack(track);
|
||||
|
||||
/**
|
||||
* @listens TextTrack#modechange
|
||||
|
@ -331,11 +331,9 @@ class TextTrack extends Track {
|
||||
addCue(cue) {
|
||||
const tracks = this.tech_.textTracks();
|
||||
|
||||
if (tracks) {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
if (tracks[i] !== this) {
|
||||
tracks[i].removeCue(cue);
|
||||
}
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
if (tracks[i] !== this) {
|
||||
tracks[i].removeCue(cue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class TrackList extends EventTarget {
|
||||
});
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
list.addTrack_(tracks[i]);
|
||||
list.addTrack(tracks[i]);
|
||||
}
|
||||
|
||||
// must return the object, as for ie8 it will not be this
|
||||
@ -65,9 +65,8 @@ class TrackList extends EventTarget {
|
||||
* The audio, video, or text track to add to the list.
|
||||
*
|
||||
* @fires TrackList#addtrack
|
||||
* @private
|
||||
*/
|
||||
addTrack_(track) {
|
||||
addTrack(track) {
|
||||
const index = this.tracks_.length;
|
||||
|
||||
if (!('' + index in this)) {
|
||||
@ -99,13 +98,12 @@ class TrackList extends EventTarget {
|
||||
/**
|
||||
* Remove a {@link Track} from the `TrackList`
|
||||
*
|
||||
* @param {Track} track
|
||||
* @param {Track} rtrack
|
||||
* The audio, video, or text track to remove from the list.
|
||||
*
|
||||
* @fires TrackList#removetrack
|
||||
* @private
|
||||
*/
|
||||
removeTrack_(rtrack) {
|
||||
removeTrack(rtrack) {
|
||||
let track;
|
||||
|
||||
for (let i = 0, l = this.length; i < l; i++) {
|
||||
|
68
src/js/tracks/track-types.js
Normal file
68
src/js/tracks/track-types.js
Normal file
@ -0,0 +1,68 @@
|
||||
import AudioTrackList from './audio-track-list';
|
||||
import VideoTrackList from './video-track-list';
|
||||
import TextTrackList from './text-track-list';
|
||||
import HTMLTrackElementList from './html-track-element-list';
|
||||
|
||||
import TextTrack from './text-track';
|
||||
import AudioTrack from './audio-track';
|
||||
import VideoTrack from './video-track';
|
||||
import HTMLTrackElement from './html-track-element';
|
||||
|
||||
import mergeOptions from '../utils/merge-options';
|
||||
|
||||
/**
|
||||
* This file contains all track properties that are used in
|
||||
* player.js, tech.js, html5.js and possibly other techs in the future.
|
||||
*/
|
||||
|
||||
const NORMAL = {
|
||||
audio: {
|
||||
ListClass: AudioTrackList,
|
||||
TrackClass: AudioTrack,
|
||||
capitalName: 'Audio'
|
||||
},
|
||||
video: {
|
||||
ListClass: VideoTrackList,
|
||||
TrackClass: VideoTrack,
|
||||
capitalName: 'Video'
|
||||
},
|
||||
text: {
|
||||
ListClass: TextTrackList,
|
||||
TrackClass: TextTrack,
|
||||
capitalName: 'Text'
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(NORMAL).forEach(function(type) {
|
||||
NORMAL[type].getterName = `${type}Tracks`;
|
||||
NORMAL[type].privateName = `${type}Tracks_`;
|
||||
});
|
||||
|
||||
const REMOTE = {
|
||||
remoteText: {
|
||||
ListClass: TextTrackList,
|
||||
TrackClass: TextTrack,
|
||||
capitalName: 'RemoteText',
|
||||
getterName: 'remoteTextTracks',
|
||||
privateName: 'remoteTextTracks_'
|
||||
},
|
||||
remoteTextEl: {
|
||||
ListClass: HTMLTrackElementList,
|
||||
TrackClass: HTMLTrackElement,
|
||||
capitalName: 'RemoteTextTrackEls',
|
||||
getterName: 'remoteTextTrackEls',
|
||||
privateName: 'remoteTextTrackEls_'
|
||||
}
|
||||
};
|
||||
|
||||
const ALL = mergeOptions(NORMAL, REMOTE);
|
||||
|
||||
REMOTE.names = Object.keys(REMOTE);
|
||||
NORMAL.names = Object.keys(NORMAL);
|
||||
ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
|
||||
|
||||
export {
|
||||
NORMAL,
|
||||
REMOTE,
|
||||
ALL
|
||||
};
|
@ -97,14 +97,13 @@ class VideoTrackList extends TrackList {
|
||||
* The VideoTrack to add to the list
|
||||
*
|
||||
* @fires TrackList#addtrack
|
||||
* @private
|
||||
*/
|
||||
addTrack_(track) {
|
||||
addTrack(track) {
|
||||
if (track.selected) {
|
||||
disableOthers(this, track);
|
||||
}
|
||||
|
||||
super.addTrack_(track);
|
||||
super.addTrack(track);
|
||||
// native tracks don't have this
|
||||
if (!track.addEventListener) {
|
||||
return;
|
||||
@ -124,31 +123,6 @@ class VideoTrackList extends TrackList {
|
||||
this.trigger('change');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link VideoTrack} to the `VideoTrackList`.
|
||||
*
|
||||
* @param {VideoTrack} track
|
||||
* The VideoTrack to add to the list
|
||||
*
|
||||
* @fires TrackList#addtrack
|
||||
*/
|
||||
addTrack(track) {
|
||||
this.addTrack_(track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a {@link VideoTrack} to the `VideoTrackList`.
|
||||
*
|
||||
* @param {VideoTrack} track
|
||||
* The VideoTrack to remove from the list.
|
||||
*
|
||||
* @fires TrackList#removetrack
|
||||
*/
|
||||
removeTrack(track) {
|
||||
super.removeTrack_(track);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default VideoTrackList;
|
||||
|
@ -139,11 +139,11 @@ QUnit.test('dispose() should clear all tracks that are added after creation', fu
|
||||
tech.addRemoteTextTrack({}, true);
|
||||
tech.addRemoteTextTrack({}, true);
|
||||
|
||||
tech.audioTracks().addTrack_(new AudioTrack());
|
||||
tech.audioTracks().addTrack_(new AudioTrack());
|
||||
tech.audioTracks().addTrack(new AudioTrack());
|
||||
tech.audioTracks().addTrack(new AudioTrack());
|
||||
|
||||
tech.videoTracks().addTrack_(new VideoTrack());
|
||||
tech.videoTracks().addTrack_(new VideoTrack());
|
||||
tech.videoTracks().addTrack(new VideoTrack());
|
||||
tech.videoTracks().addTrack(new VideoTrack());
|
||||
|
||||
assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start');
|
||||
assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start');
|
||||
@ -354,11 +354,11 @@ QUnit.test('should add the source handler interface to a tech', function(assert)
|
||||
tech.addRemoteTextTrack({}, true);
|
||||
tech.addRemoteTextTrack({}, true);
|
||||
|
||||
tech.audioTracks().addTrack_(new AudioTrack());
|
||||
tech.audioTracks().addTrack_(new AudioTrack());
|
||||
tech.audioTracks().addTrack(new AudioTrack());
|
||||
tech.audioTracks().addTrack(new AudioTrack());
|
||||
|
||||
tech.videoTracks().addTrack_(new VideoTrack());
|
||||
tech.videoTracks().addTrack_(new VideoTrack());
|
||||
tech.videoTracks().addTrack(new VideoTrack());
|
||||
tech.videoTracks().addTrack(new VideoTrack());
|
||||
|
||||
assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start');
|
||||
assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start');
|
||||
@ -586,4 +586,3 @@ QUnit.test('setSource after previous setSource should dispose source handler onc
|
||||
assert.equal(disposeCount, 2, 'did dispose for third setSource');
|
||||
|
||||
});
|
||||
|
||||
|
@ -40,7 +40,7 @@ QUnit.test('only one track is ever enabled', function(assert) {
|
||||
assert.equal(track.enabled, true, 'track is enabled');
|
||||
assert.equal(track2.enabled, false, 'track2 is disabled');
|
||||
|
||||
list.addTrack_(track3);
|
||||
list.addTrack(track3);
|
||||
assert.equal(track.enabled, false, 'track is disabled');
|
||||
assert.equal(track2.enabled, false, 'track2 is disabled');
|
||||
assert.equal(track3.enabled, true, 'track3 is enabled');
|
||||
@ -50,7 +50,7 @@ QUnit.test('only one track is ever enabled', function(assert) {
|
||||
assert.equal(track2.enabled, false, 'track2 is disabled');
|
||||
assert.equal(track3.enabled, false, 'track3 is disabled');
|
||||
|
||||
list.addTrack_(track4);
|
||||
list.addTrack(track4);
|
||||
assert.equal(track.enabled, true, 'track is enabled');
|
||||
assert.equal(track2.enabled, false, 'track2 is disabled');
|
||||
assert.equal(track3.enabled, false, 'track3 is disabled');
|
||||
@ -92,7 +92,7 @@ QUnit.test('trigger a change event per enabled change', function(assert) {
|
||||
track.enabled = true;
|
||||
assert.equal(change, 1, 'one change triggered');
|
||||
|
||||
list.addTrack_(track3);
|
||||
list.addTrack(track3);
|
||||
assert.equal(change, 2, 'another change triggered by adding an enabled track');
|
||||
|
||||
track.enabled = true;
|
||||
@ -101,7 +101,7 @@ QUnit.test('trigger a change event per enabled change', function(assert) {
|
||||
track.enabled = false;
|
||||
assert.equal(change, 4, 'another change trigger by changing enabled');
|
||||
|
||||
list.addTrack_(track4);
|
||||
list.addTrack(track4);
|
||||
assert.equal(change, 4, 'no change triggered by adding a disabled track');
|
||||
|
||||
});
|
||||
|
@ -1,8 +1,11 @@
|
||||
/* eslint-env qunit */
|
||||
import HTMLTrackElement from '../../../src/js/tracks/html-track-element.js';
|
||||
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
|
||||
|
||||
const defaultTech = {
|
||||
textTracks() {},
|
||||
textTracks() {
|
||||
return new TextTrackList();
|
||||
},
|
||||
on() {},
|
||||
off() {},
|
||||
currentTime() {}
|
||||
|
@ -56,8 +56,8 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
|
||||
const tt = new TextTrackList();
|
||||
|
||||
tt.addTrack_(nativeTrack.track);
|
||||
tt.addTrack_(emulatedTrack);
|
||||
tt.addTrack(nativeTrack.track);
|
||||
tt.addTrack(emulatedTrack);
|
||||
|
||||
const tech = {
|
||||
$$() {
|
||||
@ -107,8 +107,8 @@ if (Html5.supportsNativeTextTracks()) {
|
||||
|
||||
const tt = new TextTrackList();
|
||||
|
||||
tt.addTrack_(nativeTrack.track);
|
||||
tt.addTrack_(emulatedTrack);
|
||||
tt.addTrack(nativeTrack.track);
|
||||
tt.addTrack(emulatedTrack);
|
||||
|
||||
let addRemotes = 0;
|
||||
const tech = {
|
||||
@ -177,8 +177,8 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct
|
||||
|
||||
const tt = new TextTrackList();
|
||||
|
||||
tt.addTrack_(anotherTrack);
|
||||
tt.addTrack_(emulatedTrack);
|
||||
tt.addTrack(anotherTrack);
|
||||
tt.addTrack(emulatedTrack);
|
||||
|
||||
const tech = {
|
||||
$$() {
|
||||
@ -231,8 +231,8 @@ QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated
|
||||
|
||||
const tt = new TextTrackList();
|
||||
|
||||
tt.addTrack_(anotherTrack);
|
||||
tt.addTrack_(emulatedTrack);
|
||||
tt.addTrack(anotherTrack);
|
||||
tt.addTrack(emulatedTrack);
|
||||
|
||||
let addRemotes = 0;
|
||||
const tech = {
|
||||
|
@ -7,10 +7,13 @@ import TextTrack from '../../../src/js/tracks/text-track.js';
|
||||
import TestHelpers from '../test-helpers.js';
|
||||
import proxyquireify from 'proxyquireify';
|
||||
import sinon from 'sinon';
|
||||
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
|
||||
|
||||
const proxyquire = proxyquireify(require);
|
||||
const defaultTech = {
|
||||
textTracks() {},
|
||||
textTracks() {
|
||||
return new TextTrackList();
|
||||
},
|
||||
on() {},
|
||||
off() {},
|
||||
currentTime() {}
|
||||
|
@ -370,7 +370,7 @@ QUnit.test('removes cuechange event when text track is hidden for emulated track
|
||||
startTime: 2,
|
||||
endTime: 5
|
||||
});
|
||||
player.tech_.textTracks().addTrack_(tt);
|
||||
player.tech_.textTracks().addTrack(tt);
|
||||
|
||||
let numTextTrackChanges = 0;
|
||||
|
||||
|
@ -33,20 +33,20 @@ QUnit.test('can get tracks by int and string id', function(assert) {
|
||||
QUnit.test('length is updated when new tracks are added or removed', function(assert) {
|
||||
const trackList = new TrackList(this.tracks);
|
||||
|
||||
trackList.addTrack_(newTrack('100'));
|
||||
trackList.addTrack(newTrack('100'));
|
||||
assert.equal(trackList.length,
|
||||
this.tracks.length + 1,
|
||||
'the length is ' + (this.tracks.length + 1));
|
||||
trackList.addTrack_(newTrack('101'));
|
||||
trackList.addTrack(newTrack('101'));
|
||||
assert.equal(trackList.length,
|
||||
this.tracks.length + 2,
|
||||
'the length is ' + (this.tracks.length + 2));
|
||||
|
||||
trackList.removeTrack_(trackList.getTrackById('101'));
|
||||
trackList.removeTrack(trackList.getTrackById('101'));
|
||||
assert.equal(trackList.length,
|
||||
this.tracks.length + 1,
|
||||
'the length is ' + (this.tracks.length + 1));
|
||||
trackList.removeTrack_(trackList.getTrackById('100'));
|
||||
trackList.removeTrack(trackList.getTrackById('100'));
|
||||
assert.equal(trackList.length,
|
||||
this.tracks.length,
|
||||
'the length is ' + this.tracks.length);
|
||||
@ -68,23 +68,23 @@ QUnit.test('can access items by index', function(assert) {
|
||||
QUnit.test('can access new items by index', function(assert) {
|
||||
const trackList = new TrackList(this.tracks);
|
||||
|
||||
trackList.addTrack_(newTrack('100'));
|
||||
trackList.addTrack(newTrack('100'));
|
||||
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
|
||||
|
||||
trackList.addTrack_(newTrack('101'));
|
||||
trackList.addTrack(newTrack('101'));
|
||||
assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101');
|
||||
});
|
||||
|
||||
QUnit.test('cannot access removed items by index', function(assert) {
|
||||
const trackList = new TrackList(this.tracks);
|
||||
|
||||
trackList.addTrack_(newTrack('100'));
|
||||
trackList.addTrack_(newTrack('101'));
|
||||
trackList.addTrack(newTrack('100'));
|
||||
trackList.addTrack(newTrack('101'));
|
||||
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
|
||||
assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101');
|
||||
|
||||
trackList.removeTrack_(trackList.getTrackById('101'));
|
||||
trackList.removeTrack_(trackList.getTrackById('100'));
|
||||
trackList.removeTrack(trackList.getTrackById('101'));
|
||||
trackList.removeTrack(trackList.getTrackById('100'));
|
||||
|
||||
assert.ok(!trackList[3], 'nothing at index 3');
|
||||
assert.ok(!trackList[4], 'nothing at index 4');
|
||||
@ -93,13 +93,13 @@ QUnit.test('cannot access removed items by index', function(assert) {
|
||||
QUnit.test('new item available at old index', function(assert) {
|
||||
const trackList = new TrackList(this.tracks);
|
||||
|
||||
trackList.addTrack_(newTrack('100'));
|
||||
trackList.addTrack(newTrack('100'));
|
||||
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
|
||||
|
||||
trackList.removeTrack_(trackList.getTrackById('100'));
|
||||
trackList.removeTrack(trackList.getTrackById('100'));
|
||||
assert.ok(!trackList[3], 'nothing at index 3');
|
||||
|
||||
trackList.addTrack_(newTrack('101'));
|
||||
trackList.addTrack(newTrack('101'));
|
||||
assert.equal(trackList[3].id, '101', 'id of new item at index 3 is now 101');
|
||||
});
|
||||
|
||||
@ -116,14 +116,14 @@ QUnit.test('a "addtrack" event is triggered when new tracks are added', function
|
||||
|
||||
trackList.on('addtrack', addHandler);
|
||||
|
||||
trackList.addTrack_(newTrack('100'));
|
||||
trackList.addTrack_(newTrack('101'));
|
||||
trackList.addTrack(newTrack('100'));
|
||||
trackList.addTrack(newTrack('101'));
|
||||
|
||||
trackList.off('addtrack', addHandler);
|
||||
trackList.onaddtrack = addHandler;
|
||||
|
||||
trackList.addTrack_(newTrack('102'));
|
||||
trackList.addTrack_(newTrack('103'));
|
||||
trackList.addTrack(newTrack('102'));
|
||||
trackList.addTrack(newTrack('103'));
|
||||
|
||||
assert.equal(adds, 4, 'we got ' + adds + ' "addtrack" events');
|
||||
assert.equal(tracks, 4, 'we got a track with every event');
|
||||
@ -141,12 +141,12 @@ QUnit.test('a "removetrack" event is triggered when tracks are removed', functio
|
||||
};
|
||||
|
||||
trackList.on('removetrack', rmHandler);
|
||||
trackList.removeTrack_(trackList.getTrackById('1'));
|
||||
trackList.removeTrack_(trackList.getTrackById('2'));
|
||||
trackList.removeTrack(trackList.getTrackById('1'));
|
||||
trackList.removeTrack(trackList.getTrackById('2'));
|
||||
|
||||
trackList.off('removetrack', rmHandler);
|
||||
trackList.onremovetrack = rmHandler;
|
||||
trackList.removeTrack_(trackList.getTrackById('3'));
|
||||
trackList.removeTrack(trackList.getTrackById('3'));
|
||||
|
||||
assert.equal(rms, 3, 'we got ' + rms + ' "removetrack" events');
|
||||
assert.equal(tracks, 3, 'we got a track with every event');
|
||||
|
@ -2,9 +2,12 @@
|
||||
import TechFaker from '../tech/tech-faker';
|
||||
import TrackBaseline from './track-baseline';
|
||||
import Track from '../../../src/js/tracks/track.js';
|
||||
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
|
||||
|
||||
const defaultTech = {
|
||||
textTracks() {},
|
||||
textTracks() {
|
||||
return new TextTrackList();
|
||||
},
|
||||
on() {},
|
||||
off() {},
|
||||
currentTime() {}
|
||||
|
@ -40,7 +40,7 @@ QUnit.test('only one track is ever selected', function(assert) {
|
||||
assert.equal(track.selected, true, 'track is selected');
|
||||
assert.equal(track2.selected, false, 'track2 is unselected');
|
||||
|
||||
list.addTrack_(track3);
|
||||
list.addTrack(track3);
|
||||
assert.equal(track.selected, false, 'track is unselected');
|
||||
assert.equal(track2.selected, false, 'track2 is unselected');
|
||||
assert.equal(track3.selected, true, 'track3 is selected');
|
||||
@ -50,7 +50,7 @@ QUnit.test('only one track is ever selected', function(assert) {
|
||||
assert.equal(track2.selected, false, 'track2 is unselected');
|
||||
assert.equal(track3.selected, false, 'track3 is unselected');
|
||||
|
||||
list.addTrack_(track4);
|
||||
list.addTrack(track4);
|
||||
assert.equal(track.selected, true, 'track is selected');
|
||||
assert.equal(track2.selected, false, 'track2 is unselected');
|
||||
assert.equal(track3.selected, false, 'track3 is unselected');
|
||||
@ -89,7 +89,7 @@ QUnit.test('trigger a change event per selected change', function(assert) {
|
||||
track.selected = true;
|
||||
assert.equal(change, 1, 'one change triggered');
|
||||
|
||||
list.addTrack_(track3);
|
||||
list.addTrack(track3);
|
||||
assert.equal(change, 2, 'another change triggered by adding an selected track');
|
||||
|
||||
track.selected = true;
|
||||
@ -98,6 +98,6 @@ QUnit.test('trigger a change event per selected change', function(assert) {
|
||||
track.selected = false;
|
||||
assert.equal(change, 4, 'another change trigger by changing selected');
|
||||
|
||||
list.addTrack_(track4);
|
||||
list.addTrack(track4);
|
||||
assert.equal(change, 4, 'no change triggered by adding a unselected track');
|
||||
});
|
||||
|
Reference in New Issue
Block a user