/**
 * @file tech.js
 * /

importiere Komponente von '../komponente';
import mergeOptions from '../utils/merge-options.js';
import * as Fn from '../utils/fn.js';
import log from '../utils/log.js';
importiere {createTimeRange} aus '.. /utils/time-ranges.js ';
importiere {bufferedPercent} aus '.. /utils/buffer.js ';
importiere MediaError aus '.. /media-error.js ';
import window from 'global/window';
dokument aus 'global/document' importieren;
importiere {isPlain} von '../utils/obj';
importiere * als TRACK_TYPES aus '.. /tracks/track-Typen ';
importiere {toTitleCase, toLowerCase} aus '.. /utils/string-cases.js ';
importiere vtt aus 'videojs-vtt.js';
import * as Guid from '../utils/guid.js';

/**
 * Ein Objekt, das eine Struktur wie: `{src: 'url', type: 'mimetype'}` oder eine Zeichenfolge enthält
 * das enthält nur die src-URL allein.
 * * `var sourceObject = {src: 'http://ex.com/video.mp4', typ: 'video/mp4'}; `
   * `var sourceString = 'http://example.com/some-video.mp4'; `
 *
 * @typedef {object|String} tech~sourceObject
 *
 * @property {string} src
 * Die URL zur Quelle
 *
 * Typ @property {string}
 * Der MIME-Typ der Quelle
 * /

/**
 * Eine Funktion, die von {@link Tech} verwendet wird, um einen neuen {@link TextTrack} zu erstellen.
 *
 * @privat
 *
 * @param {Tech} selbst
 * Ein Beispiel der Tech-Klasse.
 *
 * @param {string} kind
 *        textTrack"-Typ (Untertitel, Untertitel, Beschreibungen, Kapitel oder Metadaten)
 *
 * @param {string} [label]
 *        Label zur Kennzeichnung der Textspur
 *
 * @param {string} [Sprache]
 *        Sprachabkürzung mit zwei Buchstaben
 *
 * @param {Object} [options={}]
 * Ein Objekt mit zusätzlichen Texttrack-Optionen
 *
 * @return {TextTrack}
 * Der Texttrack, der erstellt wurde.
 * /
function createTrackHelper (self, kind, label, language, options = {}) {
  const tracks = self.textTracks ();

  options.kind = nett;

  wenn (Etikett) {
    options.label = label;
  }
  wenn (Sprache) {
    options.language = Sprache;
  }
  options.tech = selbst;

  const track = new track_types.all.text.trackClass (Optionen);

  Tracks.addTrack (Track);

  rückweg;
}

/**
 * Dies ist die Basisklasse für Controller der Medienwiedergabetechnologie wie
 * {@link HTML5}
 *
 * @erweitert Komponente
 * /
class Tech erweitert Komponente {

  /**
  * Erstellen Sie eine Instanz dieser Tech.
  *
  * @param {Object} [Optionen]
  *        Der Schlüssel/Wertspeicher der Playeroptionen.
  *
  * @param {Component~ReadyCallback} ready
  *        Callback-Funktion, die aufgerufen wird, wenn die "HTML5"-Technologie bereit ist.
  * /
  konstruktor (Optionen = {}, bereit = Funktion () {}) {
    //Wir wollen nicht, dass der Techniker Benutzeraktivitäten automatisch meldet.
    //Dies erfolgt manuell in addControlsListeners
    options.reportTouchActivity = false;
    super(null, options, ready);

    this.onDurationChange_ = (e) => this.onDurationChange (e);
    this.trackProgress_ = (e) => this.trackProgress (e);
    this.trackCurrentTime_ = (e) => this.trackCurrentTime (e);
    this.stopTrackingCurrentTime_ = (e) => this.stopTrackingCurrentTime (e);
    this.dispeSourceHandler_ = (e) => this.dispeSourceHandler (e);

    this.queuedHanders_ = neues Set ();

    //verfolge, ob die aktuelle Quelle überhaupt abgespielt hat, bis
    //implementiere ein sehr limitiertes Played ()
    this.hasStarted_ = false;
    this.on ('spielt', function () {
      this.hasStarted_ = wahr;
    });
    this.on ('loadstart', Funktion () {
      this.hasStarted_ = false;
    });

    track_types.all.names.forEach ((name) => {
      const props = TRACK_TYPES.ALL[name];

      if (Optionen && Optionen [props.getterName]) {
        this [props.PrivateName] = Optionen [props.getterName];
      }
    });

    //Verfolge den Fortschritt manuell, wenn der Browser/die Technik ihn nicht meldet.
    wenn (! this.featuresProgressEvents) {
      this.manualProgresson ();
    }

    //Manuelles Nachverfolgen von Zeitaktualisierungen in Fällen, in denen der Browser/die Technik sie nicht meldet.
    wenn (! this.featuresTimeUpdateEvents) {
      this.manualTimeUpdatesOn ();
    }

    ['Text', 'Audio', 'Video'] .forEach (track) => {
      if (options [`native$ {track} Tracks`] === falsch) {
        this [`featuresNative$ {track} Tracks`] = falsch;
      }
    });

    wenn (options.nativeCaptions === falsch || options.nativeTextTracks === falsch) {
      this.featureNativeTextTracks = falsch;
    } sonst wenn (options.nativeCaptions === true || options.nativeTextTracks === true) {
      this.featuresNativeTextTracks = wahr;
    }

    if (!this.featuresNativeTextTracks) {
      this.emulateTextTracks ();
    }

    this.preloadTextTracks = Optionen.preloadTextTracks! == falsch;

    this.autoRemoteTextTracks_ = neues track_types.all.text.listClass ();

    this.initTrackListeners ();

    //Aktiviere Component-Tap-Events nur, wenn du keine nativen Steuerelemente verwendest
    wenn (! Options.native Steuerelemente für Touch) {
      this.emitTapEvents();
    }

    wenn (this.constructor) {
      this.name_ = this.constructor.name || 'Unbekannte Technologie';
    }
  }

  /**
   * Eine spezielle Funktion, um den Quellensatz so auszulösen, dass der Spieler es ermöglicht
   * um erneut auszulösen, falls der Spieler oder der Techniker noch nicht bereit sind.
   *
   * @fires Technik #sourceset
   * @param {string} src Die Quellzeichenfolge zum Zeitpunkt der Quelländerung.
   * /
  TriggerSourceSet (src) {
    if (!this.isReady_) {
      //wenn das erste Mal bereit ist, müssen wir den Quellsatz auslösen
      //1 ms nach Bereit, damit der Spieler darauf achten kann.
      this.one ('bereit', () => this.setTimeout () => this.triggerSourceSet (src), 1));
    }

    /**
     * Wird ausgelöst, wenn die Quelle auf die Technologie eingestellt ist, die das Medienelement verursacht
     * um neu zu laden.
     *
     * @see {@link Spieler #event:sourceset}
     * @event Technik #sourceset
     * @Typ {EventTarget~Event}
     * /
    this.trigger({
      src,
      typ: 'Quellensatz'
    });
  }

  /* Fallbacks für nicht unterstützte Ereignistypen
  ============================================================================ */

  /**
   * Füllen Sie das `progress`-Ereignis für Browser, die es nicht nativ unterstützen, mit Polyfill aus.
   *
   * @see {@link Technik #trackProgress}
   * /
  Manueller Fortschritt auf () {
    this.on ('durationchange', this.onDurationChange_);

    this.manualProgress = wahr;

    //Löse die Fortschrittsüberwachung aus, wenn eine Quelle zu laden beginnt
    this.one ('bereit', this.trackProgress_);
  }

  /**
   * Deaktivieren Sie das Polyfill für "Fortschritts"-Ereignisse, das in
   * {@link Tech#manualProgressOn}
   * /
  Manueller Fortschritt von () {
    this.manualProgress = falsch;
    this.stopTrackingProgress();

    this.off ('durationchange', this.onDurationChange_);
  }

  /**
   * Dies wird verwendet, um ein Fortschrittsereignis auszulösen, wenn sich der gepufferte Prozentsatz ändert. Es
   * legt eine Intervallfunktion fest, die alle 500 Millisekunden aufgerufen wird, um zu überprüfen, ob
   * Der Endprozentsatz des Puffers hat sich geändert.
   *
   * > Diese Funktion wird von {@link Tech #manualProgressOn} aufgerufen
   *
   * @param {EventTarget~Event} event
   * Das `read`-Ereignis, das dazu geführt hat, dass dies ausgeführt wurde.
   *
   * @listens Technik #ready
   * @fires Technik #progress
   * /
  trackProgress (Ereignis) {
    this.stopTrackingProgress();
    this.progressInterval = this.setInterval (fn.Bind (this, function () {
      //Nicht auslösen, es sei denn, die gepufferte Menge ist größer als beim letzten Mal

      const numBufferedPercent = this.bufferedPercent ();

      wenn (this.BufferedPercent_! == NumBufferedPercent) {
        /**
         * Siehe {@link Player #progress}
         *
         * @event Technik #progress
         * @Typ {EventTarget~Event}
         * /
        this.trigger ('Fortschritt');
      }

      this.bufferedPercent_ = NumBufferedPercent_;

      wenn (numBufferedPercent === 1) {
        this.stopTrackingProgress();
      }
    }), 500);
  }

  /**
   * Aktualisiere unsere interne Dauer eines `durationchange`-Events, indem du uns anrufst
   * {@link Tech #duration}.
   *
   * @param {EventTarget~Event} event
   * Das `durationchange`-Ereignis, das dazu geführt hat, dass dies ausgeführt wurde.
   *
   * @listens Tech#durationchange
   * /
  onDurationChange (Ereignis) {
    this.duration_ = this.duration ();
  }

  /**
   * Ruft ein `TimeRange`-Objekt für die Pufferung ab und erzeugt es.
   *
   * @return {TimeRange}
   * Das Zeitbereichsobjekt, das erstellt wurde.
   * /
  gepuffert() {
    return createTimeRange(0, 0);
  }

  /**
   * Ruft den Prozentsatz des aktuellen Videos ab, der derzeit gepuffert ist.
   *
   * @return {number}
   * Eine Zahl von 0 bis 1, die den Dezimalprozentsatz der
   * Video, das gepuffert ist.
   *
   * /
  bufferedPercent() {
    gib bufferedPercent (this.buffered (), this.duration_) zurück;
  }

  /**
   * Deaktivieren Sie das Polyfill für "Fortschritts"-Ereignisse, das in
   * {@link Tech#manualProgressOn}
   * Beenden Sie die manuelle Verfolgung von Fortschrittsereignissen, indem Sie das eingestellte Intervall löschen
   * {@link Technik #trackProgress}.
   * /
  stopTrackingProgress () {
    this.clearInterval (this.ProgressInterval);
  }

  /**
   * Füllen Sie das `timeupdate`-Ereignis für Browser, die es nicht unterstützen, mit Polyfill aus.
   *
   * @see {@link Technik #trackCurrentTime}
   * /
  ManualTimeUpdatesOn () {
    this.manualTimeUpdates = wahr;

    this.on ('abspielen', this.trackCurrentTime_);
    this.on ('pause', this.stopTrackingCurrentTime_);
  }

  /**
   * Schalten Sie das Polyfill für `timeupdate`-Ereignisse aus, das in erstellt wurde
   * {@link Technik #manualTimeUpdatesOn}
   * /
  ManualTimeUpdatesOff () {
    this.manualTimeUpdates = falsch;
    this.stopTrackingCurrentTime();
    this.off ('abspielen', this.trackCurrentTime_);
    this.off ('pause', this.stopTrackingCurrentTime_);
  }

  /**
   * Richtet eine Intervallfunktion ein, um die aktuelle Uhrzeit zu verfolgen und jedes Mal ein `timeupdate` auszulösen
   * 250 Millisekunden.
   *
   * @listens Tech#play
   * @triggers Technik #timeupdate
   * /
  TrackCurrentTime () {
    wenn (this.currentTimeInterval) {
      this.stopTrackingCurrentTime();
    }
    this.currentTimeInterval = this.setInterval (function () {
      /**
       * Wird in einem Intervall von 250 ms ausgelöst, um anzuzeigen, dass im Video Zeit vergeht.
       *
       * @event Tech#timeupdate
       * @Typ {EventTarget~Event}
       * /
      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });

    //42 = 24 fps//250 verwendet Webkit//FF verwendet 15
    }, 250);
  }

  /**
   * Stoppen Sie die in {@link Tech #trackCurrentTime} erstellte Intervallfunktion, sodass
   * Das Ereignis `timeupdate` wird nicht mehr ausgelöst.
   *
   * @listens {Tech #pause}
   * /
  stopTrackingCurrentTime () {
    this.clearInterval (this.currentTimeInterval);

    //#1002 - wenn das Video kurz vor dem nächsten Update endet,
    //der Fortschrittsbalken wird es nicht bis zum Ende schaffen
    this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  }

  /**
   * Schalten Sie alle Event-Polyfills aus, löschen Sie die `Tech`s {@link AudioTrackList},
   * {@link videoTrackList} und {@link textTrackList} und verfügen über diese Technologie.
   *
   * @feuert Komponente#entsorgen
   * /
  dispose() {

    // alle Spuren löschen, da wir sie nicht zwischen den Techs wiederverwenden können
    this.clearTracks (track_types.normal.Names);

    //Deaktiviere jegliche manuelle Fortschritts- oder Zeitaktualisierungsverfolgung
    wenn (this.manualProgress) {
      this.manualProgressOff ();
    }

    if (this.manualTimeUpdates) {
      this.manualTimeUpdatesOff ();
    }

    super.dispose();
  }

  /**
   * Lösche eine einzelne `TrackList` oder ein Array von `TrackLists` mit ihren Namen.
   *
   * > Anmerkung: Techniker ohne Quellhandler sollten dies zwischen den Quellen für `Video` aufrufen
   * & `Audio` Titel. Du willst sie nicht zwischen Tracks benutzen!
   *
   * @param {string [] |string} -Typen
   * Zu löschende TrackList-Namen, gültige Namen sind `Video`, `Audio` und
   * `Text`.
   * /
  ClearTracks (Typen) {
    typen = [] .concat (Typen);
    // alle Spuren löschen, da wir sie nicht zwischen den Techs wiederverwenden können
    types.forEach (Typ) => {
      const list = this [`$ {type} Tracks`] () || [];
      let i = list.length;

      while (i--) {
        const track = list[i];

        if (type === 'text') {
          this.removeRemoteTextTrack(track);
        }
        list.removeTrack (verfolgen);
      }
    });
  }

  /**
   * Entferne alle TextTracks, die über addRemoteTextTrack hinzugefügt wurden und die
   * markiert für automatische Müllabfuhr
   * /
  AutoTextTracks aufräumen () {
    const list = this.autoRemoteTextTracks_ || [];
    let i = list.length;

    while (i--) {
      const track = list[i];

      this.removeRemoteTextTrack(track);
    }
  }

  /**
   * Setzen Sie die Technologie zurück, wodurch alle Quellen entfernt und der interne ReadyState zurückgesetzt wird.
   *
   * @Abstrakt
   * /
  zurücksetzen () {}

  /**
   * Holen Sie sich den Wert von `crossOrigin` vom Techniker.
   *
   * @Abstrakt
   *
   * @see {Html5 #crossOrigin}
   * /
  CrossOrigin () {}

  /**
   * Stellen Sie den Wert von `CrossOrigin` für die Technologie ein.
   *
   * @Abstrakt
   *
   * @param {string} crossOrigin der crossOrigin-Wert
   * @see {Html5 #setCrossOrigin}
   * /
  setCrossOrigin () {}

  /**
   * Erhalte einen Fehler auf dem Tech oder stelle ihn ein.
   *
   * @param {MediaError} [Fehler]
   * Fehler beim Einstellen der Technik
   *
   * @return {MediaError|null}
   * Das aktuelle Fehlerobjekt auf der Technologie oder null, wenn es keins gibt.
   * /
  error(err) {
    wenn (irr! == undefiniert) {
      this.error_ = new MediaError(err);
      this.trigger('error');
    }
    gib das.error_ zurück;
  }

  /**
   * Gibt die `TimeRange`s zurück, die für die aktuelle Quelle abgespielt wurden.
   *
   * > HINWEIS: Diese Implementierung ist unvollständig. Der abgespielte `TimeRange` wird nicht aufgezeichnet.
   * Es wird nur geprüft, ob die Quelle überhaupt abgespielt hat oder nicht.
   *
   * @return {TimeRange}
   * - Ein einzelner Zeitraum, wenn dieses Video abgespielt wurde
   * - Ein leerer Satz von Bereichen, falls nicht.
   * /
  gespielt() {
    if (this.hasStarted_) {
      return createTimeRange(0, 0);
    }
    gib createTimeRange () zurück;
  }

  /**
   * Wiedergabe starten
   *
   * @Abstrakt
   *
   * @see {Html5 #play}
   * /
  abspielen () {}

  /**
   * Stellen Sie ein, ob wir schrubben oder nicht
   *
   * @Abstrakt
   *
   * @see {Html5 #setScrubbing}
   * /
  setScrubbing () {}

  /**
   * Finde heraus, ob wir schrubben oder nicht
   *
   * @Abstrakt
   *
   * @see {Html5 #scrubbing}
   * /
  schrubben () {}

  /**
   * Führt zu einer manuellen Zeitaktualisierung, wenn {@link Tech #manualTimeUpdatesOn}
   * zuvor angerufen.
   *
   * @fires Technik #timeupdate
   * /
  setCurrentTime () {
    //die Genauigkeit manueller Zeitaktualisierungen verbessern
    if (this.manualTimeUpdates) {
      /**
       * Ein manuelles `timeupdate`-Ereignis.
       *
       * @event Tech#timeupdate
       * @Typ {EventTarget~Event}
       * /
      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
    }
  }

  /**
   * Schalten Sie die Listener für {@link videoTrackList}, {@link {AudioTrackList} ein und
   * {@link textTrackList} Ereignisse.
   *
   * Dadurch wird {@link eventTarget~eventListeners} für `addtrack` und `removetrack` hinzugefügt.
   *
   * @fires Technik #audiotrackchange
   * @fires Technik #videotrackchange
   * @fires Technik #texttrackchange
   * /
  initTrackListener () {
    /**
      * Wird ausgelöst, wenn Titel auf der Tech {@link AudioTrackList} hinzugefügt oder entfernt werden
      *
      * @event Technik #audiotrackchange
      * @Typ {EventTarget~Event}
      * /

    /**
      * Wird ausgelöst, wenn Tracks auf der Tech {@link videoTrackList} hinzugefügt oder entfernt werden
      *
      * @event Technik #videotrackchange
      * @Typ {EventTarget~Event}
      * /

    /**
      * Wird ausgelöst, wenn Tracks auf der Tech {@link textTrackList} hinzugefügt oder entfernt werden
      *
      * @event Technik #texttrackchange
      * @Typ {EventTarget~Event}
      * /
    track_types.normal.names.forEach ((name) => {
      const props = TRACK_TYPES.NORMAL [Name];
      const trackListChanges = () => {
        this.trigger (`$ {name} trackchange`);
      };

      const tracks = this [props.getterName] ();

      tracks.addEventListener ('Track entfernen', tracklistChanges);
      tracks.addEventListener ('addTrack', tracklistChanges);

      this.on('dispose', () => {
        tracks.removeEventListener ('Track entfernen', trackListChanges);
        tracks.removeEventListener ('Track hinzufügen', trackListChanges);
      });
    });
  }

  /**
   * Emulieren Sie TextTracks mit vtt.js, falls erforderlich
   *
   * @fires Technik #vttjsloaded
   * @fires Technik #vttjserror
   * /
  WebVTTScript_ () {hinzufügen
    wenn (window.webVTT) {
      rückkehr;
    }

    //Anfänglich ist Tech.el_ ein Kind eines Dummy-Div warte bis das Komponentensystem
    //signalisiert, dass der Tech bereit ist. Zu diesem Zeitpunkt ist Tech.el_ Teil des DOM
    //vor dem Einfügen des WebVTT-Skripts
    wenn (document.body.contains (this.el ())) {

      //lade über require, falls verfügbar und der Skriptort vtt.js wurde nicht übergeben
      //als Option. novtt builds wandelt den obigen Reque-Aufruf in ein leeres Objekt um
      //was dazu führt, dass die Überprüfung immer fehlschlägt.
      wenn (! this.options_ ['vtt.js'] && isPlain (vtt) && Object.keys (vtt) .length > 0) {
        this.trigger('vttjsloaded');
        rückkehr;
      }

      //lade vtt.js über die Script-Location-Option oder das CDN, wenn kein Speicherort vorhanden war
      //übergeben
      const script = document.createElement ('Skript');

      script.src = this.options_ ['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
      script.onload = () => {
        /**
         * Wird ausgelöst, wenn vtt.js geladen wird.
         *
         * @event Tech#vttjsloaded
         * @Typ {EventTarget~Event}
         * /
        this.trigger('vttjsloaded');
      };
      script.onerror = () => {
        /**
         * Wird ausgelöst, als vtt.js aufgrund eines Fehlers nicht geladen wurde
         *
         * @event Tech#vttjsloaded
         * @Typ {EventTarget~Event}
         * /
        this.trigger ('vttjs-Fehler');
      };
      this.on('dispose', () => {
        script.onload = null;
        script.onerror = null;
      });
      //haben es aber noch nicht geladen und wir haben es vor dem Inject auf true gesetzt, damit
      //wir überschreiben das eingefügte window.webVTT nicht, wenn es sofort geladen wird
      window.webVTT = wahr;
      this.el () .parentNode.appendChild (Skript);
    } sonst {
      this.ready (this.addWebVTTScript_);
    }

  }

  /**
   * Texttracks emulieren
   *
   * /
  emulieren Sie TextTracks () {
    const tracks = this.textTracks ();
    const remoteTracks = this.remoteTextTracks ();
    const handleAddTrack = (e) => tracks.addTrack (e.track);
    const handleRemoveTrack = (e) => tracks.removeTrack (e.track);

    remoteTracks.on ('addtrack', handleAddTrack);
    remoteTracks.on ('removeTrack', handleRemoveTrack);

    Das.addWebVTTScript_ ();

    const updateDisplay = () => this.trigger ('texttrackchange');

    const textTracksChanges = () => {
      Anzeige aktualisieren ();

      for (let i = 0; i < tracks.length; i++) {
        const track = tracks[i];

        track.removeEventListener('cuechange', updateDisplay);
        if (track.mode === 'showing') {
          track.addEventListener ('cuechange', updateDisplay);
        }
      }
    };

    TextTrack-Änderungen ();
    tracks.addEventListener ('ändern', textTracksChanges);
    tracks.addEventListener ('addTrack', textTracksChanges);
    tracks.addEventListener ('Track entfernen', textTracksChanges);

    this.on('dispose', function() {
      remoteTracks.off ('addtrack', handleAddTrack);
      removeTracks.off ('removeTrack', handleRemoveTrack);
      tracks.removeEventListener ('ändern', textTracksChanges);
      tracks.removeEventListener ('Track hinzufügen', textTracksChanges);
      tracks.removeEventListener ('removeTrack', textTracksChanges);

      for (let i = 0; i < tracks.length; i++) {
        const track = tracks[i];

        track.removeEventListener('cuechange', updateDisplay);
      }
    });
  }

  /**
   * Erstellt ein entferntes {@link TextTrack}-Objekt und gibt es zurück.
   *
   * @param {string} kind
   *        textTrack"-Typ (Untertitel, Untertitel, Beschreibungen, Kapitel oder Metadaten)
   *
   * @param {string} [label]
   *        Label zur Kennzeichnung der Textspur
   *
   * @param {string} [Sprache]
   *        Sprachabkürzung mit zwei Buchstaben
   *
   * @return {TextTrack}
   *         Die TextSpur, die erstellt wird.
   * /
  addTextTrack(kind, label, language) {
    wenn (! nett) {
      einen neuen Fehler auslösen ('Die Art von TextTrack ist erforderlich, wurde aber nicht bereitgestellt');
    }

    return createTrackHelper (this, kind, label, language);
  }

  /**
   * Erstellen Sie einen emulierten TextTrack zur Verwendung durch addRemoteTextTrack
   *
   * Dies soll durch Klassen außer Kraft gesetzt werden, die von
   * Technologie, um native oder benutzerdefinierte TextTracks zu erstellen.
   *
   * @param {Object} options
   *        Das Objekt sollte die Optionen enthalten, mit denen der TextTrack initialisiert werden soll.
   *
   * @param {string} [options.kind]
   *        textTrack"-Typ (Untertitel, Untertitel, Beschreibungen, Kapitel oder Metadaten).
   *
   * @param {string} [options.label].
   *        Label zur Kennzeichnung der Textspur
   *
   * @param {string} [options.language]
   *        Sprachabkürzung mit zwei Buchstaben.
   *
   * @return {HTMLTrackElement}
   *         Das Spurelement, das erstellt wird.
   * /
  createRemoteTextTrack(options) {
    const track = mergeOptions (Optionen, {
      Technik: das
    });

    gib das neue track_types.remote.remoteText.trackClass (track) zurück;
  }

  /**
   * Erzeugt ein entferntes Text-Track-Objekt und gibt ein HTML-Track-Element zurück.
   *
   * > Anmerkung: Dies kann ein emuliertes {@link HtmlTrackElement} oder ein natives sein.
   *
   * @param {Object} options
   * Siehe {@link Tech #createRemoteTextTrack} für detailliertere Eigenschaften.
   *
   * @param {boolean} [manualCleanup=Wahr]
   * - Wenn falsch: Der TextTrack wird automatisch aus dem Video entfernt
   * Element immer dann, wenn sich die Quelle ändert
   * - Wenn wahr: Der TextTrack muss manuell bereinigt werden
   *
   * @return {HTMLTrackElement}
   * Ein HTML-Track-Element.
   *
   * @deprecated Die Standardfunktionalität für diese Funktion wird gleichwertig sein
   * in Zukunft auf „manualCleanup=False“. Der ManualCleanup-Parameter wird
   * ebenfalls entfernt werden.
   * /
  addRemoteTextTrack (Optionen = {}, ManualCleanup) {
    const htmlTrackElement = this.createRemoteTextTrack (Optionen);

    wenn (ManualCleanup! == wahr & ManualCleanup! == falsch) {
      //Warnung vor Verfall
      log.warn ('Der Aufruf von addRemoteTextTrack, ohne den Parameter „ManualCleanup“ explizit auf `true` zu setzen, ist veraltet und wird in zukünftigen Versionen von video.js standardmäßig auf `false` gesetzt.);
      ManualCleanup = wahr;
    }

    // HTMLTrackElement und TextTrack in entfernter Liste speichern
    this.remoteTextTrackels () .addTrackElement_ (htmlTrackElement);
    this.remoteTextTracks () .addTrack (htmlTrackElement.track);

    wenn (ManualCleanup! == wahr) {
      //erstelle die TextTrackList falls sie nicht existiert
      this.ready () => this.autoRemoteTextTracks_.addTrack (htmlTrackElement.track));
    }

    return htmlTrackElement;
  }

  /**
   * Entferne einen Remote-Texttrack aus der Remote-`TextTrackList`.
   *
   * @param {TextTrack} track
   * `TextTrack` zum Entfernen aus der `TextTrackList`
   * /
  removeRemoteTextTrack(track) {
    const trackElement = this.remoteTextTrackels () .getTrackElementByTrack_ (track);

    //entferne HtmlTrackElement und TextTrack aus der Remote-Liste
    this.remoteTextTrackels () .removeTrackElement_ (trackElement);
    this.remoteTextTracks () .removeTrack (track);
    this.autoRemoteTextTracks_.removeTrack (track);
  }

  /**
   * Ruft verfügbare Metriken zur Qualität der Medienwiedergabe ab, wie sie vom W3C's Media
   * Wiedergabequalität API.
   *
   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
   *
   * @return {Object}
   *         Ein Objekt mit unterstützten Qualitätsmetriken für die Medienwiedergabe
   *
   * @Abstrakt
   * /
  getVideoPlaybackQuality() {
    kehre {} zurück;
  }

  /**
   * Versuchen Sie, ein schwebendes Videofenster immer über anderen Fenstern zu erstellen
   * damit Benutzer weiterhin Medien konsumieren können, während sie mit anderen interagieren
   * Inhaltsseiten oder Anwendungen auf ihrem Gerät.
   *
   * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
   *
   * @return {Versprechen|undefiniert}
   * Ein Versprechen mit einem Bild-im-Bild-Fenster, sofern der Browser das unterstützt
   * Versprechen (oder eines wurde optional abgegeben). Es gibt undefiniert zurück
   *         sonst.
   *
   * @Abstrakt
   * /
  requestPictureInPicture() {
    const PromiseClass = this.options_.Promise || window.Promise;

    if (PromiseClass) {
      gib promiseClass.reject () zurück;
    }
  }

  /**
   * Eine Methode, um den Wert der <video>-Eigenschaft 'disablePictureInPicture' zu überprüfen.
   * Der Standardwert ist true, da es als deaktiviert betrachtet werden sollte, wenn der Techniker Pip nicht unterstützt
   *
   * @Abstrakt
   * /
  DisablePictureInPicture () {
    true zurückgeben;
  }

  /**
   * Eine Methode, um die <video>-Eigenschaft 'disablePictureInPicture' zu setzen oder zu deaktivieren.
   *
   * @Abstrakt
   * /
  setDisablePictureInPicture () {}

  /**
   * Eine Fallback-Implementierung von RequestVideoFrameCallback mit RequestAnimationFrame
   *
   * @param {function} cb
   * @return {number} Anfrage-ID
   * /
  requestVideoFrameCallback(cb) {
    const id = GUID.newGUID ();

    wenn (! this.isReady_ || this.pausiert () {
      this.queuedHanders_.add (id);
      this.one ('spielt', () => {
        if (this.queuedHanders_.has(id)) {
          this.queuedHanders_.delete(id);
          cb();
        }
      });
    } sonst {
      this.requestNamedAnimationFrame (id, cb);
    }

    return id;
  }

  /**
   * Eine Fallback-Implementierung von CancelVideoFrameCallback
   *
   * @param {number} id ID des Rückrufs, der storniert werden soll
   * /
  cancelVideoFrameCallback(id) {
    if (this.queuedHanders_.has(id)) {
      this.queuedHanders_.delete(id);
    } sonst {
      this.cancelNamedAnimationFrame (id);
    }
  }

  /**
   * Eine Methode, um ein Poster aus einem `Tech` zu erstellen.
   *
   * @Abstrakt
   * /
  setPoster () {}

  /**
   * Eine Methode, um zu überprüfen, ob das <Video>-Attribut 'playsinline' vorhanden ist.
   *
   * @Abstrakt
   * /
  spielt online () {}

  /**
   * Eine Methode, um das <Video>-Attribut 'playsinline' zu setzen oder zu deaktivieren.
   *
   * @Abstrakt
   * /
  setPlaysInline () {}

  /**
   * Versuch, die Überschreibung der nativen Audiospuren zu erzwingen.
   *
   * @param {boolean} override - Wenn auf true gesetzt, wird das native Audio außer Kraft gesetzt,
   * andernfalls wird möglicherweise natives Audio verwendet.
   *
   * @Abstrakt
   * /
  NativeAudioTracks überschreiben () {}

  /**
   * Versuch, die Überschreibung der nativen Videospuren zu erzwingen.
   *
   * @param {boolean} override - Wenn auf true gesetzt, wird natives Video außer Kraft gesetzt,
   * andernfalls wird möglicherweise natives Video verwendet.
   *
   * @Abstrakt
   * /
  NativeVideoTracks () überschreiben {}

  /*
   * Prüfen Sie, ob der Techniker den angegebenen MIME-Typ unterstützen kann.
   *
   * Die Basistechnologie unterstützt keinen Typ, aber Quellbearbeiter können
   * überschreiben.
   *
   * @param {string} type
   * Der Mimetype, um nach Unterstützung zu suchen
   *
   * @return {string}
   * 'wahrscheinlich', 'vielleicht' oder leere Zeichenfolge
   *
   * @see [Spezifikation] {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
   *
   * @Abstrakt
   * /
  canPlayType () {
    zurückgeben '';
  }

  /**
   * Prüfen Sie, ob der Typ von dieser Technologie unterstützt wird.
   *
   * Die Basistechnologie unterstützt keinen Typ, aber Quellbearbeiter können
   * überschreiben.
   *
   * @param {string} type
   * Der zu prüfende Medientyp
   * @return {string} Gibt die Antwort des nativen Videoelements zurück
   * /
  statischer canPlayType () {
    zurückgeben '';
  }

  /**
   * Prüfen Sie, ob der Techniker die angegebene Quelle unterstützen kann
   *
   * @param {Object} srcObj
   *        Das Quellobjekt
   * @param {Object} options
   *        Die an die Technik übergebenen Optionen
   * @return {string} 'wahrscheinlich', 'vielleicht', oder '' (leere Zeichenkette)
   * /
  statisch canPlaySource (srcoBJ, Optionen) {
    gib tech.canPlayType (srcobj.Type) zurück;
  }

  /*
   * Gibt an, ob das Argument ein Tech ist oder nicht.
   * Es kann entweder eine Klasse wie `Html5` oder eine Instanz wie `player.tech_` übergeben werden
   *
   * @param {Object} component
   * Der zu prüfende Artikel
   *
   * @return {boolean}
   * Ob es sich um eine Technologie handelt oder nicht
   * - Stimmt, wenn es sich um eine Technologie handelt
   * - Falsch, wenn nicht
   * /
  statisches iTech (Komponente) {
    gib component.prototype instanceof Tech zurück ||
           Komponenteninstanz von Tech ||
           Komponente === Technik;
  }

  /**
   * Registriert einen `Tech` in eine gemeinsame Liste für videojs.
   *
   * @param {string} name
   * Name des `Tech`, der registriert werden soll.
   *
   * @param {Object} -Technologie
   * Die Klasse `Tech`, die registriert werden soll.
   * /
  statisches RegisterTech (Name, Technologie) {
    wenn (! Technik.technik_) {
      tech.techs_ = {};
    }

    wenn (! Tech.iTech (Technik) {
      einen neuen Fehler auslösen (`Tech $ {name} muss ein Techniker sein`);
    }

    wenn (! tech.canPlayType) {
      einen neuen Fehler auslösen ('Techniker müssen eine statische CanPlayType-Methode verwenden');
    }
    wenn (! tech.canplayQuelle) {
      einen neuen Fehler auslösen ('Techniker müssen eine statische CanPlaySource-Methode haben. ');
    }

    name = toTitleCase(name);

    tech.techs_ [Name] = Technik;
    tech.techs_ [toLowerCase (Name)] = Technik;
    wenn (Name! == 'Technik') {
      //camel case der TechName zur Verwendung in TechOrder
      tech.defaultTechOrder_.push (Name);
    }
    Technologie zurückgeben;
  }

  /**
   * Holen Sie sich einen `Tech` aus der geteilten Liste mit Namen.
   *
   * @param {string} name
   * `CamelCase` oder `TitleCase` Name des Tech, der abgerufen werden soll
   *
   * @return {tech|undefiniert}
   * Das `Tech` oder undefined, wenn es keinen Techniker mit dem angeforderten Namen gab.
   * /
  statisches GetTech (Name) {
    if (!name) {
      rückkehr;
    }

    wenn (Tech.techs_ && Tech.techs_ [Name]) {
      gib tech.techs_ [name] zurück;
    }

    name = toTitleCase(name);

    wenn (Fenster && Fenstervideojs && Fenstervideojs [Name]) {
      log.warn (`Die $ {name} -Technologie wurde dem videojs-Objekt hinzugefügt, obwohl es mit videoJS.registerTech (name, tech) registriert werden sollte `);
      gib window.videojs [Name] zurück;
    }
  }
}

/**
 * Abrufen der {@link VideoTrackList}
 *
 * @returns {videoTrackList}
 * @method tech.prototype.VideoTracks
 * /

/**
 * Abrufen der {@link AudioTrackList}
 *
 * @returns {AudioTrackList}
 * @method tech.prototype.AudioTracks
 * /

/**
 * Abrufen der {@link TextTrackList}
 *
 * @returns {TextTrackList}
 * @method tech.prototype.TextTracks
 * /

/**
 * Holen Sie sich das Remote-Element {@link textTrackList}
 *
 * @returns {TextTrackList}
 * @method tech.prototype.remoteTextTracks
 * /

/**
 * Holen Sie sich das Remote-Element {@link htmlTrackElementList}
 *
 * @returns {htmlTrackElementList}
 * @method tech.prototype.remoteTextTrackels
 * /

track_types.all.names.forEach (Funktion (Name) {
  const props = TRACK_TYPES.ALL[name];

  tech.prototype [props.getterName] = Funktion () {
    this[props.privateName] = this[props.privateName] || new props.ListClass();
    return this[props.privateName];
  };
});

/**
 * Liste der zugehörigen Texttracks
 *
 * @type {TextTrackList}
 * @privat
 * @property Technik #textTracks_
 * /

/**
 * Liste der zugehörigen Audiotracks.
 *
 * @type {AudioTrackList}
 * @privat
 * @property Technik #audioTracks_
 * /

/**
 * Liste der zugehörigen Videotracks.
 *
 * @type {videoTrackList}
 * @privat
 * @property Technik #videoTracks_
 * /

/**
 * Boolescher Wert, der angibt, ob das "Tech" die Lautstärkeregelung unterstützt.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featuresVolumeControl = wahr;

/**
 * Boolescher Wert, der angibt, ob das "Tech" die Stummschaltung der Lautstärke unterstützt.
 *
 * @Typ {bolean}
 * @default
 * /
tech.prototype.featuresMuteControl = wahr;

/**
 * Boolescher Wert, der angibt, ob `Tech` die Größenänderung im Vollbildmodus unterstützt.
 * Das Ändern der Größe von Plugins mithilfe von Request Fullscreen lädt das Plugin neu
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featuresFullScreenResize = falsch;

/**
 * Boolescher Wert, der angibt, ob der `Tech` das Ändern der Geschwindigkeit unterstützt, mit der das Video aufgenommen wird
 * spielt. Beispiele:
 *   - Player so einstellen, dass er 2x (doppelt) so schnell spielt
 *   - Player so einstellen, dass er 0,5x (halb) so schnell spielt
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featurePlaybackRate = falsch;

/**
 * Boolescher Wert, der angibt, ob das `Tech` das `Progress`-Ereignis unterstützt. Dies ist derzeit
 * nicht durch video-js-swf ausgelöst. Auf diese Weise wird festgestellt, ob
 * {@link Tech #manualProgressOn} sollte aufgerufen werden.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featureProgressEvents = falsch;

/**
 * Boolescher Wert, der angibt, ob das "Tech" das Ereignis "Sourceset" unterstützt.
 *
 * Ein Techniker sollte dies auf `true` setzen und dann {@link Tech #triggerSourceset} verwenden
 * um ein {@link Tech #event:sourceset} zum frühestmöglichen Zeitpunkt nach dem Abrufen auszulösen
 * eine neue Quelle.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.FeatureResourceSet = falsch;

/**
 * Boolescher Wert, der angibt, ob `Tech` das `timeupdate`-Ereignis unterstützt. Dies ist derzeit
 * nicht durch video-js-swf ausgelöst. Auf diese Weise wird festgestellt, ob
 * {@link Tech #manualTimeUpdates} sollte aufgerufen werden.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featurestimeUpdateEvents = falsch;

/**
 * Boolescher Wert, der angibt, ob der `Tech` den nativen `TextTrack`s unterstützt.
 * Dies hilft uns bei der Integration mit nativen `TextTrack`s, sofern der Browser sie unterstützt.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featuresNativeTextTracks = falsch;

/**
 * Boolescher Wert, der angibt, ob der `Tech` `RequestVideoFrameCallback` unterstützt.
 *
 * @Typ {boolean}
 * @default
 * /
tech.prototype.featuresVideoFrameCallback = falsch;

/**
 * Ein funktionales Mixin für Techniker, die das Source Handler-Pattern verwenden möchten.
 * Quellhandler sind Skripte für den Umgang mit bestimmten Formaten.
 * Das Quellhandlermuster wird für adaptive Formate (HLS, DASH) verwendet, die
 * manuelles Laden von Videodaten und Einspeisen in einen Quellpuffer (Media Source Extensions)
 * Beispiel: `tech.withSourceHandlers.Call (myTech); `
 *
 * @param {Technik} _Technik
 * Die Technologie zum Hinzufügen von Quellhandler-Funktionen.
 *
 * @mixes Tech~SourceHandlerAdditions
 * /
tech.withSourceHandlers = Funktion (_Tech) {

  /**
   * Registrieren Sie einen Quellhandler
   *
   * @param {Function} -Handler
   * Die Source-Handler-Klasse
   *
   * @param {Zahl} [Index]
   * Registriere es im folgenden Index
   * /
  _tech.registerSourceHandler = Funktion (Handler, Index) {
    let-Handler = _tech.SourceHandlers;

    if (!handlers) {
      Handler = _tech.SourceHandlers = [];
    }

    if (index === undefiniert) {
      //an das Ende der Liste hinzufügen
      index = Handlers.length;
    }

    handlers.splice (Index, 0, Handler);
  };

  /**
   * Prüfen Sie, ob der Techniker den angegebenen Typ unterstützen kann. Überprüft auch die
   * Tech SourceHandlers.
   *
   * @param {string} type
   * Der zu prüfende Mimetyp.
   *
   * @return {string}
   *         'wahrscheinlich', 'vielleicht', oder '' (leere Zeichenfolge)
   * /
  _tech.canPlayType = Funktion (Typ) {
    const handlers = _Tech.sourceHandlers || [];
    können;

    for (let i = 0; i < handlers.length; i++) {
      can = Handler [i] .canPlayType (Typ);

      wenn (kann) {
        zurückgeben können;
      }
    }

    zurückgeben '';
  };

  /**
   * Gibt den ersten Quellhandler zurück, der die Quelle unterstützt.
   *
   * ALLES: Frage beantworten: sollte „wahrscheinlich“ vor „vielleicht“ priorisiert werden
   *
   * @param {Tech~SourceObject} source
   *        Das Quellobjekt
   *
   * @param {Object} options
   *        Die an die Technik übergebenen Optionen
   *
   * @return {sourceHandler|Null}
   * Der erste Quellhandler, der die Quelle unterstützt, oder null, wenn
   * kein SourceHandler unterstützt die Quelle
   * /
  _tech.selectSourceHandler = Funktion (Quelle, Optionen) {
    const handlers = _Tech.sourceHandlers || [];
    können;

    for (let i = 0; i < handlers.length; i++) {
      can = handlers [i] .canHandleSource (Quelle, Optionen);

      wenn (kann) {
        Return-Handler [i];
      }
    }

    null zurückgeben;
  };

  /**
   * Prüfen Sie, ob der Techniker die angegebene Quelle unterstützen kann.
   *
   * @param {Tech~SourceObject} srcObj
   *        Das Quellobjekt
   *
   * @param {Object} options
   *        Die an die Technik übergebenen Optionen
   *
   * @return {string}
   *         'wahrscheinlich', 'vielleicht', oder '' (leere Zeichenfolge)
   * /
  _tech.canPlaySource = Funktion (srcoBJ, Optionen) {
    const sh = _tech.SelectSourceHandler (srcObj, Optionen);

    wenn (sh) {
      gib sh.canHandleSource zurück (srcObj, Optionen);
    }

    zurückgeben '';
  };

  /**
   * Wenn Sie einen Quellhandler verwenden, bevorzugen Sie dessen Implementierung von
   * jede Funktion, die normalerweise vom Techniker bereitgestellt wird.
   * /
  const deferrable = [
    suchbar',
    suchen",
    'Dauer'
  ];

  /**
   * Ein Wrapper rund um {@link Tech #seekable}, der ein `SourceHandler`s seekable aufruft
   * Funktion, falls sie existiert, mit einem Fallback auf die suchbare Funktion von Techs.
   *
   * @method _tech.Seekable
   * /

  /**
   * Ein Wrapper rund um {@link Tech #duration}, der eine `SourceHandler`s duration aufruft
   * funktioniert, falls sie existiert, andernfalls wird auf die Duration-Funktion des Technikers zurückgegriffen.
   *
   * @method _tech.Dauer
   * /

  deferrable.forEach (function (fnName) {
    const originalFn = dieser [FNName];

    if (Typ des Original-FN! == 'Funktion') {
      rückkehr;
    }

    dies [fnName] = Funktion () {
      wenn (this.SourceHandler_ && this.SourceHandler_ [fnName]) {
        gib this.sourceHandler_ [fnName] .apply (this.SourceHandler_, Argumente) zurück;
      }
      gib originalFN.apply zurück (dies, Argumente);
    };
  }, _Tech.prototype);

  /**
   * Erstellen Sie eine Funktion zum Festlegen der Quelle mithilfe eines Quellobjekts
   * und Quellhandler.
   * Sollte niemals aufgerufen werden, es sei denn, ein Quellhandler wurde gefunden.
   *
   * @param {Tech~SourceObject} source
   * Ein Quellobjekt mit den Schlüsseln src und type
   * /
  _tech.prototype.setSource = Funktion (Quelle) {
    sei sh = _tech.SelectSourceHandler (source, this.options_);

    wenn (! sh) {
      //Fallen Sie auf einen nativen Source-Hander zurück, wenn Quellen nicht unterstützt werden
      //bewusst gesetzt
      wenn (_tech.nativeSourceHandler) {
        sh = _Tech.NativeSourceHandler;
      } sonst {
        log.error ('Kein Quellhandler für die aktuelle Quelle gefunden. ');
      }
    }

    //Lösche jeden existierenden Quell-Handler
    this.dispeSourceHandler ();
    this.off ('entsorgen', this.dispeSourceHandler_);

    wenn (sh! == _Tech.NativeSourceHandler) {
      this.currentSource_ = Quelle;
    }

    this.sourceHandler_ = sh.handleSource (source, this, this.options_);
    this.one ('entsorgen', this.disponeSourceHandler_);
  };

  /**
   * Bereinigen Sie alle vorhandenen SourceHandler und Listener, wenn der Tech entsorgt ist.
   *
   * @listens Technik #dispose
   * /
  _tech.prototype.dispeSourceHandler = Funktion () {
    //wenn wir eine Quelle haben und eine andere bekommen
    //dann laden wir etwas Neues
    //dann lösche alle unsere aktuellen Tracks
    if (this.currentSource_) {
      this.clearTracks (['Audio', 'Video']);
      this.currentSource_ = null;
    }

    //Autotext-Tracks immer bereinigen
    this.cleanupAutoTextTracks ();

    wenn (this.SourceHandler_) {

      wenn (this.SourceHandler_.dispose) {
        this.sourceHandler_.dispose ();
      }

      this.sourceHandler_ = null;
    }
  };

};

//Die Basisklasse Tech muss als Komponente registriert werden. Es ist das einzige
//Technologie, die als Komponente registriert werden kann.
component.registerComponent ('Technik', Technik);
tech.registerTech ('Technik', Technik);

/**
 * Eine Liste der Techniker, die zu TechOrder on Players hinzugefügt werden sollten
 *
 * @privat
 * /
tech.defaultTechOrder_ = [];

Standard-Tech exportieren;