var BaseTexture = require('./BaseTexture'), 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 BaseTexture * @memberof PIXI * @param source {HTMLVideoElement} * @param [scaleMode] {number} See {@link SCALE_MODES} for possible values */ function VideoBaseTexture(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; } BaseTexture.call(this, 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; } VideoBaseTexture.prototype = Object.create(BaseTexture.prototype); VideoBaseTexture.prototype.constructor = VideoBaseTexture; module.exports = VideoBaseTexture; /** * The internal update loop of the video base texture, only runs when autoUpdate is set to true * @private */ VideoBaseTexture.prototype._onUpdate = function () { if (this.autoUpdate) { window.requestAnimationFrame(this._onUpdate); this.update(); } }; /** * Runs the update loop when the video is ready to play * @private */ VideoBaseTexture.prototype._onPlayStart = function () { if (!this.autoUpdate) { window.requestAnimationFrame(this._onUpdate); this.autoUpdate = true; } }; /** * Fired when a pause event is triggered, stops the update loop * @private */ VideoBaseTexture.prototype._onPlayStop = function () { this.autoUpdate = false; }; /** * Fired when the video is loaded and ready to play * @private */ VideoBaseTexture.prototype._onCanPlay = function () { 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 * */ VideoBaseTexture.prototype.destroy = function () { if (this.source && this.source._pixiId) { utils.BaseTextureCache[ this.source._pixiId ] = null; delete utils.BaseTextureCache[ this.source._pixiId ]; this.source._pixiId = null; delete this.source._pixiId; } BaseTexture.prototype.destroy.call(this); }; /** * Mimic Pixi BaseTexture.from.... method. * * @static * @param video {HTMLVideoElement} * @param scaleMode {number} See {@link SCALE_MODES} for possible values * @return {VideoBaseTexture} */ VideoBaseTexture.fromVideo = function (video, scaleMode) { if (!video._pixiId) { video._pixiId = 'video_' + utils.uuid(); } 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 SCALE_MODES} for possible values * @return {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.src || videoSrc, videoSrc.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; }