| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 | import defaultValue from "./defaultValue.js";import defined from "./defined.js";import destroyObject from "./destroyObject.js";import Iso8601 from "./Iso8601.js";import JulianDate from "./JulianDate.js";/** * Synchronizes a video element with a simulation clock. * * @alias VideoSynchronizer * @constructor * * @param {Object} [options] Object with the following properties: * @param {Clock} [options.clock] The clock instance used to drive the video. * @param {HTMLVideoElement} [options.element] The video element to be synchronized. * @param {JulianDate} [options.epoch=Iso8601.MINIMUM_VALUE] The simulation time that marks the start of the video. * @param {Number} [options.tolerance=1.0] The maximum amount of time, in seconds, that the clock and video can diverge. * * @demo {@link https://sandcastle.cesium.com/index.html?src=Video.html|Video Material Demo} */function VideoSynchronizer(options) {  options = defaultValue(options, defaultValue.EMPTY_OBJECT);  this._clock = undefined;  this._element = undefined;  this._clockSubscription = undefined;  this._seekFunction = undefined;  this._lastPlaybackRate = undefined;  this.clock = options.clock;  this.element = options.element;  /**   * Gets or sets the simulation time that marks the start of the video.   * @type {JulianDate}   * @default Iso8601.MINIMUM_VALUE   */  this.epoch = defaultValue(options.epoch, Iso8601.MINIMUM_VALUE);  /**   * Gets or sets the amount of time in seconds the video's currentTime   * and the clock's currentTime can diverge before a video seek is performed.   * Lower values make the synchronization more accurate but video   * performance might suffer.  Higher values provide better performance   * but at the cost of accuracy.   * @type {Number}   * @default 1.0   */  this.tolerance = defaultValue(options.tolerance, 1.0);  this._seeking = false;  this._seekFunction = undefined;  this._firstTickAfterSeek = false;}Object.defineProperties(VideoSynchronizer.prototype, {  /**   * Gets or sets the clock used to drive the video element.   *   * @memberof VideoSynchronizer.prototype   * @type {Clock}   */  clock: {    get: function () {      return this._clock;    },    set: function (value) {      const oldValue = this._clock;      if (oldValue === value) {        return;      }      if (defined(oldValue)) {        this._clockSubscription();        this._clockSubscription = undefined;      }      if (defined(value)) {        this._clockSubscription = value.onTick.addEventListener(          VideoSynchronizer.prototype._onTick,          this        );      }      this._clock = value;    },  },  /**   * Gets or sets the video element to synchronize.   *   * @memberof VideoSynchronizer.prototype   * @type {HTMLVideoElement}   */  element: {    get: function () {      return this._element;    },    set: function (value) {      const oldValue = this._element;      if (oldValue === value) {        return;      }      if (defined(oldValue)) {        oldValue.removeEventListener("seeked", this._seekFunction, false);      }      if (defined(value)) {        this._seeking = false;        this._seekFunction = createSeekFunction(this);        value.addEventListener("seeked", this._seekFunction, false);      }      this._element = value;      this._seeking = false;      this._firstTickAfterSeek = false;    },  },});/** * Destroys and resources used by the object.  Once an object is destroyed, it should not be used. * * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. */VideoSynchronizer.prototype.destroy = function () {  this.element = undefined;  this.clock = undefined;  return destroyObject(this);};/** * Returns true if this object was destroyed; otherwise, false. * * @returns {Boolean} True if this object was destroyed; otherwise, false. */VideoSynchronizer.prototype.isDestroyed = function () {  return false;};VideoSynchronizer.prototype._trySetPlaybackRate = function (clock) {  if (this._lastPlaybackRate === clock.multiplier) {    return;  }  const element = this._element;  try {    element.playbackRate = clock.multiplier;  } catch (error) {    // Seek manually for unsupported playbackRates.    element.playbackRate = 0.0;  }  this._lastPlaybackRate = clock.multiplier;};VideoSynchronizer.prototype._onTick = function (clock) {  const element = this._element;  if (!defined(element) || element.readyState < 2) {    return;  }  const paused = element.paused;  const shouldAnimate = clock.shouldAnimate;  if (shouldAnimate === paused) {    if (shouldAnimate) {      element.play();    } else {      element.pause();    }  }  //We need to avoid constant seeking or the video will  //never contain a complete frame for us to render.  //So don't do anything if we're seeing or on the first  //tick after a seek (the latter of which allows the frame  //to actually be rendered.  if (this._seeking || this._firstTickAfterSeek) {    this._firstTickAfterSeek = false;    return;  }  this._trySetPlaybackRate(clock);  const clockTime = clock.currentTime;  const epoch = defaultValue(this.epoch, Iso8601.MINIMUM_VALUE);  let videoTime = JulianDate.secondsDifference(clockTime, epoch);  const duration = element.duration;  let desiredTime;  const currentTime = element.currentTime;  if (element.loop) {    videoTime = videoTime % duration;    if (videoTime < 0.0) {      videoTime = duration - videoTime;    }    desiredTime = videoTime;  } else if (videoTime > duration) {    desiredTime = duration;  } else if (videoTime < 0.0) {    desiredTime = 0.0;  } else {    desiredTime = videoTime;  }  //If the playing video's time and the scene's clock time  //ever drift too far apart, we want to set the video to match  const tolerance = shouldAnimate ? defaultValue(this.tolerance, 1.0) : 0.001;  if (Math.abs(desiredTime - currentTime) > tolerance) {    this._seeking = true;    element.currentTime = desiredTime;  }};function createSeekFunction(that) {  return function () {    that._seeking = false;    that._firstTickAfterSeek = true;  };}export default VideoSynchronizer;
 |