/**
* @archivo tech.js
* /
importar componente desde '../componente';
importar mergeOptions desde '../utils/merge-options.js';
importar * como Fn desde '../utils/fn.js';
importar registro desde '../utils/log.js';
import { createTimeRange } from '../utils/time-ranges.js';
importar {bufferedPercent} desde '../utils/buffer.js';
importar MediaError desde '../media-error.js';
importar ventana desde 'global/window';
importar documento desde 'global/document';
importar {isPlain} desde '../utils/obj';
importar * como TRACK_TYPES desde '../tracks/track-types';
importar {toTitleCase, toLowerCase} desde '../utils/string-cases.js';
importar vtt desde 'videojs-vtt.js';
importar * como Guid desde '../utils/guid.js';
/**
* Un objeto que contiene una estructura como: `{src: 'url', type: 'mimetype'}` o cadena
* que solo contiene la url src sola.
* * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
* `var SourceString = 'http://example.com/some-video.mp4';`
*
* @typedef {Objeto|cadena} Tech~SourceObject
*
* @propiedad {cadena} src
* La url a la fuente
*
* tipo @property {cadena}
* El tipo mime de la fuente
* /
/**
* Una función utilizada por {@link Tech} para crear un nuevo {@link TextTrack}.
*
* @privado
*
* @param {Tecnología} yo mismo
* Una instancia de la clase Tech.
*
* @param {cadena} tipo
* Tipo `TextTrack` (subtítulos, leyendas, descripciones, capítulos o metadatos)
*
* @param {cadena} [etiqueta]
* Etiqueta para identificar la pista de texto
*
* @param {cadena} [idioma]
* Abreviatura de idioma de dos letras
*
* @param {Objeto} [opciones={}]
* Un objeto con opciones de seguimiento de texto adicionales
*
* @return {Pista de texto}
* La pista de texto que se creó.
* /
function createTrackHelper(self, tipo, etiqueta, idioma, opciones = {}) {
const pistas = self.textTracks();
opciones.tipo = tipo;
si (etiqueta) {
opciones.etiqueta = etiqueta;
}
si (idioma) {
opciones.idioma = idioma;
}
opciones.tech = self;
const track = new TRACK_TYPES.ALL.text.TrackClass(opciones);
pistas.addTrack(pista);
pista de retorno;
}
/**
* Esta es la clase base para los controladores de tecnología de reproducción multimedia, como
* {@enlace HTML5}
*
* Componente @extiende
* /
class Tech extiende el componente {
/**
* Crear una instancia de este Tech.
*
* @param {Objeto} [opciones]
* El almacén de clave/valor de las opciones del jugador.
*
* @param {Componente~ReadyCallback} listo
* Función de devolución de llamada para llamar cuando la tecnología `HTML5` esté lista.
* /
constructor(opciones = {}, listo = función() {}) {
// no queremos que la tecnología informe la actividad del usuario automáticamente.
// Esto se hace manualmente en addControlsListeners
opciones.reportTouchActivity = falso;
super(nulo, opciones, listo);
this.onDurationChange_ = (e) => this.onDurationChange(e);
this.trackProgress_ = (e) => este.trackProgress(e);
this.trackCurrentTime_ = (e) => this.trackCurrentTime(e);
this.stopTrackingCurrentTime_ = (e) => this.stopTrackingCurrentTime(e);
this.disposeSourceHandler_ = (e) => this.disposeSourceHandler(e);
this.queuedHanders_ = new Set();
// realizar un seguimiento de si la fuente actual se ha reproducido en absoluto para
// implementar un jugado() muy limitado
this.hasStarted_ = false;
this.on('jugando', function() {
this.hasStarted_ = true;
});
this.on('loadstart', function() {
this.hasStarted_ = false;
});
TRACK_TYPES.ALL.names.forEach((nombre) => {
const props = TRACK_TYPES.ALL[nombre];
si (opciones && opciones[props.getterName]) {
this[props.privateName] = options[props.getterName];
}
});
// Realice un seguimiento manual del progreso en los casos en que el navegador/tecnología no lo informe.
if (!this.featuresProgressEvents) {
this.manualProgressOn();
}
// Realice un seguimiento manual de las actualizaciones de tiempo en los casos en que el navegador/tecnología no lo informe.
if (!this.featuresTimeupdateEvents) {
this.manualTimeUpdatesOn();
}
['Texto', 'Audio', 'Video'].forEach((pista) => {
if (opciones[`native${pista}Pistas`] === falso) {
this[`featuresNative${track}Tracks`] = false;
}
});
if (opciones.nativeCaptions === false || options.nativeTextTracks === false) {
this.featuresNativeTextTracks = false;
} else if (opciones.nativeCaptions === verdadero || opciones.nativeTextTracks === verdadero) {
this.featuresNativeTextTracks = true;
}
if (!this.featuresNativeTextTracks) {
this.emulateTextTracks();
}
this.preloadTextTracks = options.preloadTextTracks !== false;
this.autoRemoteTextTracks_ = new TRACK_TYPES.ALL.text.ListClass();
this.initTrackListeners();
// Active los eventos de pulsación de componentes solo si no utiliza controles nativos
si (!opciones.nativeControlsForTouch) {
esto.emitTapEvents();
}
if (este.constructor) {
este.nombre_ = este.constructor.nombre || 'Tecnología desconocida';
}
}
/**
* Una función especial para activar el conjunto de fuentes de una manera que permitirá al jugador
* para volver a activar si el jugador o la tecnología aún no están listos.
*
* @fuegos Tech#sourceset
* @param {string} src La cadena de origen en el momento del cambio de fuente.
* /
triggerSourceset(origen) {
si (!esto.estáListo_) {
// en la preparación inicial, tenemos que activar el conjunto de fuentes
// 1 ms después de listo para que el jugador pueda verlo.
this.one('listo', () => this.setTimeout(() => this.triggerSourceset(src), 1));
}
/**
* Activado cuando la fuente está configurada en la tecnología que causa el elemento multimedia
* para recargar.
*
* @ver {@link Player#event:sourceset}
* @event Tech#sourceset
* @type {Objetivo del evento~Evento}
* /
este.disparador({
origen,
tipo: 'conjunto de fuentes'
});
}
/* Reservas para tipos de eventos no admitidos
================================================== ==============================*/
/**
* Polyfill el evento `progress` para los navegadores que no lo admiten de forma nativa.
*
* @ver {@link Tech#trackProgress}
* /
manualProgressOn() {
this.on('cambioduración', this.onDurationChange_);
este.progresomanual = verdadero;
// Activar la observación del progreso cuando una fuente comienza a cargarse
this.one('listo', this.trackProgress_);
}
/**
* Desactivar el polyfill para eventos de `progreso` que se creó en
* {@link Tech#manualProgressOn}
* /
ManualProgressOff() {
este.progresomanual = falso;
this.stopTrackingProgress();
this.off('cambioduración', this.onDurationChange_);
}
/**
* Esto se usa para desencadenar un evento de `progreso` cuando cambia el porcentaje almacenado en el búfer. Él
* establece una función de intervalo que se llamará cada 500 milisegundos para verificar si el
* el porcentaje final del búfer ha cambiado.
*
* > Esta función es llamada por {@link Tech#manualProgressOn}
*
* @param {EventTarget~Evento} evento
* El evento `ready` que hizo que esto se ejecutara.
*
* @escucha Tech#listo
* @fires Tech#progreso
* /
trackProgress(evento) {
this.stopTrackingProgress();
this.progressInterval = this.setInterval(Fn.bind(this, function() {
// No activar a menos que la cantidad almacenada en búfer sea mayor que la última vez
const numBufferedPercent = this.bufferedPercent();
if (this.bufferedPercent_ !== numBufferedPercent) {
/**
* Ver {@link Player#progress}
*
* @event Tech#progreso
* @type {Objetivo del evento~Evento}
* /
this.trigger('progreso');
}
this.bufferedPercent_ = numBufferedPercent;
if (numeroPorcentajeBúfer === 1) {
this.stopTrackingProgress();
}
}), 500);
}
/**
* Actualice nuestra duración interna en un evento `durationchange` llamando
* {@link Tecnología#duración}.
*
* @param {EventTarget~Evento} evento
* El evento `durationchange` que hizo que esto se ejecutara.
*
* @escucha Tech#durationchange
* /
onDurationChange(evento) {
this.duration_ = this.duration();
}
/**
* Obtenga y cree un objeto `TimeRange` para el almacenamiento en búfer.
*
* @return {Intervalo de tiempo}
* El objeto de intervalo de tiempo que se creó.
* /
almacenado en búfer () {
volver crearRangoTiempo(0, 0);
}
/**
* Obtenga el porcentaje del video actual que actualmente está almacenado en búfer.
*
* @return {número}
* Un número del 0 al 1 que representa el porcentaje decimal de la
* video que está almacenado en búfer.
*
* /
porcentajebuffered() {
return bufferedPercent(this.buffered(), this.duration_);
}
/**
* Desactivar el polyfill para eventos de `progreso` que se creó en
* {@link Tech#manualProgressOn}
* Deje de rastrear manualmente los eventos de progreso borrando el intervalo que se estableció en
* {@link Tech#trackProgress}.
* /
detener el seguimiento del progreso () {
este.clearInterval(este.progressInterval);
}
/**
* Polyfill el evento `timeupdate` para los navegadores que no lo admiten.
*
* @ver {@link Tech#trackCurrentTime}
* /
manualTimeUpdatesOn() {
this.manualTimeUpdates = verdadero;
this.on('reproducir', this.trackCurrentTime_);
this.on('pausa', this.stopTrackingCurrentTime_);
}
/**
* Desactivar el polyfill para eventos `timeupdate` que se creó en
* {@link Tech#manualTimeUpdatesOn}
* /
manualTimeUpdatesOff() {
this.manualTimeUpdates = falso;
this.stopTrackingCurrentTime();
this.off('reproducir', this.trackCurrentTime_);
this.off('pausa', this.stopTrackingCurrentTime_);
}
/**
* Configura una función de intervalo para realizar un seguimiento de la hora actual y activar `timeupdate` cada
* 250 milisegundos.
*
* @escucha Tech#play
* @triggers Tech#timeupdate
* /
seguimientoHoraActual() {
if (este.intervaloTiempoActual) {
this.stopTrackingCurrentTime();
}
this.currentTimeInterval = this.setInterval(function() {
/**
* Activado en un intervalo de 250 ms para indicar que el tiempo está pasando en el video.
*
* @event Tech#timeupdate
* @type {Objetivo del evento~Evento}
* /
this.trigger({ type: 'timeupdate', target: this, manualmente Triggered: true });
// 42 = 24 fps // 250 es lo que usa Webkit // FF usa 15
}, 250);
}
/**
* Detenga la función de intervalo creada en {@link Tech#trackCurrentTime} para que el
* El evento `timeupdate` ya no se activa.
*
* @escucha {Tecnología#pausa}
* /
detener el seguimiento de la hora actual () {
this.clearInterval(this.currentTimeInterval);
// #1002 - si el video termina justo antes de que ocurra la próxima actualización,
// la barra de progreso no llegará hasta el final
this.trigger({ type: 'timeupdate', target: this, manualmente Triggered: true });
}
/**
* Apague todos los polyfills de eventos, borre la `Tech`s {@link AudioTrackList},
* {@link VideoTrackList} y {@link TextTrackList}, y deseche esta tecnología.
*
* Componente @fires#dispose
* /
disponer () {
// borrar todas las pistas porque no podemos reutilizarlas entre técnicos
this.clearTracks(TRACK_TYPES.NORMAL.names);
// Desactiva cualquier progreso manual o seguimiento de actualización de tiempo
if (este.progresomanual) {
this.manualProgressOff();
}
if (this.manualTimeUpdates) {
this.manualTimeUpdatesOff();
}
super.dispose();
}
/**
* Borrar una sola `TrackList` o una matriz de `TrackLists` dados sus nombres.
*
* > Nota: Los técnicos sin controladores de fuentes deberían llamar a esto entre fuentes para `video`
* & Pistas de `audio`. ¡No querrás usarlos entre pistas!
*
* @param {cadena[]|cadena} tipos
* Los nombres de TrackList para borrar, los nombres válidos son `video`, `audio` y
* `texto`.
* /
borrar pistas (tipos) {
tipos = [].concat(tipos);
// borrar todas las pistas porque no podemos reutilizarlas entre técnicos
tipos.forEach((tipo) => {
const list = this[`${type}Pistas`]() || [];
let i = lista.longitud;
mientras yo--) {
const pista = lista[i];
if (escriba === 'texto') {
this.removeRemoteTextTrack(pista);
}
list.removeTrack(pista);
}
});
}
/**
* Elimine cualquier TextTracks agregado a través de addRemoteTextTrack que sea
* marcado para recolección automática de basura
* /
limpiezaAutoTextTracks() {
const list = this.autoRemoteTextTracks_ || [];
let i = lista.longitud;
mientras yo--) {
const pista = lista[i];
this.removeRemoteTextTrack(pista);
}
}
/**
* Restablecer la tecnología, que eliminará todas las fuentes y restablecerá el estado de preparación interno.
*
* @abstracto
* /
reiniciar() {}
/**
* Obtenga el valor de `crossOrigin` de la tecnología.
*
* @abstracto
*
* @ver {Html5#crossOrigin}
* /
origencruz() {}
/**
* Establezca el valor de `crossOrigin` en la tecnología.
*
* @abstracto
*
* @param {string} crossOrigin el valor de crossOrigin
* @ver {Html5#setCrossOrigin}
* /
establecer origen cruzado () {}
/**
* Obtener o establecer un error en el Tech.
*
* @param {Error de medios} [error]
* Error al configurar en el Tech
*
* @return {Error de Medios|null}
* El objeto de error actual en la tecnología, o nulo si no lo hay.
* /
error (err) {
si (err !== indefinido) {
this.error_ = new MediaError(err);
this.trigger('error');
}
devolver este.error_;
}
/**
* Devuelve los `TimeRange`s que se han reproducido para la fuente actual.
*
* > NOTA: Esta implementación está incompleta. No rastrea el `TimeRange` reproducido.
* Solo comprueba si la fuente se ha reproducido o no.
*
* @return {Intervalo de tiempo}
* - Un solo rango de tiempo si este video se ha reproducido
* - Un conjunto vacío de rangos si no.
* /
jugó() {
if (esto.haComenzado_) {
volver crearRangoTiempo(0, 0);
}
devuelve createTimeRange();
}
/**
* Iniciar reproducción
*
* @abstracto
*
* @ver {Html5#play}
* /
jugar() {}
/**
* Establecer si estamos fregando o no
*
* @abstracto
*
* @ver {Html5#setScrubbing}
* /
setScrubbing() {}
/**
* Obtener si estamos fregando o no
*
* @abstracto
*
* @ver {Html5#scrubbing}
* /
fregado() {}
/**
* Hace que se produzca una actualización manual de la hora si {@link Tech#manualTimeUpdatesOn} fue
* previamente llamado.
*
* @Fire Tech#timeupdate
* /
establecerHoraActual() {
// mejorar la precisión de las actualizaciones de tiempo manuales
if (this.manualTimeUpdates) {
/**
* Un evento manual `timeupdate`.
*
* @event Tech#timeupdate
* @type {Objetivo del evento~Evento}
* /
this.trigger({ type: 'timeupdate', target: this, manualmente Triggered: true });
}
}
/**
* Activar oyentes para {@link VideoTrackList}, {@link {AudioTrackList} y
* {@link TextTrackList} eventos.
*
* Esto agrega {@link EventTarget~EventListeners} para `addtrack` y `removetrack`.
*
* @fires Tech#cambio de pista de audio
* @fires Tech#videotrackchange
* @fires Tech#texttrackchange
* /
initTrackListeners() {
/**
* Se activa cuando se agregan o eliminan pistas en Tech {@link AudioTrackList}
*
* @event Tech#cambio de pista de audio
* @type {Objetivo del evento~Evento}
* /
/**
* Se activa cuando se agregan o eliminan pistas en Tech {@link VideoTrackList}
*
* @event Tech#videotrackchange
* @type {Objetivo del evento~Evento}
* /
/**
* Se activa cuando se agregan o eliminan pistas en Tech {@link TextTrackList}
*
* @event Tech#texttrackchange
* @type {Objetivo del evento~Evento}
* /
TRACK_TYPES.NORMAL.names.forEach((nombre) => {
const props = TRACK_TYPES.NORMAL[nombre];
const trackListChanges = () => {
this.trigger(`${nombre}cambio de pista`);
};
const pistas = this[props.getterName]();
pistas.addEventListener('removetrack', trackListChanges);
pistas.addEventListener('addtrack', trackListChanges);
this.on('dispose', () => {
pistas.removeEventListener('removetrack', trackListChanges);
pistas.removeEventListener('addtrack', trackListChanges);
});
});
}
/**
* Emular TextTracks usando vtt.js si es necesario
*
* @fires Tech#vttjsloaded
* @fires Tech#vttjserror
* /
agregarWebVttScript_() {
si (ventana.WebVTT) {
devolver;
}
// Inicialmente, Tech.el_ es un elemento secundario de un div ficticio, espere hasta que el sistema de componentes
// señala que el Tech está listo en cuyo punto Tech.el_ es parte del DOM
// antes de insertar el script WebVTT
if (documento.cuerpo.contiene(esto.el())) {
// cargar a través de require si está disponible y no se pasó la ubicación del script vtt.js
// como una opción. Las compilaciones novtt convertirán la llamada requerida anterior en un objeto vacío
// lo que causará esto si la verificación falla siempre.
if (!this.options_['vtt.js'] && es simple (vtt) && Objeto.claves(vtt).longitud > 0) {
this.trigger('vttjsloaded');
devolver;
}
// cargar vtt.js a través de la opción de ubicación del script o el cdn sin ubicación fue
// aprobada en
const guión = documento.createElement('guión');
script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
script.onload = () => {
/**
* Se activa cuando se carga vtt.js.
*
* @event Tech#vttjsloaded
* @type {Objetivo del evento~Evento}
* /
this.trigger('vttjsloaded');
};
script.onerror = () => {
/**
* Activado cuando vtt.js no se cargó debido a un error
*
* @event Tech#vttjsloaded
* @type {Objetivo del evento~Evento}
* /
this.trigger('vttjserror');
};
this.on('dispose', () => {
secuencia de comandos.onload = nulo;
script.onerror = nulo;
});
// pero aún no se ha cargado y lo establecemos en verdadero antes de la inyección para que
// no sobrescribimos la ventana inyectada.WebVTT si se carga de inmediato
ventana.WebVTT = verdadero;
this.el().parentNode.appendChild(script);
} else {
esto.listo(esto.addWebVttScript_);
}
}
/**
* Emular pistas de texto
*
* /
emular pistas de texto () {
const pistas = this.textTracks();
const remoteTracks = this.remoteTextTracks();
const handleAddTrack = (e) => pistas.addTrack(e.pista);
const handleRemoveTrack = (e) => pistas.removeTrack(pista electrónica);
remoteTracks.on('addtrack', handleAddTrack);
remoteTracks.on('removetrack', handleRemoveTrack);
esto.addWebVttScript_();
const actualizarPantalla = () => this.trigger('texttrackchange');
const textTracksChanges = () => {
actualizarPantalla();
para (sea i = 0; i < pistas.longitud; i++) {
const pista = pistas[i];
track.removeEventListener('cuechange', updateDisplay);
if (track.mode === 'mostrando') {
track.addEventListener('cuechange', updateDisplay);
}
}
};
cambios de pistas de texto ();
tracks.addEventListener('cambio', textTracksChanges);
tracks.addEventListener('addtrack', textTracksChanges);
pistas.addEventListener('removetrack', textTracksChanges);
this.on('dispose', function() {
remoteTracks.off('addtrack', handleAddTrack);
remoteTracks.off('removetrack', handleRemoveTrack);
tracks.removeEventListener('cambio', textTracksChanges);
tracks.removeEventListener('addtrack', textTracksChanges);
tracks.removeEventListener('removetrack', textTracksChanges);
para (sea i = 0; i < pistas.longitud; i++) {
const pista = pistas[i];
track.removeEventListener('cuechange', updateDisplay);
}
});
}
/**
* Crea y devuelve un objeto {@link TextTrack} remoto.
*
* @param {cadena} tipo
* Tipo `TextTrack` (subtítulos, leyendas, descripciones, capítulos o metadatos)
*
* @param {cadena} [etiqueta]
* Etiqueta para identificar la pista de texto
*
* @param {cadena} [idioma]
* Abreviatura de idioma de dos letras
*
* @return {Pista de texto}
* El TextTrack que se crea.
* /
addTextTrack(tipo, etiqueta, idioma) {
si (! tipo) {
throw new Error('Se requiere el tipo TextTrack pero no se proporcionó');
}
return createTrackHelper(this, kind, label, language);
}
/**
* Cree un TextTrack emulado para que lo use addRemoteTextTrack
*
* Esto está destinado a ser anulado por las clases que heredan de
* Tecnología para crear TextTracks nativos o personalizados.
*
* @param {Objeto} opciones
* El objeto debe contener las opciones para inicializar TextTrack.
*
* @param {cadena} [opciones.tipo]
* Tipo `TextTrack` (subtítulos, leyendas, descripciones, capítulos o metadatos).
*
* @param {cadena} [opciones.etiqueta].
* Etiqueta para identificar la pista de texto
*
* @param {cadena} [opciones.idioma]
* Abreviatura del idioma de dos letras.
*
* @return {HTMLTrackElement}
* El elemento de pista que se crea.
* /
createRemoteTextTrack(opciones) {
const track = mergeOptions(opciones, {
tecnología: esto
});
devuelve nuevos TRACK_TYPES.REMOTE.remoteTextEl.TrackClass(pista);
}
/**
* Crea un objeto de seguimiento de texto remoto y devuelve un elemento de seguimiento html.
*
* > Nota: Puede ser un {@link HTMLTrackElement} emulado o uno nativo.
*
* @param {Objeto} opciones
* Consulte {@link Tech#createRemoteTextTrack} para obtener propiedades más detalladas.
*
* @param {booleano} [limpieza manual=verdadero]
* - Cuando es falso: TextTrack se eliminará automáticamente del video
* elemento cada vez que cambia la fuente
* - Cuando es cierto: El TextTrack tendrá que limpiarse manualmente
*
* @return {HTMLTrackElement}
* Un elemento de pista Html.
*
* @deprecated La funcionalidad predeterminada para esta función será equivalente
* a "manualCleanup=false" en el futuro. El parámetro manualCleanup
* también ser eliminado.
* /
addRemoteTextTrack(opciones = {}, limpiezamanual) {
const htmlTrackElement = this.createRemoteTextTrack(opciones);
if (limpieza manual! == verdadero && limpieza manual !== falso) {
// advertencia de obsolescencia
log.warn('Llamar a addRemoteTextTrack sin establecer explícitamente el parámetro "manualCleanup" en `true` está en desuso y está predeterminado en `false` en futuras versiones de video.js');
limpieza manual = verdadero;
}
// almacenar HTMLTrackElement y TextTrack en la lista remota
this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
this.remoteTextTracks().addTrack(htmlTrackElement.track);
if (limpieza manual! == verdadero) {
// crea la TextTrackList si no existe
esto.listo(() => this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track));
}
devolver htmlTrackElement;
}
/**
* Elimina una pista de texto remota de la `TextTrackList` remota.
*
* @param {TextTrack} pista
* `TextTrack` para eliminar de `TextTrackList`
* /
removeRemoteTextTrack(pista) {
const trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(pista);
// eliminar HTMLTrackElement y TextTrack de la lista remota
this.remoteTextTrackEls().removeTrackElement_(trackElement);
this.remoteTextTracks().removeTrack(pista);
this.autoRemoteTextTracks_.removeTrack(pista);
}
/**
* Obtiene métricas de calidad de reproducción de medios disponibles según lo especificado por los medios de W3C
* API de calidad de reproducción.
*
* @ver [Especificaciones]{@enlace https://wicg.github.io/media-playback-quality}
*
* @return {Objeto}
* Un objeto con métricas de calidad de reproducción multimedia compatibles
*
* @abstracto
* /
getVideoPlaybackQuality() {
devolver {};
}
/**
* Intente crear una ventana de video flotante siempre encima de otras ventanas
* para que los usuarios puedan seguir consumiendo medios mientras interactúan con otros
* sitios de contenido o aplicaciones en su dispositivo.
*
* @ver [Especificación]{@enlace https://wicg.github.io/picture-in-picture}
*
* @return {Promesa|indefinido}
* Una promesa con una ventana Picture-in-Picture si el navegador es compatible
* Promesas (o se pasó una como opción). Devuelve indefinido
* de lo contrario.
*
* @abstracto
* /
solicitudImagenEnImagen() {
const PromiseClass = this.options_.Promise || ventana.Promesa;
si (PromesaClase) {
devolver PromiseClass.reject();
}
}
/**
* Un método para verificar el valor de 'disablePictureInPicture' < video> propiedad.
* El valor predeterminado es verdadero, ya que debe considerarse deshabilitado si la tecnología no es compatible con pip
*
* @abstracto
* /
deshabilitarImagenEnImagen() {
devolver verdadero;
}
/**
* Un método para configurar o desactivar 'disablePictureInPicture' < video> propiedad.
*
* @abstracto
* /
setDisablePictureInPicture() {}
/**
* Una implementación alternativa de requestVideoFrameCallback usando requestAnimationFrame
*
* @param {función} cb
* ID de solicitud de @return {número}
* /
solicitudVideoFrameCallback(cb) {
const id = Guid.nuevoGUID();
if (!this.isReady_ || this.paused()) {
this.queuedHanders_.add(id);
this.one('jugando', () => {
if (this.queuedHanders_.has(id)) {
this.queuedHanders_.delete(id);
cb();
}
});
} else {
this.requestNamedAnimationFrame(id, cb);
}
identificación de retorno;
}
/**
* Una implementación alternativa de cancelVideoFrameCallback
*
* @param {number} id id de la devolución de llamada que se cancelará
* /
cancelarVideoFrameCallback(id) {
if (this.queuedHanders_.has(id)) {
this.queuedHanders_.delete(id);
} else {
this.cancelNamedAnimationFrame(id);
}
}
/**
* Un método para configurar un cartel de un `Tech`.
*
* @abstracto
* /
establecerPoster() {}
/**
* Un método para verificar la presencia de 'playsinline' < video> atributo.
*
* @abstracto
* /
juegosenlinea() {}
/**
* Un método para configurar o desactivar el 'playsinline' < video> atributo.
*
* @abstracto
* /
setPlaysinline() {}
/**
* Intento de forzar la anulación de las pistas de audio nativas.
*
* @param {boolean} anular: si se establece en verdadero audio nativo, se anulará,
* de lo contrario, se utilizará potencialmente el audio nativo.
*
* @abstracto
* /
anular las pistas de audio nativas () {}
/**
* Intento de forzar la anulación de pistas de video nativas.
*
* @param {boolean} anular: si se establece en verdadero video nativo, se anulará,
* de lo contrario, se utilizará potencialmente el video nativo.
*
* @abstracto
* /
anular las pistas de vídeo nativas () {}
/*
* Verifique si la tecnología puede admitir el tipo de mimo dado.
*
* La tecnología base no admite ningún tipo, pero los controladores de origen pueden
* sobrescribir esto.
*
* @param {cadena} tipo
* El tipo MIME para comprobar si hay soporte.
*
* @return {cadena}
* 'probablemente', 'quizás' o cadena vacía
*
* @ver [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
*
* @abstracto
* /
puedePlayType() {
devolver '';
}
/**
* Compruebe si el tipo es compatible con esta tecnología.
*
* La tecnología base no admite ningún tipo, pero los controladores de origen pueden
* sobrescribir esto.
*
* @param {cadena} tipo
* El tipo de medio para comprobar
* @return {string} Devuelve la respuesta del elemento de video nativo
* /
canPlayType estático () {
devolver '';
}
/**
* Verifique si la tecnología puede admitir la fuente dada
*
* @param {Objeto} srcObj
* El objeto fuente
* @param {Objeto} opciones
* Las opciones pasan a la tecnología
* @return {cadena} 'probablemente', 'tal vez' o '' (cadena vacía)
* /
canPlaySource estático (srcObj, opciones) {
devuelve Tech.canPlayType(srcObj.type);
}
/*
* Devuelve si el argumento es un Tech o no.
* Se puede pasar una clase como `Html5` o una instancia como `player.tech_`
*
* @param {Objeto} componente
* El artículo a comprobar
*
* @return {booleano}
* Si es una tecnología o no
* - Cierto si es una tecnología
* - Falso si no lo es
* /
isTech estático (componente) {
volver componente.prototipo instancia de Tech ||
instancia de componente de tecnología ||
componente === Tecnología;
}
/**
* Registra un `Tech` en una lista compartida para videojs.
*
* @param {cadena} nombre
* Nombre del `Tech` a registrar.
*
* @param {Objeto} tecnología
* La clase `Tech` para registrarse.
* /
static registerTech(nombre, tecnología) {
if (!Tech.techs_) {
Tech.techs_ = {};
}
if (!Tecnología.esTecnología(tecnología)) {
throw new Error(`Tech ${name} debe ser un Tech`);
}
if (!Tech.canPlayType) {
throw new Error('Los técnicos deben tener un método canPlayType estático');
}
si (!Tech.canPlaySource) {
throw new Error('Los técnicos deben tener un método canPlaySource estático');
}
nombre = toTitleCase(nombre);
Tech.techs_[nombre] = tecnología;
Tech.techs_[toLowerCase(nombre)] = tecnología;
if (nombre !== 'Tecnología') {
// camel case el techName para usar en techOrder
Tech.defaultTechOrder_.push(nombre);
}
tecnología de retorno;
}
/**
* Obtenga un 'Tecnólogo' de la lista compartida por nombre.
*
* @param {cadena} nombre
* `camelCase` o `TitleCase` nombre del Tech para obtener
*
* @return {Tecnología|indefinido}
* El `Tech` o indefinido si no hubo ningún tech con el nombre solicitado.
* /
getTech estático (nombre) {
si (! nombre) {
devolver;
}
si (Tecnología.técnicas_ && Tech.techs_[nombre]) {
return Tech.techs_[nombre];
}
nombre = toTitleCase(nombre);
si (ventana && ventana.videojs && ventana.videojs[nombre]) {
log.warn(`La tecnología ${name} se agregó al objeto videojs cuando debería registrarse usando videojs.registerTech(name, tech)`);
volver ventana.videojs[nombre];
}
}
}
/**
* Obtener la {@link VideoTrackList}
*
* @returns {VideoTrackList}
* @método Tech.prototype.videoTracks
* /
/**
* Obtener la {@link AudioTrackList}
*
* @returns {Lista de pistas de audio}
* @método Tech.prototype.audioTracks
* /
/**
* Obtén la {@link TextTrackList}
*
* @devoluciones {TextTrackList}
* @método Tech.prototype.textTracks
* /
/**
* Obtener el elemento remoto {@link TextTrackList}
*
* @devoluciones {TextTrackList}
* @método Tech.prototype.remoteTextTracks
* /
/**
* Obtener el elemento remoto {@link HtmlTrackElementList}
*
* @devoluciones {HtmlTrackElementList}
* @método Tech.prototype.remoteTextTrackEls
* /
TRACK_TYPES.ALL.names.forEach(función(nombre) {
const props = TRACK_TYPES.ALL[nombre];
Tech.prototype[props.getterName] = función() {
this[props.privateName] = this[props.privateName] || nuevos accesorios.ListClass();
devolver este [props.privateName];
};
});
/**
* Lista de pistas de texto asociadas
*
* @escriba {TextTrackList}
* @privado
* @property Tech#textTracks_
* /
/**
* Lista de pistas de audio asociadas.
*
* @escriba {Lista de pistas de audio}
* @privado
* @property Tech#audioTracks_
* /
/**
* Lista de pistas de video asociadas.
*
* @escriba {Lista de pistas de video}
* @privado
* @property Tech#videoTracks_
* /
/**
* Booleano que indica si `Tech` admite control de volumen.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresVolumeControl = verdadero;
/**
* Booleano que indica si `Tech` admite volumen de silenciamiento.
*
* @tipo {bolean}
* @por defecto
* /
Tech.prototype.featuresMuteControl = verdadero;
/**
* Booleano que indica si `Tech` admite el control de cambio de tamaño de pantalla completa.
* Cambiar el tamaño de los complementos mediante la solicitud de pantalla completa vuelve a cargar el complemento
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresFullscreenResize = false;
/**
* Booleano que indica si `Tech` admite cambiar la velocidad a la que se reproduce el video
* obras de teatro. Ejemplos:
* - Establecer jugador para jugar 2x (dos veces) tan rápido
* - Configure el jugador para que juegue 0.5x (la mitad) más rápido
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresPlaybackRate = falso;
/**
* Booleano que indica si `Tech` admite el evento `progress`. esto es actualmente
* no activado por video-js-swf. Esto se utilizará para determinar si
* Se debe llamar a {@link Tech#manualProgressOn}.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresProgressEvents = falso;
/**
* Booleano que indica si `Tech` admite el evento `sourceset`.
*
* Un técnico debe establecer esto en "verdadero" y luego usar {@link Tech#triggerSourceset}
* para activar un {@link Tech#event:sourceset} lo antes posible después de obtener
* una nueva fuente.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresSourceset = false;
/**
* Booleano que indica si `Tech` admite el evento `timeupdate`. esto es actualmente
* no activado por video-js-swf. Esto se utilizará para determinar si
* Se debe llamar a {@link Tech#manualTimeUpdates}.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresTimeupdateEvents = falso;
/**
* Booleano que indica si `Tech` es compatible con `TextTrack`s nativos.
* Esto nos ayudará a integrarnos con `TextTrack`s nativos si el navegador los admite.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresNativeTextTracks = falso;
/**
* Booleano que indica si `Tech` admite `requestVideoFrameCallback`.
*
* @tipo {booleano}
* @por defecto
* /
Tech.prototype.featuresVideoFrameCallback = falso;
/**
* Un mixin funcional para técnicos que quieren usar el patrón Source Handler.
* Los controladores de origen son scripts para manejar formatos específicos.
* El patrón de controlador de origen se utiliza para formatos adaptables (HLS, DASH) que
* Cargue manualmente los datos de video y aliméntelos a un búfer de origen (extensiones de origen de medios)
* Ejemplo: `Tech.withSourceHandlers.call(MyTech);`
*
* @param {Tecnología} _Tecnología
* La tecnología para agregar funciones de controlador de fuente.
*
* @mixes Tech~SourceHandlerAdditions
* /
Tech.withSourceHandlers = function(_Tech) {
/**
* Registrar un controlador de fuente
*
* Controlador @param {Función}
* La clase de controlador de origen
*
* @param {número} [índice]
* Regístralo en el siguiente índice
* /
_Tech.registerSourceHandler = función (controlador, índice) {
dejar manejadores = _Tech.sourceHandlers;
si (! manejadores) {
manejadores = _Tech.sourceHandlers = [];
}
si (índice === indefinido) {
// añadir al final de la lista
índice = manejadores.longitud;
}
manejadores.empalme(índice, 0, manejador);
};
/**
* Verifique si la tecnología puede admitir el tipo dado. También comprueba la
* Técnicos sourceHandlers.
*
* @param {cadena} tipo
* El tipo MIME a comprobar.
*
* @return {cadena}
* 'probablemente', 'tal vez' o '' (cadena vacía)
* /
_Tech.canPlayType = función (tipo) {
manejadores const = _Tech.sourceHandlers || [];
dejar puede;
para (sea i = 0; i < manejadores.longitud; i++) {
can = handlers[i].canPlayType(tipo);
si puede) {
lata de retorno;
}
}
devolver '';
};
/**
* Devuelve el primer controlador de fuente que admite la fuente.
*
* HACER: Responda a la pregunta: ¿debería priorizarse 'probablemente' sobre 'quizás'?
*
* @param {Tech~SourceObject} fuente
* El objeto fuente
*
* @param {Objeto} opciones
* Las opciones pasan a la tecnología
*
* @return {SourceHandler|null}
* El primer controlador de fuente que admite la fuente o nulo si
* ningún SourceHandler admite la fuente
* /
_Tech.selectSourceHandler = función (fuente, opciones) {
manejadores const = _Tech.sourceHandlers || [];
dejar puede;
para (sea i = 0; i < manejadores.longitud; i++) {
can = handlers[i].canHandleSource(fuente, opciones);
si puede) {
manejadores de devolución [i];
}
}
devolver nulo;
};
/**
* Compruebe si la tecnología puede admitir la fuente dada.
*
* @param {Tech~SourceObject} srcObj
* El objeto fuente
*
* @param {Objeto} opciones
* Las opciones pasan a la tecnología
*
* @return {cadena}
* 'probablemente', 'tal vez' o '' (cadena vacía)
* /
_Tech.canPlaySource = function(srcObj, opciones) {
const sh = _Tech.selectSourceHandler(srcObj, opciones);
si (sh) {
volver sh.canHandleSource(srcObj, opciones);
}
devolver '';
};
/**
* Cuando utilice un controlador de código fuente, prefiera su implementación de
* cualquier función proporcionada normalmente por la tecnología.
* /
const diferible = [
'buscable',
'buscando',
'duración'
];
/**
* Un contenedor alrededor de {@link Tech#seekable} que llamará a un `SourceHandler`s seekable
* función si existe, con un respaldo a la función de búsqueda Techs.
*
* @método _Tech.buscable
* /
/**
* Un contenedor alrededor de {@link Tech#duration} que llamará a la duración de un 'SourceHandler'
* función si existe, de lo contrario, recurrirá a la función de duración de tecnología.
*
* @método _Tecnología.duración
* /
diferible.forEach(function(fnName) {
const originalFn = this[fnName];
if (tipo de originalFn !== 'función') {
devolver;
}
this[fnName] = función () {
si (este.sourceHandler_ && this.sourceHandler_[fnName]) {
devuelve this.sourceHandler_[fnName].apply(this.sourceHandler_, argumentos);
}
return originalFn.apply(esto, argumentos);
};
}, _Tecnología.prototipo);
/**
* Crear una función para configurar la fuente usando un objeto fuente
* y controladores de origen.
* Nunca debe llamarse a menos que se encuentre un controlador de origen.
*
* @param {Tech~SourceObject} fuente
* Un objeto fuente con src y claves de tipo
* /
_Tech.prototype.setSource = función (fuente) {
let sh = _Tech.selectSourceHandler(fuente, this.options_);
si (!sh) {
// Recurra a un manejador de fuente nativo cuando las fuentes no admitidas son
// establecer deliberadamente
si (_Tech.nativeSourceHandler) {
sh = _Tech.nativeSourceHandler;
} else {
log.error('No se encontró un controlador de fuente para la fuente actual.');
}
}
// Eliminar cualquier controlador de fuente existente
this.disposeSourceHandler();
this.off('dispose', this.disposeSourceHandler_);
if (sh !== _Tech.nativeSourceHandler) {
this.currentSource_ = fuente;
}
this.sourceHandler_ = sh.handleSource(source, this, this.options_);
this.one('dispose', this.disposeSourceHandler_);
};
/**
* Limpie cualquier SourceHandlers y oyentes existentes cuando se elimine el Tech.
*
* @escucha Tech#dispose
* /
_Tech.prototype.disposeSourceHandler = función () {
// si tenemos una fuente y obtenemos otra
// entonces estamos cargando algo nuevo
// que borrar todas nuestras pistas actuales
if (this.currentSource_) {
this.clearTracks(['audio', 'video']);
this.currentSource_ = null;
}
// siempre limpia las pistas de texto automático
this.cleanupAutoTextTracks();
si (este.sourceHandler_) {
si (este.sourceHandler_.dispose) {
este.sourceHandler_.dispose();
}
este.sourceHandler_ = nulo;
}
};
};
// La clase de tecnología base debe registrarse como un componente. Es el único
// Tecnología que se puede registrar como Componente.
Component.registerComponent('Tecnología', Tecnología);
Tecnología.registrarTecnología('Tecnología', Tecnología);
/**
* Una lista de tecnologías que deben agregarse a techOrder en Players
*
* @privado
* /
Tech.defaultTechOrder_ = [];
exportar tecnología predeterminada;