Home Reference Source

src/controller/audio-track-controller.js

/*
 * audio track controller
*/

import Event from '../events';
import EventHandler from '../event-handler';
import { logger } from '../utils/logger';
import { ErrorTypes } from '../errors';

class AudioTrackController extends EventHandler {
  constructor (hls) {
    super(hls, Event.MANIFEST_LOADING,
      Event.MANIFEST_PARSED,
      Event.AUDIO_TRACK_LOADED,
      Event.ERROR);
    this.ticks = 0;
    this.ontick = this.tick.bind(this);
  }

  destroy () {
    this.cleanTimer();
    EventHandler.prototype.destroy.call(this);
  }

  cleanTimer () {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  tick () {
    this.ticks++;
    if (this.ticks === 1) {
      this.doTick();
      if (this.ticks > 1)
        setTimeout(this.tick, 1);

      this.ticks = 0;
    }
  }

  doTick () {
    this.updateTrack(this.trackId);
  }

  onError (data) {
    if (data.fatal && data.type === ErrorTypes.NETWORK_ERROR)
      this.cleanTimer();
  }

  onManifestLoading () {
    // reset audio tracks on manifest loading
    this.tracks = [];
    this.trackId = -1;
  }

  onManifestParsed (data) {
    let tracks = data.audioTracks || [];
    let defaultFound = false;
    this.tracks = tracks;
    this.hls.trigger(Event.AUDIO_TRACKS_UPDATED, { audioTracks: tracks });
    // loop through available audio tracks and autoselect default if needed
    let id = 0;
    tracks.forEach(track => {
      if (track.default && !defaultFound) {
        this.audioTrack = id;
        defaultFound = true;
        return;
      }
      id++;
    });
    if (defaultFound === false && tracks.length) {
      logger.log('no default audio track defined, use first audio track as default');
      this.audioTrack = 0;
    }
  }

  onAudioTrackLoaded (data) {
    if (data.id < this.tracks.length) {
      logger.log(`audioTrack ${data.id} loaded`);
      this.tracks[data.id].details = data.details;
      // check if current playlist is a live playlist
      if (data.details.live && !this.timer) {
        // if live playlist we will have to reload it periodically
        // set reload period to playlist target duration
        this.timer = setInterval(this.ontick, 1000 * data.details.targetduration);
      }
      if (!data.details.live && this.timer) {
        // playlist is not live and timer is armed : stopping it
        this.cleanTimer();
      }
    }
  }

  /** get alternate audio tracks list from playlist **/
  get audioTracks () {
    return this.tracks;
  }

  /** get index of the selected audio track (index in audio track lists) **/
  get audioTrack () {
    return this.trackId;
  }

  /** select an audio track, based on its index in audio track lists**/
  set audioTrack (audioTrackId) {
    if (this.trackId !== audioTrackId || this.tracks[audioTrackId].details === undefined)
      this.setAudioTrackInternal(audioTrackId);
  }

  setAudioTrackInternal (newId) {
    // check if level idx is valid
    if (newId >= 0 && newId < this.tracks.length) {
      // stopping live reloading timer if any
      this.cleanTimer();
      this.trackId = newId;
      logger.log(`switching to audioTrack ${newId}`);
      let audioTrack = this.tracks[newId],
        hls = this.hls,
        type = audioTrack.type,
        url = audioTrack.url,
        eventObj = { id: newId, type: type, url: url };
      hls.trigger(Event.AUDIO_TRACK_SWITCHING, eventObj);
      // check if we need to load playlist for this audio Track
      let details = audioTrack.details;
      if (url && (details === undefined || details.live === true)) {
        // track not retrieved yet, or live playlist we need to (re)load it
        logger.log(`(re)loading playlist for audioTrack ${newId}`);
        hls.trigger(Event.AUDIO_TRACK_LOADING, { url: url, id: newId });
      }
    }
  }

  updateTrack (newId) {
    // check if level idx is valid
    if (newId >= 0 && newId < this.tracks.length) {
      // stopping live reloading timer if any
      this.cleanTimer();
      this.trackId = newId;
      logger.log(`updating audioTrack ${newId}`);
      let audioTrack = this.tracks[newId], url = audioTrack.url;
      // check if we need to load playlist for this audio Track
      let details = audioTrack.details;
      if (url && (details === undefined || details.live === true)) {
        // track not retrieved yet, or live playlist we need to (re)load it
        logger.log(`(re)loading playlist for audioTrack ${newId}`);
        this.hls.trigger(Event.AUDIO_TRACK_LOADING, { url: url, id: newId });
      }
    }
  }
}

export default AudioTrackController;