/**
* @file html5.js
*/
import Tech from './tech.js' ;
import * as Dom from '../utils/dom.js' ;
import * as Url from '../utils/url.js' ;
import log from '../utils/log.js' ;
import * as browser from '../utils/browser.js' ;
import document from 'global/document' ;
import window from 'global/window' ;
import {assign} from '../utils/obj' ;
import mergeOptions from '../utils/merge-options.js' ;
import {toTitleCase} de '../utils/string-cases.js' ;
import {NORMAL as TRACK_TYPES, REMOTE} from '../tracks/track-types' ;
import setupSourceset from './setup-sourceset' ;
import defineLazyProperty de '../utils/define-lazy-property.js' ;
import {silencePromesse} from '../utils/promesse' ;
/**
* Contrôleur HTML5 Media - Enveloppe pour l'API HTML5 Media
*
* @mixes Tech~SourceHandlerAdditions
* @extends Tech
*/
class Html5 extends Tech {
/**
* Créer une instance de ce Tech.
*
* @param {Objet} [options]
* La mémoire clé/valeur des options du lecteur.
*
* @param {Component~ReadyCallback} ready
* Fonction de rappel à appeler lorsque le Tech `HTML5` est prêt.
*/
constructor(options, ready) {
super(options, ready) ;
const source = options.source ;
let crossoriginTracks = false ;
this.featuresVideoFrameCallback = this.featuresVideoFrameCallback && this.el_.tagName === 'VIDEO' ;
// Définir la source si elle est fournie
// 1) Vérifier si la source est nouvelle (si ce n'est pas le cas, nous voulons conserver l'original pour que la lecture ne soit pas interrompue)
// 2) Vérifier si l'état du réseau de l'étiquette a échoué lors de l'initialisation, et si c'est le cas, réinitialiser la source
// de toute façon pour que l'erreur soit déclenchée.
if (source && (this.el_.currentSrc !== source.src || (options.tag && options.tag.initNetworkState_ === 3))) {
this.setSource(source) ;
} else {
this.handleLateInit_(this.el_) ;
}
// mise en place d'un jeu de sources après un jeu de sources tardif/init
if (options.enableSourceset) {
this.setupSourcesetHandling_() ;
}
this.isScrubbing_ = false ;
if (this.el_.hasChildNodes()) {
const nodes = this.el_.childNodes ;
let nodesLength = nodes.length ;
const removeNodes = [] ;
while (nodesLength--) {
const node = nodes[nodesLength] ;
const nodeName = node.nodeName.toLowerCase() ;
if (nodeName === 'track') {
if (!this.featuresNativeTextTracks) {
// Vider les pistes de balises vidéo afin que le lecteur intégré ne les utilise pas également.
// Cela peut ne pas être assez rapide pour empêcher les navigateurs HTML5 de lire les balises
// nous devrons donc désactiver toutes les pistes par défaut si nous le faisons manuellement
// sous-titres et légendes. videoElement.textTracks
removeNodes.push(node) ;
} else {
// enregistrer HTMLTrackElement et TextTrack dans la liste distante
this.remoteTextTrackEls().addTrackElement_(node) ;
this.remoteTextTracks().addTrack(node.track) ;
this.textTracks().addTrack(node.track) ;
if (!crossoriginTracks &&
!this.el_.hasAttribute('crossorigin') &&
Url.isCrossOrigin(node.src)) {
crossoriginTracks = true ;
}
}
}
}
for (let i = 0 ; i < removeNodes.length ; i++) {
this.el_.removeChild(removeNodes[i]) ;
}
}
this.proxyNativeTracks_() ;
if (this.featuresNativeTextTracks && crossoriginTracks) {
log.warn('Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.\N' +
ceci peut empêcher le chargement des pistes de texte") ;
}
// empêcher Safari iOS de désactiver les pistes de texte de métadonnées pendant la lecture native
this.restoreMetadataTracksInIOSNativePlayer_() ;
// Déterminer si les contrôles natifs doivent être utilisés
// Notre objectif devrait être d'obtenir les contrôles personnalisés sur les mobiles solides partout
// afin que nous puissions le supprimer. Pour l'instant, cela bloque la personnalisation
// contrôles sur les ordinateurs portables tactiles comme le Chrome Pixel
if ((browser.TOUCH_ENABLED || browser.IS_IPHONE ||
browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
this.setControls(true) ;
}
// sur iOS, nous voulons proxyer `webkitbeginfullscreen` et `webkitendfullscreen`
// dans un événement `fullscreenchange` (changement d'écran)
this.proxyWebkitFullscreen_() ;
this.triggerReady() ;
}
/**
* Dispose de l'élément média `HTML5` et supprime toutes les pistes.
*/
dispose() {
if (this.el_ && this.el_.resetSourceset_) {
this.el_.resetSourceset_() ;
}
Html5.disposeMediaElement(this.el_) ;
this.options_ = null ;
// tech s'occupera de l'effacement de la liste des pistes émulées
super.dispose() ;
}
/**
* Modifiez l'élément media de manière à pouvoir détecter le moment où
* la source est modifiée. Déclenche `sourceset` juste après que la source ait changé
*/
setupSourcesetHandling_() {
setupSourceset(this) ;
}
/**
* Lorsqu'une piste de sous-titres est activée dans le lecteur natif Safari d'iOS, toutes les autres pistes de sous-titres sont désactivées
* sont désactivées (y compris les pistes de métadonnées), ce qui annule toutes leurs pistes de
* les points de repère associés. Cette opération rétablit les pistes de métadonnées dans l'état où elles se trouvaient avant le plein écran
* dans ces cas-là, afin de ne pas perdre inutilement des points de repère.
*
* @private
*/
restoreMetadataTracksInIOSNativePlayer_() {
const textTracks = this.textTracks() ;
let metadataTracksPreFullscreenState ;
// capture un instantané de l'état actuel de chaque piste de métadonnées
const takeMetadataTrackSnapshot = () => {
metadataTracksPreFullscreenState = [] ;
for (let i = 0 ; i < textTracks.length ; i++) {
const track = textTracks[i] ;
if (track.kind === 'metadata') {
metadataTracksPreFullscreenState.push({
la voie ferrée,
storedMode : track.mode
}) ;
}
}
};
// prendre un instantané de l'état initial de chaque piste de métadonnées et mettre à jour l'instantané
// à chaque fois qu'il y a un événement de "changement" de piste
takeMetadataTrackSnapshot() ;
textTracks.addEventListener('change', takeMetadataTrackSnapshot) ;
this.on('dispose', () => textTracks.removeEventListener('change', takeMetadataTrackSnapshot)) ;
const restoreTrackMode = () => {
for (let i = 0 ; i < metadataTracksPreFullscreenState.length ; i++) {
const storedTrack = metadataTracksPreFullscreenState[i] ;
if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
storedTrack.track.mode = storedTrack.storedMode ;
}
}
// nous voulons que ce gestionnaire soit exécuté uniquement lors du premier événement "change"
textTracks.removeEventListener('change', restoreTrackMode) ;
};
// lorsque nous passons en lecture plein écran, nous arrêtons de mettre à jour l'instantané et nous nous concentrons sur les éléments suivants
// rétablir tous les modes de piste dans leur état d'avant le plein écran
this.on('webkitbeginfullscreen', () => {
textTracks.removeEventListener('change', takeMetadataTrackSnapshot) ;
// supprimer l'écouteur avant de l'ajouter, au cas où il n'aurait pas été supprimé auparavant
textTracks.removeEventListener('change', restoreTrackMode) ;
textTracks.addEventListener('change', restoreTrackMode) ;
}) ;
// recommencer à mettre à jour l'instantané après avoir quitté le plein écran
this.on('webkitendfullscreen', () => {
// supprimer l'écouteur avant de l'ajouter, au cas où il n'aurait pas été supprimé auparavant
textTracks.removeEventListener('change', takeMetadataTrackSnapshot) ;
textTracks.addEventListener('change', takeMetadataTrackSnapshot) ;
// supprimer le gestionnaire restoreTrackMode au cas où il n'aurait pas été déclenché pendant la lecture en plein écran
textTracks.removeEventListener('change', restoreTrackMode) ;
}) ;
}
/**
* Tentative de forcer le remplacement des pistes pour le type donné
*
* @param {string} type - Type de piste à remplacer, les valeurs possibles étant notamment "Audio",
* vidéo" et "Texte".
* @param {boolean} override - Si la valeur est fixée à true, les données audio/vidéo natives seront remplacées,
* sinon, le son/vidéo natif sera potentiellement utilisé.
* @private
*/
overrideNative_(type, override) {
// S'il n'y a pas de changement de comportement, n'ajoutez/supprimez pas d'écouteurs
if (override !== this[`featuresNative${type}Tracks`]) {
retour ;
}
const lowerCaseType = type.toLowerCase() ;
if (this[`${lowerCaseType}TracksListeners_`]) {
Object.keys(this[`${lowerCaseType}TracksListeners_`]).forEach((eventName) => {
const elTracks = this.el()[`${lowerCaseType}Tracks`] ;
elTracks.removeEventListener(eventName, this[`${lowerCaseType}TracksListeners_`][eventName]) ;
}) ;
}
this[`featuresNative${type}Tracks`] = !override ;
this[`${lowerCaseType}TracksListeners_`] = null ;
this.proxyNativeTracksForType_(lowerCaseType) ;
}
/**
* Tentative de forcer le remplacement des pistes audio natives.
*
* @param {boolean} override - Si la valeur est fixée à true, l'audio natif sera remplacé,
* sinon le son natif sera potentiellement utilisé.
*/
overrideNativeAudioTracks(override) {
this.overrideNative_('Audio', override) ;
}
/**
* Tentative de forcer le remplacement des pistes vidéo natives.
*
* @param {boolean} override - Si la valeur est fixée à true, la vidéo native sera remplacée,
* dans le cas contraire, la vidéo native sera potentiellement utilisée.
*/
overrideNativeVideoTracks(override) {
this.overrideNative_('Video', override) ;
}
/**
* Proxy des événements natifs de la liste des pistes pour le type donné vers notre piste
* si le navigateur dans lequel nous jouons prend en charge ce type de liste de pistes.
*
* @param {string} name - Type de piste ; les valeurs incluent "audio", "vidéo" et "texte"
* @private
*/
proxyNativeTracksForType_(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) {
retour ;
}
const listeners = {
changement : (e) => {
const event = {
type : "change",
cible : techTracks,
cible actuelle : techTracks,
srcElement : techTracks
};
techTracks.trigger(event) ;
// s'il s'agit d'un événement de changement de piste de texte, nous devons également notifier l'événement de changement de piste de texte
// liste des pistes de texte à distance. Cela peut potentiellement provoquer un faux positif
// si nous recevions un événement de changement sur une piste non distante et que
// nous avons déclenché l'événement sur la liste des textes distants, qui n'est pas
// contiennent cette piste. Cependant, les meilleures pratiques impliquent que l'on passe en boucle par le
// liste des pistes et recherche de la valeur de mode appropriée, donc,
// cela ne devrait pas poser de problème
if (name === 'text') {
this[REMOTE.remoteText.getterName]().trigger(event) ;
}
},
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]) {
trouvé = vrai ;
pause ;
}
}
if (!found) {
removeTracks.push(techTracks[i]) ;
}
}
while (removeTracks.length) {
techTracks.removeTrack(removeTracks.shift()) ;
}
};
this[props.getterName + 'Listeners_'] = listeners ;
Object.keys(listeners).forEach((eventName) => {
const listener = listeners[eventName] ;
elTracks.addEventListener(eventName, listener) ;
this.on('dispose', (e) => elTracks.removeEventListener(eventName, listener)) ;
}) ;
// Supprimer les pistes (natives) qui ne sont plus utilisées
this.on('loadstart', removeOldTracks) ;
this.on('dispose', (e) => this.off('loadstart', removeOldTracks)) ;
}
/**
* Proxy de tous les événements natifs de la liste des pistes vers nos listes de pistes si le navigateur dans lequel nous jouons
* prend en charge ce type de liste de pistes.
*
* @private
*/
proxyNativeTracks_() {
TRACK_TYPES.names.forEach((name) => {
this.proxyNativeTracksForType_(name) ;
}) ;
}
/**
* Créer l'élément DOM de la Tech `Html5`.
*
* @return {Element}
* L'élément qui est créé.
*/
createEl() {
let el = this.options_.tag ;
// Vérifier si ce navigateur prend en charge le déplacement de l'élément dans la boîte.
// Sur l'iPhone, la vidéo s'interrompra si vous déplacez l'élément,
// Nous devons donc créer un tout nouvel élément.
// Si nous avons intégré la div player, il n'est pas nécessaire de déplacer l'élément media.
if (!el ||
!(this.options_.playerElIngest ||
this.movingMediaElementInDOM)) {
// Si la balise originale est toujours présente, la cloner et la supprimer.
if (el) {
const clone = el.cloneNode(true) ;
if (el.parentNode) {
el.parentNode.insertBefore(clone, el) ;
}
Html5.disposeMediaElement(el) ;
el = clone ;
} else {
el = document.createElement('video') ;
// déterminer si les contrôles natifs doivent être utilisés
const tagAttributes = this.options_.tag && Dom.getAttributes(this.options_.tag) ;
const attributes = mergeOptions({}, tagAttributes) ;
if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
supprimer les attributs.controls ;
}
Dom.setAttributes(
el,
assign(attributes, {
id : this.options_.techId,
classe : 'vjs-tech'
})
) ;
}
el.playerId = this.options_.playerId ;
}
if (typeof this.options_.preload !== 'undefined') {
Dom.setAttribute(el, 'preload', this.options_.preload) ;
}
if (this.options_.disablePictureInPicture !== undefined) {
el.disablePictureInPicture = this.options_.disablePictureInPicture ;
}
// Mise à jour des paramètres spécifiques de la balise, au cas où ils auraient été remplacés
// `autoplay` doit être *dernier* pour que `muted` et `playsinline` soient présents
// lorsque iOS/Safari ou d'autres navigateurs tentent d'effectuer une lecture automatique.
const settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'] ;
for (let i = 0 ; i < settingsAttrs.length ; i++) {
const attr = settingsAttrs[i] ;
const value = this.options_[attr] ;
if (typeof value !== 'undefined') {
if (value) {
Dom.setAttribute(el, attr, attr) ;
} else {
Dom.removeAttribute(el, attr) ;
}
el[attr] = valeur ;
}
}
return el ;
}
/**
* Ceci sera déclenché si l'événement loadstart s'est déjà déclenché, avant que videojs ne soit
* prêt. Voici deux exemples connus de cas où cela peut se produire :
* 1. Si nous chargeons l'objet de lecture après le chargement
* 2. Le média est déjà en train de lire (souvent avec la lecture automatique) puis
*
* Cette fonction déclenchera un autre chargement pour que videojs puisse rattraper son retard.
*
* @fires Tech#loadstart
*
* @return {undefined}
* ne renvoie rien.
*/
handleLateInit_(el) {
if (el.networkState === 0 || el.networkState === 3) {
// L'élément vidéo n'a pas encore commencé à charger la source
// ou n'a pas trouvé de source
retour ;
}
if (el.readyState === 0) {
// L'état du réseau est défini de manière synchrone MAIS loadstart est déclenché au moment de l'activation de l'état du réseau
// fin de la pile actuelle, généralement avant setInterval(fn, 0).
// A ce stade, nous savons que loadstart s'est peut-être déjà déclenché ou qu'il est en cours d'exécution
// sur le point de se déclencher, et dans tous les cas, le joueur ne l'a pas encore vu.
// Nous ne voulons pas déclencher prématurément le démarrage de la charge et provoquer un
// double loadstart donc nous attendons de voir si cela se produit entre-temps
// et la boucle suivante, et la déclenche si ce n'est pas le cas.
// CEPENDANT, nous voulons aussi nous assurer qu'il se déclenche avant loadedmetadata
// ce qui pourrait également se produire entre maintenant et la prochaine boucle, nous allons donc
// à surveiller également.
let loadstartFired = false ;
const setLoadstartFired = function() {
loadstartFired = true ;
};
this.on('loadstart', setLoadstartFired) ;
const triggerLoadstart = function() {
// Nous avons manqué le démarrage de la charge d'origine. Assurez-vous que le lecteur
// voit loadstart avant loadedmetadata
if (!loadstartFired) {
this.trigger('loadstart') ;
}
};
this.on('loadedmetadata', triggerLoadstart) ;
this.ready(function() {
this.off('loadstart', setLoadstartFired) ;
this.off('loadedmetadata', triggerLoadstart) ;
if (!loadstartFired) {
// Le démarrage natif de la charge nous a manqué. Tirez tout de suite.
this.trigger('loadstart') ;
}
}) ;
retour ;
}
// A partir de là, nous savons que loadstart s'est déjà déclenché et que nous l'avons manqué.
// Les autres événements readyState ne posent pas autant de problèmes si nous doublons les événements readyState avec les événements readyState
//, et ne se donnera donc pas autant de mal que loadstart pour empêcher les
// que si nous ne trouvons pas de raison de le faire.
const eventsToTrigger = ['loadstart'] ;
// loadedmetadata : nouvellement égal à HAVE_METADATA (1) ou supérieur
eventsToTrigger.push('loadedmetadata') ;
// données chargées : nouvellement augmentées à HAVE_CURRENT_DATA (2) ou plus
if (el.readyState >= 2) {
eventsToTrigger.push('loadeddata') ;
}
// canplay : nouvellement augmenté à HAVE_FUTURE_DATA (3) ou supérieur
if (el.readyState >= 3) {
eventsToTrigger.push('canplay') ;
}
// canplaythrough : nouvellement égal à HAVE_ENOUGH_DATA (4)
if (el.readyState >= 4) {
eventsToTrigger.push('canplaythrough') ;
}
// Nous devons encore donner au joueur le temps d'ajouter des récepteurs d'événements
this.ready(function() {
eventsToTrigger.forEach(function(type) {
this.trigger(type) ;
}, this) ;
}) ;
}
/**
* Permet de déterminer si l'on procède à un nettoyage ou non.
* Ceci est utilisé pour décider si nous devons utiliser `fastSeek` ou non.
* `fastSeek` est utilisé pour fournir un jeu d'astuces sur les navigateurs Safari.
*
* @param {boolean} isScrubbing
* - vrai pour nous sommes en train de nettoyer
* - faux car nous ne frottons plus
*/
setScrubbing(isScrubbing) {
this.isScrubbing_ = isScrubbing ;
}
/**
* Obtenir que l'on frotte ou non.
*
* @return {boolean} isScrubbing
* - vrai pour nous sommes en train de nettoyer
* - faux car nous ne frottons plus
*/
scrubbing() {
return this.isScrubbing_ ;
}
/**
* Fixe l'heure actuelle pour la technologie `HTML5`.
*
* @param {number} seconds
* Régler l'heure actuelle du média sur cette valeur.
*/
setCurrentTime(seconds) {
essayez {
if (this.isScrubbing_ && this.el_.fastSeek && browser.IS_ANY_SAFARI) {
this.el_.fastSeek(seconds) ;
} else {
this.el_.currentTime = seconds ;
}
} catch (e) {
log(e, 'La vidéo n'est pas prête. (Video.js)') ;
// this.warning(VideoJS.warnings.videoNotReady) ;
}
}
/**
* Obtenir la durée actuelle de l'élément multimédia HTML5.
*
* @return {number}
* La durée du média ou 0 s'il n'y a pas de durée.
*/
duration() {
// Android Chrome indiquera la durée comme étant l'infini pour la VOD HLS jusqu'à ce qu'elle soit terminée
// la lecture a commencé, ce qui déclenche par erreur l'affichage en direct.
// Retourne NaN si la lecture n'a pas commencé et déclenche une mise à jour de la durée une fois
// la durée peut être connue de manière fiable.
si (
this.el_.duration === Infinity &&
navigateur.IS_ANDROID &&
navigateur.IS_CHROME &&
this.el_.currentTime === 0
) {
// Attendez la première `timeupdate` avec currentTime > 0 - il peut y avoir des `timeupdate`
// plusieurs avec 0
const checkProgress = () => {
if (this.el_.currentTime > 0) {
// Déclenchement d'un changement de durée pour une véritable vidéo en direct
if (this.el_.duration === Infinity) {
this.trigger('durationchange') ;
}
this.off('timeupdate', checkProgress) ;
}
};
this.on('timeupdate', checkProgress) ;
renvoie NaN ;
}
return this.el_.duration || NaN ;
}
/**
* Obtenir la largeur actuelle de l'élément HTML5 media.
*
* @return {number}
* La largeur de l'élément HTML5 media.
*/
width() {
return this.el_.offsetWidth ;
}
/**
* Obtenir la hauteur actuelle de l'élément HTML5 media.
*
* @return {number}
* La hauteur de l'élément HTML5 media.
*/
height() {
return this.el_.offsetHeight ;
}
/**
* Proxy iOS `webkitbeginfullscreen` et `webkitendfullscreen` en
* événement `fullscreenchange`.
*
* @private
* @fires fullscreenchange
* @listens webkitendfullscreen
* @listens webkitbeginfullscreen
* @listens webkitbeginfullscreen
*/
proxyWebkitFullscreen_() {
if ( !('webkitDisplayingFullscreen' in this.el_)) {
retour ;
}
const endFn = function() {
this.trigger('fullscreenchange', { isFullscreen : false }) ;
// Safari définit parfois des contols sur l'élément vidéo lorsqu'il existe en plein écran.
if (this.el_.controls && !this.options_.nativeControlsForTouch && this.controls()) {
this.el_.controls = false ;
}
};
const beginFn = function() {
if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
this.one('webkitendfullscreen', endFn) ;
this.trigger('fullscreenchange', {
isFullscreen : true,
// mettre un drapeau au cas où un autre technicien déclencherait un changement d'écran complet
nativeIOSFullscreen : true
}) ;
}
};
this.on('webkitbeginfullscreen', beginFn) ;
this.on('dispose', () => {
this.off('webkitbeginfullscreen', beginFn) ;
this.off('webkitendfullscreen', endFn) ;
}) ;
}
/**
* Vérifier si le plein écran est pris en charge par l'appareil de lecture actuel.
*
* @return {boolean}
* - Vrai si le plein écran est pris en charge.
* - Faux si le plein écran n'est pas pris en charge.
*/
supportsFullScreen() {
if (typeof this.el_.webkitEnterFullScreen === 'function') {
const userAgent = window.navigator && window.navigator.userAgent || '' ;
// Semble être cassé dans Chromium/Chrome && Safari dans Leopard
if ((/Android/).test(userAgent) || !(/Chrome|Mac OS X 10.5/).test(userAgent)) {
retourner vrai ;
}
}
retourner faux ;
}
/**
* Demande que le Tech `HTML5` entre en plein écran.
*/
enterFullScreen() {
const video = this.el_ ;
if (video.paused && video.networkState <= video.HAVE_METADATA) {
// tentative d'amorcer l'élément vidéo pour un accès programmatique
// ce n'est pas nécessaire sur le bureau mais cela ne devrait pas nuire
silencePromesse(this.el_.play()) ;
// lecture et pause synchronisées pendant la transition vers le plein écran
// peut faire entrer les appareils iOS ~6.1 dans une boucle de lecture/pause
this.setTimeout(function() {
video.pause() ;
essayez {
video.webkitEnterFullScreen() ;
} catch (e) {
this.trigger('fullscreenerror', e) ;
}
}, 0) ;
} else {
essayez {
video.webkitEnterFullScreen() ;
} catch (e) {
this.trigger('fullscreenerror', e) ;
}
}
}
/**
* Demande que le Tech `HTML5` quitte le plein écran.
*/
exitFullScreen() {
if (!this.el_.webkitDisplayingFullscreen) {
this.trigger('fullscreenerror', new Error('La vidéo n'est pas en plein écran')) ;
retour ;
}
this.el_.webkitExitFullScreen() ;
}
/**
* Créer une fenêtre vidéo flottante toujours au-dessus des autres fenêtres afin que les utilisateurs puissent
* continuer à consommer des médias tout en interagissant avec d'autres sites de contenu, ou
* sur leur appareil.
*
* @see [Spec]{@link https://wicg.github.io/picture-in-picture}
*
* @return {Promesse}
* Une promesse avec une fenêtre d'image dans l'image.
*/
requestPictureInPicture() {
return this.el_.requestPictureInPicture() ;
}
/**
* RequestVideoFrameCallback native si elle est prise en charge par le navigateur/la technologie, ou fallback
* N'utilisez pas rVCF sur Safari lorsque le DRM est en cours de lecture, car il ne se déclenche pas
* Doit être vérifié après le constructeur
* Il s'agira d'un faux positif pour les sources claires chargées après une source Fairplay
*
* @param {fonction} fonction cb à appeler
* @return {number} id of request
*/
requestVideoFrameCallback(cb) {
if (this.featuresVideoFrameCallback && !this.el_.webkitKeys) {
return this.el_.requestVideoFrameCallback(cb) ;
}
return super.requestVideoFrameCallback(cb) ;
}
/**
* Native ou fallback requestVideoFrameCallback
*
* @param {number} id id de la demande à annuler
*/
cancelVideoFrameCallback(id) {
if (this.featuresVideoFrameCallback && !this.el_.webkitKeys) {
this.el_.cancelVideoFrameCallback(id) ;
} else {
super.cancelVideoFrameCallback(id) ;
}
}
/**
* Un getter/setter pour l'objet source de `Html5` Tech.
* > Remarque : Veuillez utiliser {@link Html5#setSource}
*
* @param {Tech~SourceObject} [src]
* L'objet source que vous voulez placer sur l'élément techs `HTML5`.
*
* @return {Tech~SourceObject|undefined}
* - L'objet source actuel lorsqu'aucune source n'est transmise.
* - indéfinie lors de la mise en place
*
* @deprecated Depuis la version 5.
*/
src(src) {
if (src === undefined) {
return this.el_.src ;
}
// Le réglage de src par `src` au lieu de `setSrc` sera déprécié
this.setSrc(src) ;
}
/**
* Réinitialisez le technicien en supprimant toutes les sources et en appelant ensuite
* {@link Html5.resetMediaElement}.
*/
reset() {
Html5.resetMediaElement(this.el_) ;
}
/**
* Obtenir la source actuelle sur le Tech `HTML5`. Renvoie au retour de la source de
* l'élément HTML5 media.
*
* @return {Tech~SourceObject}
* L'objet source actuel de la technologie HTML5. Avec un repli sur la
* source d'éléments.
*/
currentSrc() {
if (this.currentSource_) {
return this.currentSource_.src ;
}
return this.el_.currentSrc ;
}
/**
* Définir l'attribut de contrôle pour l'élément HTML5 media.
*
* @param {string} val
* Valeur à attribuer à l'attribut controls
*/
setControls(val) {
this.el_.controls = !!val ;
}
/**
* Crée et renvoie un objet {@link TextTrack} distant.
*
* @param {string} kind
* type de `TextTrack` (sous-titres, légendes, descriptions, chapitres ou métadonnées)
*
* @param {string} [label]
* Étiquette pour identifier la piste de texte
*
* @param {string} [langue]
* Abréviation linguistique à deux lettres
*
* @return {TextTrack}
* La piste de texte qui est créée.
*/
addTextTrack(kind, label, language) {
if (!this.featuresNativeTextTracks) {
return super.addTextTrack(kind, label, language) ;
}
return this.el_.addTextTrack(kind, label, language) ;
}
/**
* Crée soit un TextTrack natif, soit un TextTrack émulé dépendant de l'application
* sur la valeur de `featuresNativeTextTracks`
*
* @param {Objet} options
* L'objet doit contenir les options permettant d'initialiser la piste de texte.
*
* @param {string} [options.kind]
* type de `TextTrack` (sous-titres, légendes, descriptions, chapitres ou métadonnées).
*
* @param {string} [options.label]
* Étiquette pour identifier la piste de texte
*
* @param {string} [options.language]
* Abréviation linguistique de deux lettres.
*
* @param {boolean} [options.default]
* Cette piste est activée par défaut.
*
* @param {string} [options.id]
* L'identifiant interne à attribuer à cette piste.
*
* @param {string} [options.src]
* Une source url pour le titre.
*
* @return {HTMLTrackElement}
* L'élément de piste qui est créé.
*/
createRemoteTextTrack(options) {
if (!this.featuresNativeTextTracks) {
return super.createRemoteTextTrack(options) ;
}
const htmlTrackElement = document.createElement('track') ;
if (options.kind) {
htmlTrackElement.kind = options.kind ;
}
if (options.label) {
htmlTrackElement.label = options.label ;
}
if (options.language || options.srclang) {
htmlTrackElement.srclang = options.language || options.srclang ;
}
if (options.default) {
htmlTrackElement.default = options.default ;
}
if (options.id) {
htmlTrackElement.id = options.id ;
}
if (options.src) {
htmlTrackElement.src = options.src ;
}
return htmlTrackElement ;
}
/**
* Crée un objet de piste de texte distant et renvoie un élément de piste html.
*
* @param {Object} options L'objet doit contenir des valeurs pour
* type, langue, étiquette et src (emplacement du fichier WebVTT)
* @param {boolean} [manualCleanup=true] si la valeur est false, le TextTrack sera nettoyé à la main
* automatiquement supprimée de l'élément vidéo lorsque la source change
* @return {HTMLTrackElement} Un élément de piste Html.
* Il peut s'agir d'un {@link HTMLTrackElement} émulé ou natif.
* @deprecated La valeur par défaut du paramètre "manualCleanup" sera par défaut
* à "false" dans les prochaines versions de Video.js
*/
addRemoteTextTrack(options, manualCleanup) {
const htmlTrackElement = super.addRemoteTextTrack(options, manualCleanup) ;
if (this.featuresNativeTextTracks) {
this.el().appendChild(htmlTrackElement) ;
}
return htmlTrackElement ;
}
/**
* Supprimer la `TextTrack` distante de l'objet `TextTrackList`
*
* @param {TextTrack} track
* objet `TextTrack` à supprimer
*/
removeRemoteTextTrack(track) {
super.removeRemoteTextTrack(track) ;
if (this.featuresNativeTextTracks) {
const tracks = this.$$('track') ;
let i = tracks.length ;
while (i--) {
if (track === tracks[i] || track === tracks[i].track) {
this.el().removeChild(tracks[i]) ;
}
}
}
}
/**
* Obtient les mesures de qualité de lecture des médias disponibles, telles que spécifiées par la norme Media
* Qualité de lecture API.
*
* @see [Spec]{@link https://wicg.github.io/media-playback-quality}
*
* @return {Object}
* Un objet contenant les mesures de qualité de lecture des médias prises en charge
*/
getVideoPlaybackQuality() {
if (typeof this.el().getVideoPlaybackQuality === 'function') {
return this.el().getVideoPlaybackQuality() ;
}
const videoPlaybackQuality = {} ;
if (typeof this.el().webkitDroppedFrameCount !== 'undefined') &&
typeof this.el().webkitDecodedFrameCount !== 'undefined') {
videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount ;
videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount ;
}
if (window.performance && typeof window.performance.now === 'function') {
videoPlaybackQuality.creationTime = window.performance.now() ;
} else if (window.performance &&
fenêtre.performance.timing &&
typeof window.performance.timing.navigationStart === 'number') {
videoPlaybackQuality.creationTime =
window.Date.now() - window.performance.timing.navigationStart ;
}
return videoPlaybackQuality ;
}
}
/* Test du support HTML5 ---------------------------------------------------- */
/**
* Élément permettant de tester les capacités média du navigateur HTML5
*
* @type {Element}
* @constant
* @private
*/
defineLazyProperty(Html5, 'TEST_VID', function() {
if (!Dom.isReal()) {
retour ;
}
const video = document.createElement('video') ;
const track = document.createElement('track') ;
track.kind = 'captions' ;
track.srclang = 'en' ;
track.label = 'English' ;
video.appendChild(track) ;
retour vidéo ;
}) ;
/**
* Vérifier si les médias HTML5 sont pris en charge par ce navigateur/appareil.
*
* @return {boolean}
* - True si les médias HTML5 sont pris en charge.
* - Faux si le média HTML5 n'est pas pris en charge.
*/
Html5.isSupported = function() {
// IE sans Media Player est un menteur ! (#984)
essayez {
Html5.TEST_VID.volume = 0.5 ;
} catch (e) {
retourner faux ;
}
return ! !(Html5.TEST_VID && Html5.TEST_VID.canPlayType) ;
};
/**
* Vérifier si le technicien peut prendre en charge le type donné
*
* @param {string} type
* Le type d'image à vérifier
* @return {string} 'probablement', 'peut-être', ou '' (chaîne vide)
*/
Html5.canPlayType = function(type) {
return Html5.TEST_VID.canPlayType(type) ;
};
/**
* Vérifier si le technicien peut prendre en charge la source donnée
*
* @param {Objet} srcObj
* L'objet source
* @param {Objet} options
* Les options transmises au tech
* @return {string} 'probablement', 'peut-être', ou '' (chaîne vide)
*/
Html5.canPlaySource = function(srcObj, options) {
return Html5.canPlayType(srcObj.type) ;
};
/**
* Vérifier si le volume peut être modifié dans ce navigateur/appareil.
* De nombreux appareils mobiles ne permettent pas de modifier le volume.
* Plus précisément, il ne peut pas être modifié à partir de 1 sur iOS.
*
* @return {boolean}
* - Vrai si le volume peut être contrôlé
* - Faux sinon
*/
Html5.canControlVolume = function() {
// IE se trompera si Windows Media Player n'est pas installé #3315
essayez {
const volume = Html5.TEST_VID.volume ;
Html5.TEST_VID.volume = (volume / 2) + 0,1 ;
const canControl = volume !== Html5.TEST_VID.volume ;
// Avec l'introduction d'iOS 15, il y a des cas où le volume est lu comme
// modifié mais revient à son état d'origine au début du prochain tick.
// Déterminer si le volume peut être contrôlé sur iOS,
// un délai d'attente est fixé et le volume est vérifié de manière asynchrone.
// Comme `features` ne fonctionne pas actuellement de manière asynchrone, la valeur est définie manuellement.
if (canControl && browser.IS_IOS) {
window.setTimeout(() => {
if (Html5 && Html5.prototype) {
Html5.prototype.featuresVolumeControl = volume !== Html5.TEST_VID.volume ;
}
}) ;
// La valeur par défaut d'iOS est false, qui sera mise à jour dans le timeout ci-dessus.
retourner faux ;
}
return canControl ;
} catch (e) {
retourner faux ;
}
};
/**
* Vérifier si le volume peut être mis en sourdine dans ce navigateur/appareil.
* Certains appareils, par exemple iOS, ne permettent pas de modifier le volume
* mais permet l'inhibition/la désactivation.
*
* @return {bolean}
* - Vrai si le volume peut être mis en sourdine
* - Faux sinon
*/
Html5.canMuteVolume = function() {
essayez {
const muted = Html5.TEST_VID.muted ;
// dans certaines versions d'iOS, la propriété "muted" ne fonctionne pas toujours
// travail, nous voulons donc définir à la fois la propriété et l'attribut
Html5.TEST_VID.muted = !muted ;
if (Html5.TEST_VID.muted) {
Dom.setAttribute(Html5.TEST_VID, 'muted', 'muted') ;
} else {
Dom.removeAttribute(Html5.TEST_VID, 'muted', 'muted') ;
}
return muted !== Html5.TEST_VID.muted ;
} catch (e) {
retourner faux ;
}
};
/**
* Vérifiez si la vitesse de lecture peut être modifiée dans ce navigateur/appareil.
*
* @return {boolean}
* - Vrai si la vitesse de lecture peut être contrôlée
* - Faux sinon
*/
Html5.canControlPlaybackRate = function() {
// L'API de taux de lecture est implémentée dans Android Chrome, mais ne fait rien
// https://github.com/videojs/video.js/issues/3180
if (browser.IS_ANDROID && browser.IS_CHROME && browser.CHROME_VERSION < 58) {
retourner faux ;
}
// IE se trompera si Windows Media Player n'est pas installé #3315
essayez {
const playbackRate = Html5.TEST_VID.playbackRate ;
Html5.TEST_VID.playbackRate = (playbackRate / 2) + 0.1 ;
return playbackRate !== Html5.TEST_VID.playbackRate ;
} catch (e) {
retourner faux ;
}
};
/**
* Vérifier si l'on peut remplacer les attributs d'un élément vidéo/audio, avec
* Object.defineProperty.
*
* @return {boolean}
* - True si les attributs intégrés peuvent être remplacés
* - Faux sinon
*/
Html5.canOverrideAttributes = function() {
// si nous ne pouvons pas écraser la propriété src/innerHTML, il n'y a pas de support
// iOS 7 safari par exemple ne peut pas faire cela.
essayez {
const noop = () => {} ;
Object.defineProperty(document.createElement('video'), 'src', {get : noop, set : noop}) ;
Object.defineProperty(document.createElement('audio'), 'src', {get : noop, set : noop}) ;
Object.defineProperty(document.createElement('video'), 'innerHTML', {get : noop, set : noop}) ;
Object.defineProperty(document.createElement('audio'), 'innerHTML', {get : noop, set : noop}) ;
} catch (e) {
retourner faux ;
}
retourner vrai ;
};
/**
* Vérifier si les `TextTrack`s natifs sont supportés par ce navigateur/appareil.
*
* @return {boolean}
* - True si les `TextTrack`s natifs sont supportés.
* - Faux sinon
*/
Html5.supportsNativeTextTracks = function() {
return browser.IS_ANY_SAFARI || (browser.IS_IOS && browser.IS_CHROME) ;
};
/**
* Vérifier si les `VideoTrack`s natifs sont supportés par ce navigateur/appareil
*
* @return {boolean}
* - True si les `VideoTrack`s natifs sont supportés.
* - Faux sinon
*/
Html5.supportsNativeVideoTracks = function() {
return ! !(Html5.TEST_VID && Html5.TEST_VID.videoTracks) ;
};
/**
* Vérifier si les `AudioTrack`s natifs sont supportés par ce navigateur/appareil
*
* @return {boolean}
* - True si les `AudioTrack`s natifs sont supportés.
* - Faux sinon
*/
Html5.supportsNativeAudioTracks = function() {
return ! !(Html5.TEST_VID && Html5.TEST_VID.audioTracks) ;
};
/**
* Une série d'événements disponibles sur la technologie Html5.
*
* @private
* @type {Array}
*/
Html5.Events = [
'loadstart',
suspendre",
'abort',
'erreur',
vide",
"bloqué",
'loadedmetadata',
'loadeddata',
'canplay',
'canplaythrough',
'jouant',
attente",
recherche",
recherchée",
terminé",
'durationchange',
'timeupdate',
'progrès',
'play',
pause",
'ratechange',
'redimensionner',
volumechange"
] ;
/**
* Booléen indiquant si la `Tech` prend en charge le contrôle du volume.
*
* @type {boolean}
* @default {@link Html5.canControlVolume}
*/
/**
* Booléen indiquant si la `Tech` supporte le volume muting.
*
* @type {bolean}
* @default {@link Html5.canMuteVolume}
*/
/**
* Booléen indiquant si la `Technologie` permet de modifier la vitesse à laquelle le média est utilisé
* joue. Exemples :
* - Régler le lecteur pour qu'il joue 2x (deux fois) plus vite
* - Régler le lecteur pour qu'il joue 0,5x (moitié) plus vite
*
* @type {boolean}
* @default {@link Html5.canControlPlaybackRate}
*/
/**
* Booléen indiquant si la `Tech` supporte l'événement `sourceset`.
*
* @type {boolean}
* @défaut
*/
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement les `TextTrack`s natifs.
*
* @type {boolean}
* @default {@link Html5.supportsNativeTextTracks}
*/
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement les `VideoTrack`s natifs.
*
* @type {boolean}
* @default {@link Html5.supportsNativeVideoTracks}
*/
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement les `AudioTrack`s natifs.
*
* @type {boolean}
* @default {@link Html5.supportsNativeAudioTracks}
*/
[
["featuresMuteControl", "canMuteVolume"],
["featuresPlaybackRate", "canControlPlaybackRate"],
['featuresSourceset', 'canOverrideAttributes'],
["featuresNativeTextTracks", "supportsNativeTextTracks"],
["featuresNativeVideoTracks", "supportsNativeVideoTracks"],
['featuresNativeAudioTracks', 'supportsNativeAudioTracks']]
].forEach(function([key, fn]) {
defineLazyProperty(Html5.prototype, key, () => Html5[fn](), true) ;
}) ;
Html5.prototype.featuresVolumeControl = Html5.canControlVolume() ;
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement l'élément media
* se déplaçant dans le DOM. iOS s'interrompt si l'on déplace l'élément média, il faut donc lui donner la valeur suivante
* faux là. Partout ailleurs, cela devrait être vrai.
*
* @type {boolean}
* @défaut
*/
Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS ;
// TODO : Commentaire précédent : Ne semble plus être utilisé. Peut probablement être retiré.
// Est-ce vrai ?
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement le redimensionnement automatique des médias
* lors du passage en plein écran.
*
* @type {boolean}
* @défaut
*/
Html5.prototype.featuresFullscreenResize = true ;
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement l'événement de progression.
* Si cette valeur est fausse, les événements manuels de type `progress` seront déclenchés à la place.
*
* @type {boolean}
* @défaut
*/
Html5.prototype.featuresProgressEvents = true ;
/**
* Booléen indiquant si la technologie `HTML5` supporte actuellement l'événement timeupdate.
* Si c'est faux, les événements manuels `timeupdate` seront déclenchés à la place.
*
* @défaut
*/
Html5.prototype.featuresTimeupdateEvents = true ;
/**
* Si l'el HTML5 supporte `requestVideoFrameCallback`
*
* @type {boolean}
*/
Html5.prototype.featuresVideoFrameCallback = ! !(Html5.TEST_VID && Html5.TEST_VID.requestVideoFrameCallback) ;
// Détection des fonctionnalités HTML5 et corrections pour les appareils --------------------------------- //
let canPlayType ;
Html5.patchCanPlayType = function() {
// Android 4.0 et supérieur peut lire HLS dans une certaine mesure, mais il signale qu'il n'est pas en mesure de le faire
// Firefox et Chrome rapportent correctement
if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX && !browser.IS_CHROME) {
canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType ;
Html5.TEST_VID.constructor.prototype.canPlayType = function(type) {
const mpegurlRE = /^application/(?:x-|vnd.apple\.)mpegurl/i ;
if (type && mpegurlRE.test(type)) {
retourner "peut-être" ;
}
return canPlayType.call(this, type) ;
};
}
};
Html5.unpatchCanPlayType = function() {
const r = Html5.TEST_VID.constructor.prototype.canPlayType ;
if (canPlayType) {
Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType ;
}
retour r ;
};
// par défaut, patcher l'élément média
Html5.patchCanPlayType() ;
Html5.disposeMediaElement = function(el) {
if (!el) {
retour ;
}
if (el.parentNode) {
el.parentNode.removeChild(el) ;
}
// supprimer toute piste enfant ou nœud source pour empêcher leur chargement
while (el.hasChildNodes()) {
el.removeChild(el.firstChild) ;
}
// supprimer toute référence à src. ne pas mettre `src=''` car cela provoque un avertissement
// dans firefox
el.removeAttribute('src') ;
// forcer l'élément média à mettre à jour son état de chargement en appelant load()
// cependant IE sur Windows 7N a un bug qui provoque une erreur, il faut donc un try/catch (#793)
if (typeof el.load === 'function') {
// envelopper dans un iife pour ne pas être désoptimisé (#1060#discussion_r10324473)
(function() {
essayez {
el.load() ;
} catch (e) {
// non pris en charge
}
}()) ;
}
};
Html5.resetMediaElement = function(el) {
if (!el) {
retour ;
}
const sources = el.querySelectorAll('source') ;
let i = sources.length ;
while (i--) {
el.removeChild(sources[i]) ;
}
// supprimer toute référence src.
// ne pas mettre `src=''` car cela provoque une erreur
el.removeAttribute('src') ;
if (typeof el.load === 'function') {
// envelopper dans un iife pour ne pas être désoptimisé (#1060#discussion_r10324473)
(function() {
essayez {
el.load() ;
} catch (e) {
// satisfaire le linter
}
}()) ;
}
};
/* enveloppement des propriétés des éléments HTML5 natifs ----------------------------------- */
// Envelopper les attributs booléens natifs avec des getters qui vérifient à la fois la propriété et l'attribut
// La liste est la suivante :
// muted, defaultMuted, autoplay, controls, loop, playsinline
[
/**
* Récupère la valeur de `muted` de l'élément media. `muted` indique
* que le volume du support doit être réglé sur silencieux. Cela ne change pas grand-chose
* l'attribut `volume`.
*
* @method Html5#muted
* @return {boolean}
* - True si la valeur de `volume` doit être ignorée et l'audio mis en silencieux.
* - False si la valeur de `volume` doit être utilisée.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
en sourdine",
/**
* Obtenir la valeur de `defaultMuted` de l'élément media. `defaultMuted` indique
* si le média doit être mis en sourdine ou non. Ne modifie que l'état par défaut du
* media. `muted` et `defaultMuted` peuvent avoir des valeurs différentes. {@link Html5#muted} indique la valeur de l'attribut
* état actuel.
*
* @method Html5#defaultMuted
* @return {boolean}
* - La valeur de `defaultMuted` de l'élément media.
* - True indique que le média doit être mis en sourdine.
* - False indique que le média ne doit pas démarrer en sourdine
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'defaultMuted',
/**
* Récupère la valeur de `autoplay` de l'élément media. `autoplay` indique
* que la lecture du média doit commencer dès que la page est prête.
*
* @method Html5#autoplay
* @return {boolean}
* - La valeur de `autoplay` de l'élément media.
* - True indique que le média doit démarrer dès le chargement de la page.
* - False indique que le média ne doit pas démarrer dès le chargement de la page.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Obtenir la valeur de `controls` de l'élément media. `controls` indique
* si les contrôles médias natifs doivent être affichés ou masqués.
*
* @method Html5#controls
* @return {boolean}
* - La valeur de `controls` de l'élément media.
* - True indique que les contrôles natifs doivent être affichés.
* - False indique que les contrôles natifs doivent être cachés.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
*/
contrôles",
/**
* Récupère la valeur de `loop` de l'élément média. `loop` indique
* que le média doit revenir au début du média et continuer la lecture une fois que le média est terminé
* il arrive au bout.
*
* @method Html5#loop
* @return {boolean}
* - La valeur de `loop` de l'élément média.
* - True indique que la lecture doit revenir au début une fois que
* la fin d'un média est atteinte.
* - Faux indique que la lecture ne doit pas revenir en boucle au début lorsque le bouton
* l'extrémité du support est atteinte.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop' (boucle),
/**
* Obtenir la valeur de `playsinline` de l'élément media. `playsinline` indique
* au navigateur que la lecture en mode non-plein écran est préférée lorsque la lecture en mode plein écran est possible
* est la valeur native par défaut, comme dans Safari d'iOS.
*
* @method Html5#playsinline
* @return {boolean}
* - La valeur de `playsinline` de l'élément media.
* - True indique que le média doit être lu en ligne.
* - False indique que le média ne doit pas être lu en ligne.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
joue en ligne
].forEach(function(prop) {
Html5.prototype[prop] = function() {
return this.el_[prop] || this.el_.hasAttribute(prop) ;
};
}) ;
// Envelopper les attributs booléens natifs avec des fixateurs qui fixent à la fois la propriété et l'attribut
// La liste est la suivante :
// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
// setControls est un cas particulier ci-dessus
[
/**
* Fixe la valeur de `muted` sur l'élément media. `muted` indique que l'élément
* le niveau audio doit être silencieux.
*
* @method Html5#setMuted
* @param {boolean} muted
* - Vrai si l'audio doit être réglé sur silencieux
* - Faux sinon
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
*/
en sourdine",
/**
* Fixe la valeur de `defaultMuted` sur l'élément media. `defaultMuted` indique que la valeur courante de l'élément
* le niveau audio doit être silencieux, mais n'affecte le niveau de sourdine que lors de la première lecture.
*
* @method Html5.prototype.setDefaultMuted
* @param {boolean} defaultMuted
* - Vrai si l'audio doit être réglé sur silencieux
* - Faux sinon
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
*/
'defaultMuted',
/**
* Fixe la valeur de `autoplay` sur l'élément media. `autoplay` indique
* que la lecture du média doit commencer dès que la page est prête.
*
* @method Html5#setAutoplay
* @param {boolean} autoplay
* - True indique que le média doit démarrer dès le chargement de la page.
* - False indique que le média ne doit pas démarrer dès le chargement de la page.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
*/
'autoplay',
/**
* Fixe la valeur de `loop` sur l'élément média. `loop` indique
* que le média doit revenir au début du média et continuer la lecture une fois que le média est terminé
* il arrive au bout.
*
* @method Html5#setLoop
* @param {boolean} loop
* - True indique que la lecture doit revenir au début une fois que
* la fin d'un média est atteinte.
* - Faux indique que la lecture ne doit pas revenir en boucle au début lorsque le bouton
* l'extrémité du support est atteinte.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
*/
'loop' (boucle),
/**
* Fixe la valeur de `playsinline` de l'élément media. `playsinline` indique
* au navigateur que la lecture en mode non-plein écran est préférée lorsque la lecture en mode plein écran est possible
* est la valeur native par défaut, comme dans Safari d'iOS.
*
* @method Html5#setPlaysinline
* @param {boolean} playsinline
* - True indique que le média doit être lu en ligne.
* - False indique que le média ne doit pas être lu en ligne.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
*/
joue en ligne
].forEach(function(prop) {
Html5.prototype['set' + toTitleCase(prop)] = function(v) {
this.el_[prop] = v ;
if (v) {
this.el_.setAttribute(prop, prop) ;
} else {
this.el_.removeAttribute(prop) ;
}
};
}) ;
// Envelopper les propriétés natives avec un getter
// La liste est la suivante
// paused, currentTime, buffered, volume, poster, preload, error, seeking
// recherchable, terminé, playbackRate, defaultPlaybackRate, disablePictureInPicture
// joué, networkState, readyState, videoWidth, videoHeight, crossOrigin
[
/**
* Obtenir la valeur de `paused` de l'élément média. `paused` indique si l'élément media
* est en pause ou non.
*
* @method Html5#paused
* @return {boolean}
* La valeur de `paused` de l'élément média.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
*/
pausé",
/**
* Récupère la valeur de `currentTime` de l'élément media. `currentTime` indique
* la seconde à laquelle se trouve le média en cours de lecture.
*
* @method Html5#currentTime
* @return {number}
* La valeur de `currentTime` de l'élément média.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
*/
'currentTime',
/**
* Récupère la valeur de `buffered` de l'élément media. `buffered` est un élément de type `TimeRange`
* qui représente les parties du média qui sont déjà téléchargées et
* disponibles pour la lecture.
*
* @method Html5#buffered
* @return {TimeRange}
* La valeur de `buffered` de l'élément média.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
*/
'buffered',
/**
* Obtenir la valeur de `volume` de l'élément média. `volume` indique
* le volume de lecture actuel de l'audio pour un média. `volume` sera une valeur de 0
* (silencieux) à 1 (le plus fort et par défaut).
*
* @method Html5#volume
* @return {number}
* La valeur de `volume` de l'élément média. La valeur sera comprise entre 0 et 1.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
*/
volume",
/**
* Obtenir la valeur de `poster` de l'élément média. `poster` indique
* l'url d'un fichier image qui peut/veut être affiché lorsqu'aucune donnée média n'est disponible.
*
* @method Html5#poster
* @return {string}
* La valeur de `poster` de l'élément média. La valeur sera une url vers un
* image.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
*/
poster",
/**
* Récupère la valeur de `preload` de l'élément media. `preload` indique
* ce qui doit être téléchargé avant d'interagir avec les médias. Il peut présenter les caractéristiques suivantes
* valeurs :
* - none : rien ne doit être téléchargé
* - métadonnées : l'affiche et les premières images du média peuvent être téléchargées afin d'obtenir des informations sur le contenu du média
* les dimensions des médias et autres métadonnées
* - auto : permet de télécharger le média et les métadonnées du média avant la fin de l'opération
* l'interaction
*
* @method Html5#preload
* @return {string}
* La valeur de `preload` de l'élément média. Sera "none", "metadata",
* ou "auto".
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
*/
'preload',
/**
* Récupère la valeur de l'élément `error` de l'élément media. `error` indique toute
* MediaError qui a pu se produire pendant la lecture. Si l'erreur renvoie un résultat nul, il n'y a pas de
* erreur actuelle.
*
* @method Html5#error
* @return {MediaError|null}
* La valeur de `error` de l'élément media. Il y aura une `MediaError` s'il y a une
* est une erreur courante et null sinon.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
*/
'erreur',
/**
* Récupère la valeur de `seeking` de l'élément media. `seeking` indique si l'élément
* les médias sont actuellement à la recherche d'un nouveau poste ou non.
*
* @method Html5#seeking
* @return {boolean}
* - La valeur de `seeking` de l'élément média.
* - Vrai indique que le média est actuellement à la recherche d'un nouveau poste.
* - Faux indique que le média n'est pas à la recherche d'un nouveau poste pour le moment.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
*/
recherche",
/**
* Récupère la valeur de `seekable` de l'élément media. `seekable` renvoie une valeur
* objet `TimeRange` indiquant les plages de temps qui peuvent actuellement être `recherchées`.
*
* @method Html5#seekable
* @return {TimeRange}
* La valeur de `seekable` de l'élément média. Un objet `TimeRange` (plage de temps)
* indiquant les plages de temps actuelles qui peuvent être recherchées.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
*/
'cherchable',
/**
* Récupère la valeur de `ended` de l'élément media. `ended` indique si
* les médias ont atteint la fin ou non.
*
* @method Html5#ended
* @return {boolean}
* - La valeur de `ended` de l'élément média.
* - True indique que le média est terminé.
* - Faux indique que le média n'est pas terminé.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
*/
terminé",
/**
* Obtenir la valeur de `playbackRate` de l'élément media. `playbackRate` indique
* la vitesse à laquelle le média est en cours de lecture. Exemples :
* - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
* - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
*
* @method Html5#playbackRate
* @return {number}
* La valeur de `playbackRate` de l'élément media. Un nombre indiquant
* la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
taux de lecture",
/**
* Obtenir la valeur de `defaultPlaybackRate` de l'élément media. `defaultPlaybackRate` indique
* la vitesse à laquelle le média est en cours de lecture. Cette valeur n'indique pas l'état actuel de la
* `playbackRate` après le début de la lecture, utilisez {@link Html5#playbackRate} pour cela.
*
* Exemples:
* - si defaultPlaybackRate est fixé à 2, la lecture du média sera deux fois plus rapide.
* - si defaultPlaybackRate est fixé à 0,5, le média sera lu deux fois moins vite.
*
* @method Html5.prototype.defaultPlaybackRate
* @return {number}
* La valeur de `defaultPlaybackRate` de l'élément média. Un nombre indiquant
* la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
'defaultPlaybackRate',
/**
* Obtenir la valeur de "disablePictureInPicture" de l'élément vidéo.
*
* @method Html5#disablePictureInPicture
* @return {boolean} value
* - La valeur de `disablePictureInPicture` de l'élément vidéo.
* - True indique que la vidéo ne peut pas être lue en mode image dans l'image
* - Faux indique que la vidéo peut être lue en mode image dans l'image
*
* @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'disablePictureInPicture',
/**
* Récupère la valeur de `played` de l'élément media. `played` retourne un `TimeRange`
* représentant les points de la chronologie des médias qui ont été lus.
*
* @method Html5#played
* @return {TimeRange}
* La valeur de `played` de l'élément média. Un objet `TimeRange` indiquant
* les plages de temps qui ont été jouées.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
*/
jouée",
/**
* Obtenir la valeur de `networkState` de l'élément média. `networkState` indique
* l'état actuel du réseau. Il renvoie une énumération à partir de la liste suivante :
* - 0 : NETWORK_EMPTY
* - 1 : NETWORK_IDLE
* - 2 : NETWORK_LOADING
* - 3 : NETWORK_NO_SOURCE
*
* @method Html5#networkState
* @return {number}
* La valeur de `networkState` de l'élément média. Il s'agira d'un nombre
* à partir de la liste figurant dans la description.
*
* @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
*/
'networkState',
/**
* Obtenir la valeur de `readyState` de l'élément media. `readyState` indique
* l'état actuel de l'élément multimédia. Il renvoie une énumération de la liste
* liste suivante :
* - 0 : HAVE_RIEN
* - 1 : HAVE_MÉTADONNÉES
* - 2 : HAVE_CURRENT_DATA
* - 3 : HAVE_FUTURE_DATA
* - 4 : HAVE_ASOUGH_DATA
*
* @method Html5#readyState
* @return {number}
* La valeur de `readyState` de l'élément média. Il s'agira d'un nombre
* à partir de la liste figurant dans la description.
*
* @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
*/
'readyState',
/**
* Obtenir la valeur de `videoWidth` de l'élément vidéo. `videoWidth` indique
* la largeur actuelle de la vidéo en pixels css.
*
* @method Html5#videoWidth
* @return {number}
* La valeur de `videoWidth` de l'élément vidéo. Il s'agira d'un nombre
* en pixels css.
*
* @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
*/
'videoWidth',
/**
* Obtenir la valeur de `videoHeight` de l'élément vidéo. `videoHeight` indique
* la hauteur actuelle de la vidéo en pixels css.
*
* @method Html5#videoHeight
* @return {number}
* La valeur de `videoHeight` de l'élément vidéo. Il s'agira d'un nombre
* en pixels css.
*
* @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
*/
hauteur de la vidéo",
/**
* Obtenir la valeur de `crossOrigin` de l'élément média. `crossOrigin` indique
* au navigateur qui doit envoyer les cookies en même temps que les demandes de
* différents actifs/listes de lecture
*
* @method Html5#crossOrigin
* @return {string}
* - anonyme indique que le média ne doit pas envoyer de cookies.
* - use-credentials indique que le média doit envoyer des cookies avec les requêtes.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
*/
crossOrigin' (origine croisée)
].forEach(function(prop) {
Html5.prototype[prop] = function() {
return this.el_[prop] ;
};
}) ;
// Envelopper les propriétés natives avec un setter dans ce format :
// set + toTitleCase(name)
// La liste est la suivante :
// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate,
// setDisablePictureInPicture, setCrossOrigin
[
/**
* Fixe la valeur de `volume` sur l'élément média. `volume` indique la valeur actuelle de
* le niveau audio en pourcentage sous forme décimale. Cela signifie que 1 correspond à 100 %, 0,5 à 50 % et
* et ainsi de suite.
*
* @method Html5#setVolume
* @param {number} percentAsDecimal
* Le pourcentage de volume sous forme décimale. La plage valide est comprise entre 0 et 1.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
*/
volume",
/**
* Fixe la valeur de `src` sur l'élément media. `src` indique le nom de l'élément
* {@link Tech~SourceObject} pour le média.
*
* @method Html5#setSrc
* @param {Tech~SourceObject} src
* L'objet source à définir comme source courante.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
*/
'src',
/**
* Fixe la valeur de `poster` sur l'élément media. `poster` est l'url de
* un fichier image qui peut/veut être affiché lorsqu'aucune donnée média n'est disponible.
*
* @method Html5#setPoster
* @param {string} poster
* L'url d'une image qui doit être utilisée comme `poster` pour le média
* élément.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
*/
poster",
/**
* Fixe la valeur de `preload` sur l'élément media. `preload` indique
* ce qui doit être téléchargé avant d'interagir avec les médias. Il peut présenter les caractéristiques suivantes
* valeurs :
* - none : rien ne doit être téléchargé
* - métadonnées : l'affiche et les premières images du média peuvent être téléchargées afin d'obtenir des informations sur le contenu du média
* les dimensions des médias et autres métadonnées
* - auto : permet de télécharger le média et les métadonnées du média avant la fin de l'opération
* l'interaction
*
* @method Html5#setPreload
* @param {string} preload
* La valeur de `preload` à définir sur l'élément média. Doit être "none", "metadata",
* ou "auto".
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
*/
'preload',
/**
* Fixe la valeur de `playbackRate` sur l'élément media. `playbackRate` indique
* la vitesse à laquelle le média doit être lu. Exemples :
* - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
* - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
*
* @method Html5#setPlaybackRate
* @return {number}
* La valeur de `playbackRate` de l'élément media. Un nombre indiquant
* la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
*/
taux de lecture",
/**
* Fixe la valeur de `defaultPlaybackRate` sur l'élément média. `defaultPlaybackRate` indique
* la vitesse à laquelle le média doit être lu lors du démarrage initial. Modification de cette valeur
* après le démarrage d'une vidéo n'aura aucun effet. Vous devriez plutôt utiliser {@link Html5#setPlaybackRate}.
*
* Exemple de valeurs :
* - si playbackRate est réglé sur 2, le média sera lu deux fois plus vite.
* - si playbackRate est fixé à 0,5, le média sera lu deux fois moins vite.
*
* @method Html5.prototype.setDefaultPlaybackRate
* @return {number}
* La valeur de `defaultPlaybackRate` de l'élément média. Un nombre indiquant
* la vitesse de lecture actuelle du support, 1 étant la vitesse normale.
*
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
*/
'defaultPlaybackRate',
/**
* Empêche le navigateur de proposer un menu contextuel "image dans l'image"
* ou de demander une image dans l'image automatiquement dans certains cas.
*
* @method Html5#setDisablePictureInPicture
* @param {boolean} value
* La valeur vraie désactive le mode Image dans l'image.
*
* @see [Spec]{@link https://w3c.github.io/picture-in-picture/#disable-pip}
*/
'disablePictureInPicture',
/**
* Fixe la valeur de `crossOrigin` de l'élément média. `crossOrigin` indique
* au navigateur qui doit envoyer les cookies en même temps que les demandes de
* différents actifs/listes de lecture
*
* @method Html5#setCrossOrigin
* @param {string} crossOrigin
* - anonyme indique que le média ne doit pas envoyer de cookies.
* - use-credentials indique que le média doit envoyer des cookies avec les requêtes.
*
* @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
*/
crossOrigin' (origine croisée)
].forEach(function(prop) {
Html5.prototype['set' + toTitleCase(prop)] = function(v) {
this.el_[prop] = v ;
};
}) ;
// envelopper les fonctions natives dans une fonction
// La liste est la suivante :
// pause, chargement, lecture
[
/**
* Une enveloppe autour de la fonction `pause` des éléments de média. Ceci appellera la fonction `HTML5`
* la fonction `pause` des éléments de média.
*
* @method Html5#pause
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
*/
pause",
/**
* Une enveloppe autour de la fonction `load` des éléments de média. Ceci appellera le `HTML5`s
* fonction `load` de l'élément média.
*
* @method Html5#load
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
*/
'load',
/**
* Une enveloppe autour de la fonction `play` des éléments de média. Ceci appellera le `HTML5`s
* fonction `play` de l'élément média.
*
* @method Html5#play
* @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
*/
jouer"
].forEach(function(prop) {
Html5.prototype[prop] = function() {
return this.el_[prop]() ;
};
}) ;
Tech.withSourceHandlers(Html5) ;
/**
* Gestionnaire de source natif pour Html5, qui transmet simplement la source à l'élément média.
*
* @property {Tech~SourceObject} source
* L'objet source
*
* @property {Html5} tech
* L'instance de la technologie HTML5.
*/
Html5.nativeSourceHandler = {} ;
/**
* Vérifie si l'élément multimédia peut lire le type de fichier mime donné.
*
* @param {string} type
* Le type d'image à vérifier
*
* @return {string}
* "probablement", "peut-être" ou '' (chaîne vide)
*/
Html5.nativeSourceHandler.canPlayType = function(type) {
// IE sans MediaPlayer provoque une erreur (#519)
essayez {
return Html5.TEST_VID.canPlayType(type) ;
} catch (e) {
retourner '' ;
}
};
/**
* Vérifier si l'élément média peut gérer une source de manière native.
*
* @param {Tech~SourceObject} source
* L'objet source
*
* @param {Objet} [options]
* Options à transmettre au technicien.
*
* @return {string}
* 'probablement', 'peut-être' ou '' (chaîne vide).
*/
Html5.nativeSourceHandler.canHandleSource = function(source, options) {
// Si un type a été fourni, nous devons nous y fier
if (source.type) {
return Html5.nativeSourceHandler.canPlayType(source.type) ;
// S'il n'y a pas de type, on revient à la vérification de "video/[EXTENSION]"
} else if (source.src) {
const ext = Url.getFileExtension(source.src) ;
return Html5.nativeSourceHandler.canPlayType(`video/${ext}`) ;
}
retourner '' ;
};
/**
* Transmettre la source à l'élément multimédia natif.
*
* @param {Tech~SourceObject} source
* L'objet source
*
* @param {Html5} tech
* L'instance de la technologie Html5
*
* @param {Objet} [options]
* Les options à transmettre à la source
*/
Html5.nativeSourceHandler.handleSource = function(source, tech, options) {
tech.setSrc(source.src) ;
};
/**
* Un noop pour la fonction native dispose, car le nettoyage n'est pas nécessaire.
*/
Html5.nativeSourceHandler.dispose = function() {} ;
// Enregistrer le gestionnaire de sources natives
Html5.registerSourceHandler(Html5.nativeSourceHandler) ;
Tech.registerTech('Html5', Html5) ;
exporter par défaut Html5 ;