Lots of jsdocs added

This commit is contained in:
Richard Davey 2019-10-10 17:42:57 +01:00
parent e3b4ec1b6f
commit 9b8bc99538

View file

@ -45,8 +45,7 @@ var VideoRender = require('./VideoRender');
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time. * @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
* @param {number} x - The horizontal position of this Game Object in the world. * @param {number} x - The horizontal position of this Game Object in the world.
* @param {number} y - The vertical position of this Game Object in the world. * @param {number} y - The vertical position of this Game Object in the world.
* @param {string} texture - The key of the Texture this Game Object will use to render with, as stored in the Texture Manager. * @param {string} [key] - The key of the Video this Game Object will use to render with, as stored in the Video Cache.
* @param {(string|integer)} [frame] - An optional frame from the Texture this Game Object is rendering with.
*/ */
var Video = new Class({ var Video = new Class({
@ -77,106 +76,173 @@ var Video = new Class({
GameObject.call(this, scene, 'Video'); GameObject.call(this, scene, 'Video');
/** /**
* @property {HTMLVideoElement} video - The HTML Video Element that is added to the document. * A reference to the HTML Video Element this Video Game Object is playing.
* Will be `null` until a video is loaded for playback.
*
* @name Phaser.GameObjects.Video#video
* @type {?HTMLVideoElement}
* @since 3.20.0
*/ */
this.video = null; this.video = null;
/**
* The Phaser Texture this Game Object is using to render the video to.
* Will be `null` until a video is loaded for playback.
*
* @name Phaser.GameObjects.Video#videoTexture
* @type {?Phaser.Textures.Texture}
* @since 3.20.0
*/
this.videoTexture = null; this.videoTexture = null;
/**
* A reference to the TextureSource belong to the `videoTexture` Texture object.
* Will be `null` until a video is loaded for playback.
*
* @name Phaser.GameObjects.Video#videoTextureSource
* @type {?Phaser.Textures.TextureSource}
* @since 3.20.0
*/
this.videoTextureSource = null; this.videoTextureSource = null;
/**
* A Phaser CanvasTexture instance that holds the most recent snapshot taken from the video.
* This will only be set if `snapshot` or `snapshotArea` have been called, and will be `null` until that point.
*
* @name Phaser.GameObjects.Video#snapshotTexture
* @type {?Phaser.Textures.CanvasTexture}
* @since 3.20.0
*/
this.snapshotTexture = null;
/**
* If you have saved this video to a texture via the `saveTexture` method, this controls if the video
* is rendered with `flipY` in WebGL or not. You often need to set this if you wish to use the video texture
* as the input source for a shader. If you find your video is appearing upside down within a shader or
* custom pipeline, flip this property.
*
* @name Phaser.GameObjects.Video#flipY
* @type {boolean}
* @since 3.20.0
*/
this.flipY = false;
/**
* The key used by the texture as stored in the Texture Manager.
*
* @name Phaser.GameObjects.Video#_key
* @type {string}
* @private
* @since 3.20.0
*/
this._key = UUID(); this._key = UUID();
/** /**
* @property {boolean} touchLocked - true if this video is currently locked awaiting a touch event. This happens on some mobile devices, such as iOS. * An internal flag holding the current state of the video lock, should document interaction be required
* @default * before playback can begin.
*
* @name Phaser.GameObjects.Video#touchLocked
* @type {boolean}
* @since 3.20.0
*/ */
this.touchLocked = true; this.touchLocked = true;
/** /**
* Start playing the video when it's unlocked. * Should the video auto play when document interaction is required and happens?
* @property {boolean} playWhenUnlocked *
* @default * @name Phaser.GameObjects.Video#playWhenUnlocked
* @type {boolean}
* @since 3.20.0
*/ */
this.playWhenUnlocked = false; this.playWhenUnlocked = false;
/** /**
* @property {integer} timeout - The amount of ms allowed to elapsed before the Video.onTimeout signal is dispatched while waiting for webcam access. * When starting playback of a video Phaser will monitor its `readyState` using a `setTimeout` call.
* @default * The `setTimeout` happens once every `Video.retryInterval` ms. It will carry on monitoring the video
*/
this.timeout = 15000;
/**
* @property {integer} _timeOutID - setTimeout ID.
* @private
*/
this._timeOutID = null;
/**
* @property {MediaStream} videoStream - The Video Stream data. Only set if this Video is streaming from the webcam via `startMediaStream`.
*/
this.videoStream = null;
/**
* @property {boolean} isStreaming - Is there a streaming video source? I.e. from a webcam.
*/
this.isStreaming = false;
/**
* When starting playback of a video Phaser will monitor its readyState using a setTimeout call.
* The setTimeout happens once every `Video.retryInterval` ms. It will carry on monitoring the video
* state in this manner until the `retryLimit` is reached and then abort. * state in this manner until the `retryLimit` is reached and then abort.
* @property {integer} retryLimit *
* @default * @name Phaser.GameObjects.Video#retryLimit
* @type {integer}
* @since 3.20.0
*/ */
this.retryLimit = 20; this.retryLimit = 20;
/** /**
* @property {integer} retry - The current retry attempt. * The current retry attempt.
* @default *
* @name Phaser.GameObjects.Video#retry
* @type {integer}
* @since 3.20.0
*/ */
this.retry = 0; this.retry = 0;
/** /**
* @property {integer} retryInterval - The number of ms between each retry at monitoring the status of a downloading video. * The number of ms between each retry while monitoring the ready state of a downloading video.
* @default *
* @name Phaser.GameObjects.Video#retryInterval
* @type {integer}
* @since 3.20.0
*/ */
this.retryInterval = 500; this.retryInterval = 500;
/** /**
* @property {integer} _retryID - The callback ID of the retry setTimeout. * The setTimeout callback ID.
*
* @name Phaser.GameObjects.Video#_retryID
* @type {integer}
* @private * @private
* @since 3.20.0
*/ */
this._retryID = null; this._retryID = null;
/** /**
* @property {boolean} _systemMuted - The video was muted due to a system event like losing focus, not a game code event. * The video was muted due to a system event, such as the game losing focus.
*
* @name Phaser.GameObjects.Video#_systemMuted
* @type {boolean}
* @private * @private
* @default * @since 3.20.0
*/ */
this._systemMuted = false; this._systemMuted = false;
/** /**
* @property {boolean} _codeMuted - The video was muted due to a game code event, or the Loader setting, not a system event. * The video was muted due to game code, not a system event.
*
* @name Phaser.GameObjects.Video#_codeMuted
* @type {boolean}
* @private * @private
* @default * @since 3.20.0
*/ */
this._codeMuted = false; this._codeMuted = false;
/** /**
* @property {boolean} _systemPaused - The video was paused due to a system event like losing focus, not a game code event. * The video was paused due to a system event, such as the game losing focus.
*
* @name Phaser.GameObjects.Video#_systemPaused
* @type {boolean}
* @private * @private
* @default * @since 3.20.0
*/ */
this._systemPaused = false; this._systemPaused = false;
/** /**
* @property {boolean} _codePaused - The video was paused due to a game code event, not a system event. * The video was paused due to game code, not a system event.
*
* @name Phaser.GameObjects.Video#_codePaused
* @type {boolean}
* @private * @private
* @default * @since 3.20.0
*/ */
this._codePaused = false; this._codePaused = false;
/**
* The locally bound event callback handlers.
*
* @name Phaser.GameObjects.Video#_callbacks
* @type {any}
* @private
* @since 3.20.0
*/
this._callbacks = { this._callbacks = {
end: this.completeHandler.bind(this), end: this.completeHandler.bind(this),
play: this.playHandler.bind(this), play: this.playHandler.bind(this),
@ -191,28 +257,72 @@ var Video = new Class({
/** /**
* The internal crop data object, as used by `setCrop` and passed to the `Frame.setCropUVs` method. * The internal crop data object, as used by `setCrop` and passed to the `Frame.setCropUVs` method.
* *
* @name Phaser.GameObjects.Image#_crop * @name Phaser.GameObjects.Video#_crop
* @type {object} * @type {object}
* @private * @private
* @since 3.11.0 * @since 3.20.0
*/ */
this._crop = this.resetCropObject(); this._crop = this.resetCropObject();
/**
* An object containing in and out markers for sequence playback.
*
* @name Phaser.GameObjects.Video#markers
* @type {any}
* @since 3.20.0
*/
this.markers = {}; this.markers = {};
/**
* The in marker.
*
* @name Phaser.GameObjects.Video#_markerIn
* @type {integer}
* @private
* @since 3.20.0
*/
this._markerIn = -1; this._markerIn = -1;
/**
* The out marker.
*
* @name Phaser.GameObjects.Video#_markerOut
* @type {integer}
* @private
* @since 3.20.0
*/
this._markerOut = Number.MAX_SAFE_INTEGER; this._markerOut = Number.MAX_SAFE_INTEGER;
/**
* The last time the TextureSource was updated.
*
* @name Phaser.GameObjects.Video#_lastUpdate
* @type {integer}
* @private
* @since 3.20.0
*/
this._lastUpdate = 0; this._lastUpdate = 0;
/**
* The key of the video being played from the Video cache, if any.
*
* @name Phaser.GameObjects.Video#_cacheKey
* @type {string}
* @private
* @since 3.20.0
*/
this._cacheKey = ''; this._cacheKey = '';
/**
* Is the video currently seeking?
*
* @name Phaser.GameObjects.Video#_isSeeking
* @type {boolean}
* @private
* @since 3.20.0
*/
this._isSeeking = false; this._isSeeking = false;
this.snapshotTexture = null;
this.flipY = false;
this.setPosition(x, y); this.setPosition(x, y);
this.initPipeline(); this.initPipeline();
@ -258,9 +368,14 @@ var Video = new Class({
* If need video audio, then you'll have to factor into your game flow the fact that the video cannot start playing * If need video audio, then you'll have to factor into your game flow the fact that the video cannot start playing
* until the user has interacted with the browser. * until the user has interacted with the browser.
* *
* @method Phaser.Video#play * @method Phaser.GameObjects.Video#play
* @param {boolean} [loop=false] - Should the video loop automatically when it reaches the end? Please note that at present some browsers (i.e. Chrome) do not support *seamless* video looping. * @since 3.20.0
* @return {Phaser.Video} This Video object for method chaining. *
* @param {boolean} [loop=false] - Should the video loop automatically when it reaches the end? Please note that not all browsers support _seamless_ video looping for all encoding formats.
* @param {integer} [markerIn] - Optional in marker time, in seconds, for playback of a sequence of the video.
* @param {integer} [markerOut] - Optional out marker time, in seconds, for playback of a sequence of the video.
*
* @return {this} This Video Game Object for method chaining.
*/ */
play: function (loop, markerIn, markerOut) play: function (loop, markerIn, markerOut)
{ {
@ -300,14 +415,6 @@ var Video = new Class({
video.loop = loop; video.loop = loop;
// If video hasn't downloaded properly yet ...
if (video.readyState !== 4)
{
this.retry = this.retryLimit;
this._retryID = window.setTimeout(this.checkVideoProgress.bind(this), this.retryInterval);
}
var callbacks = this._callbacks; var callbacks = this._callbacks;
var playPromise = video.play(); var playPromise = video.play();
@ -320,6 +427,14 @@ var Video = new Class({
{ {
// Old-school browsers with no Promises // Old-school browsers with no Promises
video.addEventListener('playing', callbacks.play, true); video.addEventListener('playing', callbacks.play, true);
// If video hasn't downloaded properly yet ...
if (video.readyState < 2)
{
this.retry = this.retryLimit;
this._retryID = window.setTimeout(this.checkVideoProgress.bind(this), this.retryInterval);
}
} }
// Set these after calling `play` or they don't fire (useful, thanks browsers) // Set these after calling `play` or they don't fire (useful, thanks browsers)
@ -330,6 +445,20 @@ var Video = new Class({
return this; return this;
}, },
/**
* TODO
*
* @method Phaser.GameObjects.Video#changeSource
* @since 3.20.0
*
* @param {string} key - The key of the Video this Game Object will swap to playing, as stored in the Video Cache.
* @param {boolean} [autoplay=true] - Should the video start playing immediately, once the swap is complete?
* @param {boolean} [loop=false] - Should the video loop automatically when it reaches the end? Please note that not all browsers support _seamless_ video looping for all encoding formats.
* @param {integer} [markerIn] - Optional in marker time, in seconds, for playback of a sequence of the video.
* @param {integer} [markerOut] - Optional out marker time, in seconds, for playback of a sequence of the video.
*
* @return {this} This Video Game Object for method chaining.
*/
changeSource: function (key, autoplay, loop, markerIn, markerOut) changeSource: function (key, autoplay, loop, markerIn, markerOut)
{ {
if (autoplay === undefined) { autoplay = true; } if (autoplay === undefined) { autoplay = true; }
@ -391,7 +520,27 @@ var Video = new Class({
return this; return this;
}, },
// https://github.com/w3c/media-and-entertainment/issues/4 /**
* Adds a sequence marker to this video.
*
* Markers allow you to split a video up into sequences, delineated by a start and end time, given in seconds.
*
* You can then play back specific markers via the `playMarker` method.
*
* Note that marker timing is _not_ frame-perfect. You should construct your videos in such a way that you allow for
* plenty of extra padding before and after each sequence to allow for discrepencies in browser seek and currentTime accuracy.
*
* See https://github.com/w3c/media-and-entertainment/issues/4 for more details about this issue.
*
* @method Phaser.GameObjects.Video#addMarker
* @since 3.20.0
*
* @param {string} key - A unique name to give this marker.
* @param {integer} markerIn - The time, in seconds, representing the start of this marker.
* @param {integer} markerOut - The time, in seconds, representing the end of this marker.
*
* @return {this} This Video Game Object for method chaining.
*/
addMarker: function (key, markerIn, markerOut) addMarker: function (key, markerIn, markerOut)
{ {
if (!isNaN(markerIn) && markerIn >= 0 && !isNaN(markerOut)) if (!isNaN(markerIn) && markerIn >= 0 && !isNaN(markerOut))
@ -402,6 +551,25 @@ var Video = new Class({
return this; return this;
}, },
/**
* Plays a pre-defined sequence in this video.
*
* Markers allow you to split a video up into sequences, delineated by a start and end time, given in seconds and
* specified via the `addMarker` method.
*
* Note that marker timing is _not_ frame-perfect. You should construct your videos in such a way that you allow for
* plenty of extra padding before and after each sequence to allow for discrepencies in browser seek and currentTime accuracy.
*
* See https://github.com/w3c/media-and-entertainment/issues/4 for more details about this issue.
*
* @method Phaser.GameObjects.Video#playMarker
* @since 3.20.0
*
* @param {string} key - The name of the marker sequence to play.
* @param {boolean} [loop=false] - Should the video loop automatically when it reaches the end? Please note that not all browsers support _seamless_ video looping for all encoding formats.
*
* @return {this} This Video Game Object for method chaining.
*/
playMarker: function (key, loop) playMarker: function (key, loop)
{ {
var marker = this.markers[key]; var marker = this.markers[key];
@ -414,6 +582,18 @@ var Video = new Class({
return this; return this;
}, },
/**
* Removes a previously set marker from this video.
*
* If the marker is currently playing it will _not_ stop playback.
*
* @method Phaser.GameObjects.Video#removeMarker
* @since 3.20.0
*
* @param {string} key - The name of the marker to remove.
*
* @return {this} This Video Game Object for method chaining.
*/
removeMarker: function (key) removeMarker: function (key)
{ {
delete this.markers[key]; delete this.markers[key];
@ -421,6 +601,21 @@ var Video = new Class({
return this; return this;
}, },
/**
* Takes a snapshot of the current frame of the video and renders it to a CanvasTexture object,
* which is then returned. You can optionally resize the grab by passing a width and height.
*
* This method returns a reference to the `Video.snapshotTexture` object. Calling this method
* multiple times will overwrite the previous snapshot with the most recent one.
*
* @method Phaser.GameObjects.Video#snapshot
* @since 3.20.0
*
* @param {integer} [width] - The width of the resulting CanvasTexture.
* @param {integer} [height] - The height of the resulting CanvasTexture.
*
* @return {Phaser.Textures.CanvasTexture}
*/
snapshot: function (width, height) snapshot: function (width, height)
{ {
if (width === undefined) { width = this.width; } if (width === undefined) { width = this.width; }
@ -429,6 +624,25 @@ var Video = new Class({
return this.snapshotArea(0, 0, this.width, this.height, width, height); return this.snapshotArea(0, 0, this.width, this.height, width, height);
}, },
/**
* Takes a snapshot of the specified area of the current frame of the video and renders it to a CanvasTexture object,
* which is then returned. You can optionally resize the grab by passing a different `destWidth` and `destHeight`.
*
* This method returns a reference to the `Video.snapshotTexture` object. Calling this method
* multiple times will overwrite the previous snapshot with the most recent one.
*
* @method Phaser.GameObjects.Video#snapshotArea
* @since 3.20.0
*
* @param {integer} [x=0] - The horizontal location of the top-left of the area to grab from.
* @param {integer} [y=0] - The vertical location of the top-left of the area to grab from.
* @param {integer} [srcWidth] - The width of area to grab from the video. If not given it will grab the full video dimensions.
* @param {integer} [srcHeight] - The height of area to grab from the video. If not given it will grab the full video dimensions.
* @param {integer} [destWidth] - The destination width of the grab, allowing you to resize it.
* @param {integer} [destHeight] - The destination height of the grab, allowing you to resize it.
*
* @return {Phaser.Textures.CanvasTexture}
*/
snapshotArea: function (x, y, srcWidth, srcHeight, destWidth, destHeight) snapshotArea: function (x, y, srcWidth, srcHeight, destWidth, destHeight)
{ {
if (x === undefined) { x = 0; } if (x === undefined) { x = 0; }
@ -555,17 +769,17 @@ var Video = new Class({
console.log(e); console.log(e);
}, true); }, true);
video.addEventListener('loadstart', function (e) video.addEventListener('loadstart', function ()
{ {
console.log('Load Start'); console.log('Load Start');
}, true); }, true);
video.addEventListener('loadedmetadata', function (e) video.addEventListener('loadedmetadata', function ()
{ {
console.log('Loaded Meta Data'); console.log('Loaded Meta Data');
}, true); }, true);
video.addEventListener('emptied', function (e) video.addEventListener('emptied', function ()
{ {
console.log('Load Emptied'); console.log('Load Emptied');
}, true); }, true);
@ -715,45 +929,7 @@ var Video = new Class({
var video = this.video; var video = this.video;
if (this.isStreaming) if (video)
{
var videoStream = this.videoStream;
if (video.mozSrcObject)
{
video.mozSrcObject.stop();
video.src = null;
}
else if (video.srcObject)
{
video.srcObject.stop();
video.src = null;
}
else
{
video.src = '';
if (videoStream.active)
{
videoStream.active = false;
}
else if (videoStream.getTracks)
{
videoStream.getTracks().forEach(function (track)
{
track.stop();
});
}
else
{
videoStream.stop();
}
}
this.videoStream = null;
this.isStreaming = false;
}
else if (video)
{ {
var callbacks = this._callbacks; var callbacks = this._callbacks;
@ -770,29 +946,6 @@ var Video = new Class({
return this; return this;
}, },
/**
* Creates a new Video element from the given Blob. The Blob must contain the video data in the correct encoded format.
* This method is typically called by the Phaser.Loader and Phaser.Cache for you, but is exposed publicly for convenience.
*
* @method Phaser.Video#createVideoFromBlob
* @param {Blob} blob - The Blob containing the video data.
* @return {Phaser.Video} This Video object for method chaining.
*/
createVideoFromBlob: function (blob)
{
var _this = this;
this.video = document.createElement('video');
this.video.controls = false;
this.video.setAttribute('autoplay', 'autoplay');
this.video.setAttribute('playsinline', 'playsinline');
this.video.addEventListener('loadeddata', function (event) { _this.updateTexture(event); }, true);
this.video.src = window.URL.createObjectURL(blob);
this.video.canplay = true;
return this;
},
/** /**
* Internal callback that monitors the download progress of a video after changing its source. * Internal callback that monitors the download progress of a video after changing its source.
* *
@ -801,10 +954,8 @@ var Video = new Class({
*/ */
checkVideoProgress: function () checkVideoProgress: function ()
{ {
if (this.video.readyState === 4) if (this.video.readyState >= 2)
{ {
this._pendingChangeSource = false;
// We've got enough data to update the texture for playback // We've got enough data to update the texture for playback
this.updateTexture(); this.updateTexture();
} }
@ -818,7 +969,7 @@ var Video = new Class({
} }
else else
{ {
console.warn('Phaser.Video: Unable to start downloading video in time', this.isStreaming); console.warn('Phaser.Video: Unable to start downloading video in time');
} }
} }
}, },
@ -1240,12 +1391,16 @@ var Video = new Class({
* *
* @method Phaser.Video#destroy * @method Phaser.Video#destroy
*/ */
destroy: function () destroy: function (removeVideoElement)
{ {
if (removeVideoElement === undefined) { removeVideoElement = true; }
this.stop(); this.stop();
// Only if this is a custom video AND hasn't been saved as a texture? if (removeVideoElement)
this.removeVideoElement(); {
this.removeVideoElement();
}
var game = this.scene.sys.game.events; var game = this.scene.sys.game.events;