Source: core/textures/VideoBaseTexture.js

core/textures/VideoBaseTexture.js

  1. import BaseTexture from './BaseTexture';
  2. import { uid, BaseTextureCache } from '../utils';
  3. import { shared } from '../ticker';
  4. import { UPDATE_PRIORITY } from '../const';
  5. import determineCrossOrigin from '../utils/determineCrossOrigin';
  6. //引入小程序补丁
  7. import { documentAlias } from '@ali/pixi-miniprogram-adapter';
  8. /**
  9. * A texture of a [playing] Video.
  10. *
  11. * Video base textures mimic PixiJS BaseTexture.from.... method in their creation process.
  12. *
  13. * This can be used in several ways, such as:
  14. *
  15. * ```js
  16. * let texture = PIXI.VideoBaseTexture.fromUrl('http://mydomain.com/video.mp4');
  17. *
  18. * let texture = PIXI.VideoBaseTexture.fromUrl({ src: 'http://mydomain.com/video.mp4', mime: 'video/mp4' });
  19. *
  20. * let texture = PIXI.VideoBaseTexture.fromUrls(['/video.webm', '/video.mp4']);
  21. *
  22. * let texture = PIXI.VideoBaseTexture.fromUrls([
  23. * { src: '/video.webm', mime: 'video/webm' },
  24. * { src: '/video.mp4', mime: 'video/mp4' }
  25. * ]);
  26. * ```
  27. *
  28. * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/).
  29. *
  30. * @class
  31. * @extends PIXI.BaseTexture
  32. * @memberof PIXI
  33. */
  34. export default class VideoBaseTexture extends BaseTexture
  35. {
  36. /**
  37. * @param {HTMLVideoElement} source - Video source
  38. * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
  39. * @param {boolean} [autoPlay=true] - Start playing video as soon as it is loaded
  40. */
  41. constructor(source, scaleMode, autoPlay = true)
  42. {
  43. if (!source)
  44. {
  45. throw new Error('No video source element specified.');
  46. }
  47. // hook in here to check if video is already available.
  48. // BaseTexture looks for a source.complete boolean, plus width & height.
  49. if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA)
  50. && source.width && source.height)
  51. {
  52. source.complete = true;
  53. }
  54. super(source, scaleMode);
  55. this.width = source.videoWidth;
  56. this.height = source.videoHeight;
  57. this._autoUpdate = true;
  58. this._isAutoUpdating = false;
  59. /**
  60. * When set to true will automatically play videos used by this texture once
  61. * they are loaded. If false, it will not modify the playing state.
  62. *
  63. * @member {boolean}
  64. * @default true
  65. */
  66. this.autoPlay = autoPlay;
  67. this.update = this.update.bind(this);
  68. this._onCanPlay = this._onCanPlay.bind(this);
  69. source.addEventListener('play', this._onPlayStart.bind(this));
  70. source.addEventListener('pause', this._onPlayStop.bind(this));
  71. this.hasLoaded = false;
  72. this.__loaded = false;
  73. if (!this._isSourceReady())
  74. {
  75. source.addEventListener('canplay', this._onCanPlay);
  76. source.addEventListener('canplaythrough', this._onCanPlay);
  77. }
  78. else
  79. {
  80. this._onCanPlay();
  81. }
  82. }
  83. /**
  84. * Returns true if the underlying source is playing.
  85. *
  86. * @private
  87. * @return {boolean} True if playing.
  88. */
  89. _isSourcePlaying()
  90. {
  91. const source = this.source;
  92. return (source.currentTime > 0 && source.paused === false && source.ended === false && source.readyState > 2);
  93. }
  94. /**
  95. * Returns true if the underlying source is ready for playing.
  96. *
  97. * @private
  98. * @return {boolean} True if ready.
  99. */
  100. _isSourceReady()
  101. {
  102. return this.source.readyState === 3 || this.source.readyState === 4;
  103. }
  104. /**
  105. * Runs the update loop when the video is ready to play
  106. *
  107. * @private
  108. */
  109. _onPlayStart()
  110. {
  111. // Just in case the video has not received its can play even yet..
  112. if (!this.hasLoaded)
  113. {
  114. this._onCanPlay();
  115. }
  116. if (!this._isAutoUpdating && this.autoUpdate)
  117. {
  118. shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
  119. this._isAutoUpdating = true;
  120. }
  121. }
  122. /**
  123. * Fired when a pause event is triggered, stops the update loop
  124. *
  125. * @private
  126. */
  127. _onPlayStop()
  128. {
  129. if (this._isAutoUpdating)
  130. {
  131. shared.remove(this.update, this);
  132. this._isAutoUpdating = false;
  133. }
  134. }
  135. /**
  136. * Fired when the video is loaded and ready to play
  137. *
  138. * @private
  139. */
  140. _onCanPlay()
  141. {
  142. this.hasLoaded = true;
  143. if (this.source)
  144. {
  145. this.source.removeEventListener('canplay', this._onCanPlay);
  146. this.source.removeEventListener('canplaythrough', this._onCanPlay);
  147. this.width = this.source.videoWidth;
  148. this.height = this.source.videoHeight;
  149. // prevent multiple loaded dispatches..
  150. if (!this.__loaded)
  151. {
  152. this.__loaded = true;
  153. this.emit('loaded', this);
  154. }
  155. if (this._isSourcePlaying())
  156. {
  157. this._onPlayStart();
  158. }
  159. else if (this.autoPlay)
  160. {
  161. this.source.play();
  162. }
  163. }
  164. }
  165. /**
  166. * Destroys this texture
  167. *
  168. */
  169. destroy()
  170. {
  171. if (this._isAutoUpdating)
  172. {
  173. shared.remove(this.update, this);
  174. }
  175. if (this.source && this.source._pixiId)
  176. {
  177. BaseTexture.removeFromCache(this.source._pixiId);
  178. delete this.source._pixiId;
  179. this.source.pause();
  180. this.source.src = '';
  181. this.source.load();
  182. }
  183. super.destroy();
  184. }
  185. /**
  186. * Mimic PixiJS BaseTexture.from.... method.
  187. *
  188. * @static
  189. * @param {HTMLVideoElement} video - Video to create texture from
  190. * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
  191. * @param {boolean} [autoPlay=true] - Start playing video as soon as it is loaded
  192. * @return {PIXI.VideoBaseTexture} Newly created VideoBaseTexture
  193. */
  194. static fromVideo(video, scaleMode, autoPlay)
  195. {
  196. if (!video._pixiId)
  197. {
  198. video._pixiId = `video_${uid()}`;
  199. }
  200. let baseTexture = BaseTextureCache[video._pixiId];
  201. if (!baseTexture)
  202. {
  203. baseTexture = new VideoBaseTexture(video, scaleMode, autoPlay);
  204. BaseTexture.addToCache(baseTexture, video._pixiId);
  205. }
  206. return baseTexture;
  207. }
  208. /**
  209. * Helper function that creates a new BaseTexture based on the given video element.
  210. * This BaseTexture can then be used to create a texture
  211. *
  212. * @static
  213. * @param {string|object|string[]|object[]} videoSrc - The URL(s) for the video.
  214. * @param {string} [videoSrc.src] - One of the source urls for the video
  215. * @param {string} [videoSrc.mime] - The mimetype of the video (e.g. 'video/mp4'). If not specified
  216. * the url's extension will be used as the second part of the mime type.
  217. * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
  218. * @param {boolean} [crossorigin=(auto)] - Should use anonymous CORS? Defaults to true if the URL is not a data-URI.
  219. * @param {boolean} [autoPlay=true] - Start playing video as soon as it is loaded
  220. * @return {PIXI.VideoBaseTexture} Newly created VideoBaseTexture
  221. */
  222. static fromUrl(videoSrc, scaleMode, crossorigin, autoPlay)
  223. {
  224. const video = documentAlias.createElement('video');
  225. video.setAttribute('webkit-playsinline', '');
  226. video.setAttribute('playsinline', '');
  227. const url = Array.isArray(videoSrc) ? (videoSrc[0].src || videoSrc[0]) : (videoSrc.src || videoSrc);
  228. if (crossorigin === undefined && url.indexOf('data:') !== 0)
  229. {
  230. video.crossOrigin = determineCrossOrigin(url);
  231. }
  232. else if (crossorigin)
  233. {
  234. video.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous';
  235. }
  236. // array of objects or strings
  237. if (Array.isArray(videoSrc))
  238. {
  239. for (let i = 0; i < videoSrc.length; ++i)
  240. {
  241. video.appendChild(createSource(videoSrc[i].src || videoSrc[i], videoSrc[i].mime));
  242. }
  243. }
  244. // single object or string
  245. else
  246. {
  247. video.appendChild(createSource(url, videoSrc.mime));
  248. }
  249. video.load();
  250. return VideoBaseTexture.fromVideo(video, scaleMode, autoPlay);
  251. }
  252. /**
  253. * Should the base texture automatically update itself, set to true by default
  254. *
  255. * @member {boolean}
  256. */
  257. get autoUpdate()
  258. {
  259. return this._autoUpdate;
  260. }
  261. set autoUpdate(value) // eslint-disable-line require-jsdoc
  262. {
  263. if (value !== this._autoUpdate)
  264. {
  265. this._autoUpdate = value;
  266. if (!this._autoUpdate && this._isAutoUpdating)
  267. {
  268. shared.remove(this.update, this);
  269. this._isAutoUpdating = false;
  270. }
  271. else if (this._autoUpdate && !this._isAutoUpdating)
  272. {
  273. shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
  274. this._isAutoUpdating = true;
  275. }
  276. }
  277. }
  278. }
  279. VideoBaseTexture.fromUrls = VideoBaseTexture.fromUrl;
  280. function createSource(path, type)
  281. {
  282. if (!type)
  283. {
  284. const purePath = path.split('?').shift().toLowerCase();
  285. type = `video/${purePath.substr(purePath.lastIndexOf('.') + 1)}`;
  286. }
  287. const source = documentAlias.createElement('source');
  288. source.src = path;
  289. source.type = type;
  290. return source;
  291. }