Source: gfx/core/textures/VideoBaseTexture.js

const BaseTexture = require('./BaseTexture');
const utils = require('../utils');

/**
 * A texture of a [playing] Video.
 *
 * Video base textures mimic Pixi BaseTexture.from.... method in their creation process.
 *
 * This can be used in several ways, such as:
 *
 * ```js
 * var texture = PIXI.VideoBaseTexture.fromUrl('http://mydomain.com/video.mp4');
 *
 * var texture = PIXI.VideoBaseTexture.fromUrl({ src: 'http://mydomain.com/video.mp4', mime: 'video/mp4' });
 *
 * var texture = PIXI.VideoBaseTexture.fromUrls(['/video.webm', '/video.mp4']);
 *
 * var texture = PIXI.VideoBaseTexture.fromUrls([
 *     { src: '/video.webm', mime: 'video/webm' },
 *     { src: '/video.mp4', mime: 'video/mp4' }
 * ]);
 * ```
 *
 * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/).
 *
 * @class
 * @extends PIXI.BaseTexture
 * @memberof PIXI
 * @param source {HTMLVideoElement}
 * @param [scaleMode] {number} See {@link PIXI.SCALE_MODES} for possible values
 */
class VideoBaseTexture extends BaseTexture {
  constructor(source, scaleMode) {
    if (!source) {
      throw new Error('No video source element specified.');
    }

    // hook in here to check if video is already available.
    // BaseTexture looks for a source.complete boolean, plus width & height.

    if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) {
      source.complete = true;
    }

    super(source, scaleMode);

    /**
     * Should the base texture automatically update itself, set to true by default
     *
     * @member {boolean}
     * @default true
     */
    this.autoUpdate = false;

    this._onUpdate = this._onUpdate.bind(this);
    this._onCanPlay = this._onCanPlay.bind(this);

    if (!source.complete) {
      source.addEventListener('canplay', this._onCanPlay);
      source.addEventListener('canplaythrough', this._onCanPlay);

        // started playing..
      source.addEventListener('play', this._onPlayStart.bind(this));
      source.addEventListener('pause', this._onPlayStop.bind(this));
    }

    this.__loaded = false;
  }

  /**
   * The internal update loop of the video base texture, only runs when autoUpdate is set to true
   *
   * @private
   */
  _onUpdate() {
    if (this.autoUpdate) {
      window.requestAnimationFrame(this._onUpdate);
      this.update();
    }
  }

  /**
   * Runs the update loop when the video is ready to play
   *
   * @private
   */
  _onPlayStart() {
    if (!this.autoUpdate) {
      window.requestAnimationFrame(this._onUpdate);
      this.autoUpdate = true;
    }
  }

  /**
   * Fired when a pause event is triggered, stops the update loop
   *
   * @private
   */
  _onPlayStop() {
    this.autoUpdate = false;
  }

  /**
   * Fired when the video is loaded and ready to play
   *
   * @private
   */
  _onCanPlay() {
    this.hasLoaded = true;

    if (this.source) {
      this.source.removeEventListener('canplay', this._onCanPlay);
      this.source.removeEventListener('canplaythrough', this._onCanPlay);

      this.width = this.source.videoWidth;
      this.height = this.source.videoHeight;

      this.source.play();

          // prevent multiple loaded dispatches..
      if (!this.__loaded) {
        this.__loaded = true;
        this.emit('loaded', this);
      }
    }
  }

  /**
   * Destroys this texture
   *
   */
  destroy() {
    if (this.source && this.source._pixiId) {
      delete utils.BaseTextureCache[ this.source._pixiId ];
      delete this.source._pixiId;
    }

    BaseTexture.prototype.destroy.call(this);
  }
}

/**
 * Mimic Pixi BaseTexture.from.... method.
 *
 * @static
 * @param video {HTMLVideoElement}
 * @param scaleMode {number} See {@link PIXI.SCALE_MODES} for possible values
 * @return {PIXI.VideoBaseTexture}
 */
VideoBaseTexture.fromVideo = function(video, scaleMode) {
  if (!video._pixiId) {
    video._pixiId = 'video_' + utils.uid();
  }

  var baseTexture = utils.BaseTextureCache[video._pixiId];

  if (!baseTexture) {
    baseTexture = new VideoBaseTexture(video, scaleMode);
    utils.BaseTextureCache[ video._pixiId ] = baseTexture;
  }

  return baseTexture;
};

/**
 * Helper function that creates a new BaseTexture based on the given video element.
 * This BaseTexture can then be used to create a texture
 *
 * @static
 * @param videoSrc {string|object|string[]|object[]} The URL(s) for the video.
 * @param [videoSrc.src] {string} One of the source urls for the video
 * @param [videoSrc.mime] {string} The mimetype of the video (e.g. 'video/mp4'). If not specified
 *  the url's extension will be used as the second part of the mime type.
 * @param scaleMode {number} See {@link PIXI.SCALE_MODES} for possible values
 * @return {PIXI.VideoBaseTexture}
 */
VideoBaseTexture.fromUrl = function(videoSrc, scaleMode) {
  var video = document.createElement('video');

    // array of objects or strings
  if (Array.isArray(videoSrc)) {
    for (var i = 0; i < videoSrc.length; ++i) {
      video.appendChild(createSource(videoSrc[i].src || videoSrc[i], videoSrc[i].mime));
    }
  }
    // single object or string
  else {
    video.appendChild(createSource(videoSrc.src || videoSrc, videoSrc.mime));
  }

  video.load();
  video.play();

  return VideoBaseTexture.fromVideo(video, scaleMode);
};

VideoBaseTexture.fromUrls = VideoBaseTexture.fromUrl;

function createSource(path, type) {
  if (!type) {
    type = 'video/' + path.substr(path.lastIndexOf('.') + 1);
  }

  var source = document.createElement('source');

  source.src = path;
  source.type = type;

  return source;
}

module.exports = VideoBaseTexture;