UNPKG

31.8 kBJavaScriptView Raw
1/**
2 * @file flash.js
3 * VideoJS-SWF - Custom Flash Player with HTML5-ish API
4 * https://github.com/zencoder/video-js-swf
5 * Not using setupTriggers. Using global onEvent func to distribute events
6 */
7
8import videojs from 'video.js';
9import {version as SWF_VERSION} from 'videojs-swf/package.json';
10import {version as VERSION} from '../package.json';
11import FlashRtmpDecorator from './rtmp';
12import window from 'global/window';
13
14const Tech = videojs.getComponent('Tech');
15const Dom = videojs.dom;
16const Url = videojs.url;
17const createTimeRange = videojs.createTimeRange;
18const mergeOptions = videojs.mergeOptions;
19
20const navigator = window && window.navigator || {};
21
22/**
23 * Flash Media Controller - Wrapper for Flash Media API
24 *
25 * @mixes FlashRtmpDecorator
26 * @mixes Tech~SouceHandlerAdditions
27 * @extends Tech
28 */
29class Flash extends Tech {
30
31 /**
32 * Create an instance of this Tech.
33 *
34 * @param {Object} [options]
35 * The key/value store of player options.
36 *
37 * @param {Component~ReadyCallback} ready
38 * Callback function to call when the `Flash` Tech is ready.
39 */
40 constructor(options, ready) {
41 super(options, ready);
42
43 // Set the source when ready
44 if (options.source) {
45 this.ready(function() {
46 this.setSource(options.source);
47 }, true);
48 }
49
50 // Having issues with Flash reloading on certain page actions
51 // (hide/resize/fullscreen) in certain browsers
52 // This allows resetting the playhead when we catch the reload
53 if (options.startTime) {
54 this.ready(function() {
55 this.load();
56 this.play();
57 this.currentTime(options.startTime);
58 }, true);
59 }
60
61 // Add global window functions that the swf expects
62 // A 4.x workflow we weren't able to solve for in 5.0
63 // because of the need to hard code these functions
64 // into the swf for security reasons
65 window.videojs = window.videojs || {};
66 window.videojs.Flash = window.videojs.Flash || {};
67 window.videojs.Flash.onReady = Flash.onReady;
68 window.videojs.Flash.onEvent = Flash.onEvent;
69 window.videojs.Flash.onError = Flash.onError;
70
71 this.on('seeked', function() {
72 this.lastSeekTarget_ = undefined;
73 });
74
75 }
76
77 /**
78 * Create the `Flash` Tech's DOM element.
79 *
80 * @return {Element}
81 * The element that gets created.
82 */
83 createEl() {
84 const options = this.options_;
85
86 // If video.js is hosted locally you should also set the location
87 // for the hosted swf, which should be relative to the page (not video.js)
88 // Otherwise this adds a CDN url.
89 // The CDN also auto-adds a swf URL for that specific version.
90 if (!options.swf) {
91 options.swf = `https://vjs.zencdn.net/swf/${SWF_VERSION}/video-js.swf`;
92 }
93
94 // Generate ID for swf object
95 const objId = options.techId;
96
97 // Merge default flashvars with ones passed in to init
98 const flashVars = mergeOptions({
99
100 // SWF Callback Functions
101 readyFunction: 'videojs.Flash.onReady',
102 eventProxyFunction: 'videojs.Flash.onEvent',
103 errorEventProxyFunction: 'videojs.Flash.onError',
104
105 // Player Settings
106 autoplay: options.autoplay,
107 preload: options.preload,
108 loop: options.loop,
109 muted: options.muted
110
111 }, options.flashVars);
112
113 // Merge default parames with ones passed in
114 const params = mergeOptions({
115 // Opaque is needed to overlay controls, but can affect playback performance
116 wmode: 'opaque',
117 // Using bgcolor prevents a white flash when the object is loading
118 bgcolor: '#000000'
119 }, options.params);
120
121 // Merge default attributes with ones passed in
122 const attributes = mergeOptions({
123 // Both ID and Name needed or swf to identify itself
124 id: objId,
125 name: objId,
126 class: 'vjs-tech'
127 }, options.attributes);
128
129 this.el_ = Flash.embed(options.swf, flashVars, params, attributes);
130 this.el_.tech = this;
131
132 return this.el_;
133 }
134
135 /**
136 * Called by {@link Player#play} to play using the `Flash` `Tech`.
137 */
138 play() {
139 if (this.ended()) {
140 this.setCurrentTime(0);
141 }
142 this.el_.vjs_play();
143 }
144
145 /**
146 * Called by {@link Player#pause} to pause using the `Flash` `Tech`.
147 */
148 pause() {
149 this.el_.vjs_pause();
150 }
151
152 /**
153 * A getter/setter for the `Flash` Tech's source object.
154 * > Note: Please use {@link Flash#setSource}
155 *
156 * @param {Tech~SourceObject} [src]
157 * The source object you want to set on the `Flash` techs.
158 *
159 * @return {Tech~SourceObject|undefined}
160 * - The current source object when a source is not passed in.
161 * - undefined when setting
162 *
163 * @deprecated Since version 5.
164 */
165 src(src) {
166 if (src === undefined) {
167 return this.currentSrc();
168 }
169
170 // Setting src through `src` not `setSrc` will be deprecated
171 return this.setSrc(src);
172 }
173
174 /**
175 * A getter/setter for the `Flash` Tech's source object.
176 *
177 * @param {Tech~SourceObject} [src]
178 * The source object you want to set on the `Flash` techs.
179 */
180 setSrc(src) {
181 // Make sure source URL is absolute.
182 src = Url.getAbsoluteURL(src);
183 this.el_.vjs_src(src);
184
185 // Currently the SWF doesn't autoplay if you load a source later.
186 // e.g. Load player w/ no source, wait 2s, set src.
187 if (this.autoplay()) {
188 this.setTimeout(() => this.play(), 0);
189 }
190 }
191
192 /**
193 * Indicates whether the media is currently seeking to a new position or not.
194 *
195 * @return {boolean}
196 * - True if seeking to a new position
197 * - False otherwise
198 */
199 seeking() {
200 return this.lastSeekTarget_ !== undefined;
201 }
202
203 /**
204 * Returns the current time in seconds that the media is at in playback.
205 *
206 * @param {number} time
207 * Current playtime of the media in seconds.
208 */
209 setCurrentTime(time) {
210 const seekable = this.seekable();
211
212 if (seekable.length) {
213 // clamp to the current seekable range
214 time = time > seekable.start(0) ? time : seekable.start(0);
215 time = time < seekable.end(seekable.length - 1) ?
216 time : seekable.end(seekable.length - 1);
217
218 this.lastSeekTarget_ = time;
219 this.trigger('seeking');
220 this.el_.vjs_setProperty('currentTime', time);
221 super.setCurrentTime();
222 }
223 }
224
225 /**
226 * Get the current playback time in seconds
227 *
228 * @return {number}
229 * The current time of playback in seconds.
230 */
231 currentTime() {
232 // when seeking make the reported time keep up with the requested time
233 // by reading the time we're seeking to
234 if (this.seeking()) {
235 return this.lastSeekTarget_ || 0;
236 }
237 return this.el_.vjs_getProperty('currentTime');
238 }
239
240 /**
241 * Get the current source
242 *
243 * @method currentSrc
244 * @return {Tech~SourceObject}
245 * The current source
246 */
247 currentSrc() {
248 if (this.currentSource_) {
249 return this.currentSource_.src;
250 }
251 return this.el_.vjs_getProperty('currentSrc');
252 }
253
254 /**
255 * Get the total duration of the current media.
256 *
257 * @return {number}
258 8 The total duration of the current media.
259 */
260 duration() {
261 if (this.readyState() === 0) {
262 return NaN;
263 }
264 const duration = this.el_.vjs_getProperty('duration');
265
266 return duration >= 0 ? duration : Infinity;
267 }
268
269 /**
270 * Load media into Tech.
271 */
272 load() {
273 this.el_.vjs_load();
274 }
275
276 /**
277 * Get the poster image that was set on the tech.
278 */
279 poster() {
280 this.el_.vjs_getProperty('poster');
281 }
282
283 /**
284 * Poster images are not handled by the Flash tech so make this is a no-op.
285 */
286 setPoster() {}
287
288 /**
289 * Determine the time ranges that can be seeked to in the media.
290 *
291 * @return {TimeRange}
292 * Returns the time ranges that can be seeked to.
293 */
294 seekable() {
295 const duration = this.duration();
296
297 if (duration === 0) {
298 return createTimeRange();
299 }
300 return createTimeRange(0, duration);
301 }
302
303 /**
304 * Get and create a `TimeRange` object for buffering.
305 *
306 * @return {TimeRange}
307 * The time range object that was created.
308 */
309 buffered() {
310 const ranges = this.el_.vjs_getProperty('buffered');
311
312 if (ranges.length === 0) {
313 return createTimeRange();
314 }
315 return createTimeRange(ranges[0][0], ranges[0][1]);
316 }
317
318 /**
319 * Get fullscreen support -
320 *
321 * Flash does not allow fullscreen through javascript
322 * so this always returns false.
323 *
324 * @return {boolean}
325 * The Flash tech does not support fullscreen, so it will always return false.
326 */
327 supportsFullScreen() {
328 // Flash does not allow fullscreen through javascript
329 return false;
330 }
331
332 /**
333 * Flash does not allow fullscreen through javascript
334 * so this always returns false.
335 *
336 * @return {boolean}
337 * The Flash tech does not support fullscreen, so it will always return false.
338 */
339 enterFullScreen() {
340 return false;
341 }
342
343 /**
344 * Gets available media playback quality metrics as specified by the W3C's Media
345 * Playback Quality API.
346 *
347 * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
348 *
349 * @return {Object}
350 * An object with supported media playback quality metrics
351 */
352 getVideoPlaybackQuality() {
353 const videoPlaybackQuality = this.el_.vjs_getProperty('getVideoPlaybackQuality');
354
355 if (window.performance && typeof window.performance.now === 'function') {
356 videoPlaybackQuality.creationTime = window.performance.now();
357 } else if (window.performance &&
358 window.performance.timing &&
359 typeof window.performance.timing.navigationStart === 'number') {
360 videoPlaybackQuality.creationTime =
361 window.Date.now() - window.performance.timing.navigationStart;
362 }
363
364 return videoPlaybackQuality;
365 }
366}
367
368// Create setters and getters for attributes
369const _readWrite = [
370 'rtmpConnection',
371 'rtmpStream',
372 'preload',
373 'defaultPlaybackRate',
374 'playbackRate',
375 'autoplay',
376 'loop',
377 'controls',
378 'volume',
379 'muted',
380 'defaultMuted'
381];
382const _readOnly = [
383 'networkState',
384 'readyState',
385 'initialTime',
386 'startOffsetTime',
387 'paused',
388 'ended',
389 'videoWidth',
390 'videoHeight'
391];
392const _api = Flash.prototype;
393
394/**
395 * Create setters for the swf on the element
396 *
397 * @param {string} attr
398 * The name of the parameter
399 *
400 * @private
401 */
402function _createSetter(attr) {
403 const attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
404
405 _api['set' + attrUpper] = function(val) {
406 return this.el_.vjs_setProperty(attr, val);
407 };
408}
409
410/**
411 * Create petters for the swf on the element
412 *
413 * @param {string} attr
414 * The name of the parameter
415 *
416 * @private
417 */
418function _createGetter(attr) {
419 _api[attr] = function() {
420 return this.el_.vjs_getProperty(attr);
421 };
422}
423
424// Create getter and setters for all read/write attributes
425for (let i = 0; i < _readWrite.length; i++) {
426 _createGetter(_readWrite[i]);
427 _createSetter(_readWrite[i]);
428}
429
430// Create getters for read-only attributes
431for (let i = 0; i < _readOnly.length; i++) {
432 _createGetter(_readOnly[i]);
433}
434
435/** ------------------------------ Getters ------------------------------ **/
436/**
437 * Get the value of `rtmpConnection` from the swf.
438 *
439 * @method Flash#rtmpConnection
440 * @return {string}
441 * The current value of `rtmpConnection` on the swf.
442 */
443
444/**
445 * Get the value of `rtmpStream` from the swf.
446 *
447 * @method Flash#rtmpStream
448 * @return {string}
449 * The current value of `rtmpStream` on the swf.
450 */
451
452/**
453 * Get the value of `preload` from the swf. `preload` indicates
454 * what should download before the media is interacted with. It can have the following
455 * values:
456 * - none: nothing should be downloaded
457 * - metadata: poster and the first few frames of the media may be downloaded to get
458 * media dimensions and other metadata
459 * - auto: allow the media and metadata for the media to be downloaded before
460 * interaction
461 *
462 * @method Flash#preload
463 * @return {string}
464 * The value of `preload` from the swf. Will be 'none', 'metadata',
465 * or 'auto'.
466 */
467
468/**
469 * Get the value of `defaultPlaybackRate` from the swf.
470 *
471 * @method Flash#defaultPlaybackRate
472 * @return {number}
473 * The current value of `defaultPlaybackRate` on the swf.
474 */
475
476/**
477 * Get the value of `playbackRate` from the swf. `playbackRate` indicates
478 * the rate at which the media is currently playing back. Examples:
479 * - if playbackRate is set to 2, media will play twice as fast.
480 * - if playbackRate is set to 0.5, media will play half as fast.
481 *
482 * @method Flash#playbackRate
483 * @return {number}
484 * The value of `playbackRate` from the swf. A number indicating
485 * the current playback speed of the media, where 1 is normal speed.
486 */
487
488/**
489 * Get the value of `autoplay` from the swf. `autoplay` indicates
490 * that the media should start to play as soon as the page is ready.
491 *
492 * @method Flash#autoplay
493 * @return {boolean}
494 * - The value of `autoplay` from the swf.
495 * - True indicates that the media ashould start as soon as the page loads.
496 * - False indicates that the media should not start as soon as the page loads.
497 */
498
499/**
500 * Get the value of `loop` from the swf. `loop` indicates
501 * that the media should return to the start of the media and continue playing once
502 * it reaches the end.
503 *
504 * @method Flash#loop
505 * @return {boolean}
506 * - The value of `loop` from the swf.
507 * - True indicates that playback should seek back to start once
508 * the end of a media is reached.
509 * - False indicates that playback should not loop back to the start when the
510 * end of the media is reached.
511 */
512
513/**
514 * Get the value of `mediaGroup` from the swf.
515 *
516 * @method Flash#mediaGroup
517 * @return {string}
518 * The current value of `mediaGroup` on the swf.
519 */
520
521/**
522 * Get the value of `controller` from the swf.
523 *
524 * @method Flash#controller
525 * @return {string}
526 * The current value of `controller` on the swf.
527 */
528
529/**
530 * Get the value of `controls` from the swf. `controls` indicates
531 * whether the native flash controls should be shown or hidden.
532 *
533 * @method Flash#controls
534 * @return {boolean}
535 * - The value of `controls` from the swf.
536 * - True indicates that native controls should be showing.
537 * - False indicates that native controls should be hidden.
538 */
539
540/**
541 * Get the value of the `volume` from the swf. `volume` indicates the current
542 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
543 * so on.
544 *
545 * @method Flash#volume
546 * @return {number}
547 * The volume percent as a decimal. Value will be between 0-1.
548 */
549
550/**
551 * Get the value of the `muted` from the swf. `muted` indicates the current
552 * audio level should be silent.
553 *
554 * @method Flash#muted
555 * @return {boolean}
556 * - True if the audio should be set to silent
557 * - False otherwise
558 */
559
560/**
561 * Get the value of `defaultMuted` from the swf. `defaultMuted` indicates
562 * whether the media should start muted or not. Only changes the default state of the
563 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
564 * current state.
565 *
566 * @method Flash#defaultMuted
567 * @return {boolean}
568 * - The value of `defaultMuted` from the swf.
569 * - True indicates that the media should start muted.
570 * - False indicates that the media should not start muted.
571 */
572
573/**
574 * Get the value of `networkState` from the swf. `networkState` indicates
575 * the current network state. It returns an enumeration from the following list:
576 * - 0: NETWORK_EMPTY
577 * - 1: NEWORK_IDLE
578 * - 2: NETWORK_LOADING
579 * - 3: NETWORK_NO_SOURCE
580 *
581 * @method Flash#networkState
582 * @return {number}
583 * The value of `networkState` from the swf. This will be a number
584 * from the list in the description.
585 */
586
587/**
588 * Get the value of `readyState` from the swf. `readyState` indicates
589 * the current state of the media element. It returns an enumeration from the
590 * following list:
591 * - 0: HAVE_NOTHING
592 * - 1: HAVE_METADATA
593 * - 2: HAVE_CURRENT_DATA
594 * - 3: HAVE_FUTURE_DATA
595 * - 4: HAVE_ENOUGH_DATA
596 *
597 * @method Flash#readyState
598 * @return {number}
599 * The value of `readyState` from the swf. This will be a number
600 * from the list in the description.
601 */
602
603/**
604 * Get the value of `readyState` from the swf. `readyState` indicates
605 * the current state of the media element. It returns an enumeration from the
606 * following list:
607 * - 0: HAVE_NOTHING
608 * - 1: HAVE_METADATA
609 * - 2: HAVE_CURRENT_DATA
610 * - 3: HAVE_FUTURE_DATA
611 * - 4: HAVE_ENOUGH_DATA
612 *
613 * @method Flash#readyState
614 * @return {number}
615 * The value of `readyState` from the swf. This will be a number
616 * from the list in the description.
617 */
618
619/**
620 * Get the value of `initialTime` from the swf.
621 *
622 * @method Flash#initialTime
623 * @return {number}
624 * The `initialTime` proprety on the swf.
625 */
626
627/**
628 * Get the value of `startOffsetTime` from the swf.
629 *
630 * @method Flash#startOffsetTime
631 * @return {number}
632 * The `startOffsetTime` proprety on the swf.
633 */
634
635/**
636 * Get the value of `paused` from the swf. `paused` indicates whether the swf
637 * is current paused or not.
638 *
639 * @method Flash#paused
640 * @return {boolean}
641 * The value of `paused` from the swf.
642 */
643
644/**
645 * Get the value of `ended` from the swf. `ended` indicates whether
646 * the media has reached the end or not.
647 *
648 * @method Flash#ended
649 * @return {boolean}
650 * - True indicates that the media has ended.
651 * - False indicates that the media has not ended.
652 *
653 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
654 */
655
656/**
657 * Get the value of `videoWidth` from the swf. `videoWidth` indicates
658 * the current width of the media in css pixels.
659 *
660 * @method Flash#videoWidth
661 * @return {number}
662 * The value of `videoWidth` from the swf. This will be a number
663 * in css pixels.
664 */
665
666/**
667 * Get the value of `videoHeight` from the swf. `videoHeigth` indicates
668 * the current height of the media in css pixels.
669 *
670 * @method Flassh.prototype.videoHeight
671 * @return {number}
672 * The value of `videoHeight` from the swf. This will be a number
673 * in css pixels.
674 */
675/** ------------------------------ Setters ------------------------------ **/
676
677/**
678 * Set the value of `rtmpConnection` on the swf.
679 *
680 * @method Flash#setRtmpConnection
681 * @param {string} rtmpConnection
682 * New value to set the `rtmpConnection` property to.
683 */
684
685/**
686 * Set the value of `rtmpStream` on the swf.
687 *
688 * @method Flash#setRtmpStream
689 * @param {string} rtmpStream
690 * New value to set the `rtmpStream` property to.
691 */
692
693/**
694 * Set the value of `preload` on the swf. `preload` indicates
695 * what should download before the media is interacted with. It can have the following
696 * values:
697 * - none: nothing should be downloaded
698 * - metadata: poster and the first few frames of the media may be downloaded to get
699 * media dimensions and other metadata
700 * - auto: allow the media and metadata for the media to be downloaded before
701 * interaction
702 *
703 * @method Flash#setPreload
704 * @param {string} preload
705 * The value of `preload` to set on the swf. Should be 'none', 'metadata',
706 * or 'auto'.
707 */
708
709/**
710 * Set the value of `defaultPlaybackRate` on the swf.
711 *
712 * @method Flash#setDefaultPlaybackRate
713 * @param {number} defaultPlaybackRate
714 * New value to set the `defaultPlaybackRate` property to.
715 */
716
717/**
718 * Set the value of `playbackRate` on the swf. `playbackRate` indicates
719 * the rate at which the media is currently playing back. Examples:
720 * - if playbackRate is set to 2, media will play twice as fast.
721 * - if playbackRate is set to 0.5, media will play half as fast.
722 *
723 * @method Flash#setPlaybackRate
724 * @param {number} playbackRate
725 * New value of `playbackRate` on the swf. A number indicating
726 * the current playback speed of the media, where 1 is normal speed.
727 */
728
729/**
730 * Set the value of `autoplay` on the swf. `autoplay` indicates
731 * that the media should start to play as soon as the page is ready.
732 *
733 * @method Flash#setAutoplay
734 * @param {boolean} autoplay
735 * - The value of `autoplay` from the swf.
736 * - True indicates that the media ashould start as soon as the page loads.
737 * - False indicates that the media should not start as soon as the page loads.
738 */
739
740/**
741 * Set the value of `loop` on the swf. `loop` indicates
742 * that the media should return to the start of the media and continue playing once
743 * it reaches the end.
744 *
745 * @method Flash#setLoop
746 * @param {boolean} loop
747 * - True indicates that playback should seek back to start once
748 * the end of a media is reached.
749 * - False indicates that playback should not loop back to the start when the
750 * end of the media is reached.
751 */
752
753/**
754 * Set the value of `mediaGroup` on the swf.
755 *
756 * @method Flash#setMediaGroup
757 * @param {string} mediaGroup
758 * New value of `mediaGroup` to set on the swf.
759 */
760
761/**
762 * Set the value of `controller` on the swf.
763 *
764 * @method Flash#setController
765 * @param {string} controller
766 * New value the current value of `controller` on the swf.
767 */
768
769/**
770 * Get the value of `controls` from the swf. `controls` indicates
771 * whether the native flash controls should be shown or hidden.
772 *
773 * @method Flash#controls
774 * @return {boolean}
775 * - The value of `controls` from the swf.
776 * - True indicates that native controls should be showing.
777 * - False indicates that native controls should be hidden.
778 */
779
780/**
781 * Set the value of the `volume` on the swf. `volume` indicates the current
782 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
783 * so on.
784 *
785 * @method Flash#setVolume
786 * @param {number} percentAsDecimal
787 * The volume percent as a decimal. Value will be between 0-1.
788 */
789
790/**
791 * Set the value of the `muted` on the swf. `muted` indicates that the current
792 * audio level should be silent.
793 *
794 * @method Flash#setMuted
795 * @param {boolean} muted
796 * - True if the audio should be set to silent
797 * - False otherwise
798 */
799
800/**
801 * Set the value of `defaultMuted` on the swf. `defaultMuted` indicates
802 * whether the media should start muted or not. Only changes the default state of the
803 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
804 * current state.
805 *
806 * @method Flash#setDefaultMuted
807 * @param {boolean} defaultMuted
808 * - True indicates that the media should start muted.
809 * - False indicates that the media should not start muted.
810 */
811
812/* Flash Support Testing -------------------------------------------------------- */
813
814/**
815 * Check if the Flash tech is currently supported.
816 *
817 * @return {boolean}
818 * - True for Chrome and Safari Desktop and if flash tech is supported
819 * - False otherwise
820 */
821Flash.isSupported = function() {
822 // for Chrome Desktop and Safari Desktop
823 if ((videojs.browser.IS_CHROME && !videojs.browser.IS_ANDROID) ||
824 (videojs.browser.IS_SAFARI && !videojs.browser.IS_IOS)) {
825 return true;
826 }
827 // for other browsers
828 return Flash.version()[0] >= 10;
829};
830
831// Add Source Handler pattern functions to this tech
832Tech.withSourceHandlers(Flash);
833
834/*
835 * Native source handler for flash, simply passes the source to the swf element.
836 *
837 * @property {Tech~SourceObject} source
838 * The source object
839 *
840 * @property {Flash} tech
841 * The instance of the Flash tech
842 */
843Flash.nativeSourceHandler = {};
844
845/**
846 * Check if the Flash can play the given mime type.
847 *
848 * @param {string} type
849 * The mimetype to check
850 *
851 * @return {string}
852 * 'maybe', or '' (empty string)
853 */
854Flash.nativeSourceHandler.canPlayType = function(type) {
855 if (type in Flash.formats) {
856 return 'maybe';
857 }
858
859 return '';
860};
861
862/**
863 * Check if the media element can handle a source natively.
864 *
865 * @param {Tech~SourceObject} source
866 * The source object
867 *
868 * @param {Object} [options]
869 * Options to be passed to the tech.
870 *
871 * @return {string}
872 * 'maybe', or '' (empty string).
873 */
874Flash.nativeSourceHandler.canHandleSource = function(source, options) {
875 let type;
876
877 /**
878 * Guess the mime type of a file if it does not have one
879 *
880 * @param {Tech~SourceObject} src
881 * The source object to guess the mime type for
882 *
883 * @return {string}
884 * The mime type that was guessed
885 */
886 function guessMimeType(src) {
887 const ext = Url.getFileExtension(src);
888
889 if (ext) {
890 return `video/${ext}`;
891 }
892 return '';
893 }
894
895 if (!source.type) {
896 type = guessMimeType(source.src);
897 } else {
898 // Strip code information from the type because we don't get that specific
899 type = source.type.replace(/;.*/, '').toLowerCase();
900 }
901
902 return Flash.nativeSourceHandler.canPlayType(type);
903};
904
905/**
906 * Pass the source to the swf.
907 *
908 * @param {Tech~SourceObject} source
909 * The source object
910 *
911 * @param {Flash} tech
912 * The instance of the Flash tech
913 *
914 * @param {Object} [options]
915 * The options to pass to the source
916 */
917Flash.nativeSourceHandler.handleSource = function(source, tech, options) {
918 tech.setSrc(source.src);
919};
920
921/**
922 * noop for native source handler dispose, as cleanup will happen automatically.
923 */
924Flash.nativeSourceHandler.dispose = function() {};
925
926// Register the native source handler
927Flash.registerSourceHandler(Flash.nativeSourceHandler);
928
929/**
930 * Flash supported mime types.
931 *
932 * @constant {Object}
933 */
934Flash.formats = {
935 'video/flv': 'FLV',
936 'video/x-flv': 'FLV',
937 'video/mp4': 'MP4',
938 'video/m4v': 'MP4'
939};
940
941/**
942 * Called when the the swf is "ready", and makes sure that the swf is really
943 * ready using {@link Flash#checkReady}
944 *
945 * @param {Object} currSwf
946 * The current swf object
947 */
948Flash.onReady = function(currSwf) {
949 const el = Dom.$('#' + currSwf);
950 const tech = el && el.tech;
951
952 // if there is no el then the tech has been disposed
953 // and the tech element was removed from the player div
954 if (tech && tech.el()) {
955 // check that the flash object is really ready
956 Flash.checkReady(tech);
957 }
958};
959
960/**
961 * The SWF isn't always ready when it says it is. Sometimes the API functions still
962 * need to be added to the object. If it's not ready, we set a timeout to check again
963 * shortly.
964 *
965 * @param {Flash} tech
966 * The instance of the flash tech to check.
967 */
968Flash.checkReady = function(tech) {
969 // stop worrying if the tech has been disposed
970 if (!tech.el()) {
971 return;
972 }
973
974 // check if API property exists
975 if (tech.el().vjs_getProperty) {
976 // tell tech it's ready
977 tech.triggerReady();
978 } else {
979 // wait longer
980 this.setTimeout(function() {
981 Flash.checkReady(tech);
982 }, 50);
983 }
984};
985
986/**
987 * Trigger events from the swf on the Flash Tech.
988 *
989 * @param {number} swfID
990 * The id of the swf that had the event
991 *
992 * @param {string} eventName
993 * The name of the event to trigger
994 */
995Flash.onEvent = function(swfID, eventName) {
996 const tech = Dom.$('#' + swfID).tech;
997 const args = Array.prototype.slice.call(arguments, 2);
998
999 // dispatch Flash events asynchronously for two reasons:
1000 // - Flash swallows any exceptions generated by javascript it
1001 // invokes
1002 // - Flash is suspended until the javascript returns which may cause
1003 // playback performance issues
1004 tech.setTimeout(function() {
1005 tech.trigger(eventName, args);
1006 }, 1);
1007};
1008
1009/**
1010 * Log errors from the swf on the Flash tech.
1011 *
1012 * @param {number} swfID
1013 * The id of the swf that had an error.
1014 *
1015 * @param {string} err
1016 * The error to set on the Flash Tech.
1017 *
1018 * @return {MediaError|undefined}
1019 * - Returns a MediaError when err is 'srcnotfound'
1020 * - Returns undefined otherwise.
1021 */
1022Flash.onError = function(swfID, err) {
1023 const tech = Dom.$('#' + swfID).tech;
1024
1025 // trigger MEDIA_ERR_SRC_NOT_SUPPORTED
1026 if (err === 'srcnotfound') {
1027 return tech.error(4);
1028 }
1029
1030 // trigger a custom error
1031 if (typeof err === 'string') {
1032 tech.error('FLASH: ' + err);
1033 } else {
1034 err.origin = 'flash';
1035 tech.error(err);
1036 }
1037};
1038
1039/**
1040 * Get the current version of Flash that is in use on the page.
1041 *
1042 * @return {Array}
1043 * an array of versions that are available.
1044 */
1045Flash.version = function() {
1046 let version = '0,0,0';
1047
1048 // IE
1049 try {
1050 version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash')
1051 .GetVariable('$version')
1052 .replace(/\D+/g, ',')
1053 .match(/^,?(.+),?$/)[1];
1054
1055 // other browsers
1056 } catch (e) {
1057 try {
1058 if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) {
1059 version = (navigator.plugins['Shockwave Flash 2.0'] ||
1060 navigator.plugins['Shockwave Flash'])
1061 .description
1062 .replace(/\D+/g, ',')
1063 .match(/^,?(.+),?$/)[1];
1064 }
1065 } catch (err) {
1066 // satisfy linter
1067 }
1068 }
1069 return version.split(',');
1070};
1071
1072/**
1073 * Only use for non-iframe embeds.
1074 *
1075 * @param {Object} swf
1076 * The videojs-swf object.
1077 *
1078 * @param {Object} flashVars
1079 * Names and values to use as flash option variables.
1080 *
1081 * @param {Object} params
1082 * Style parameters to set on the object.
1083 *
1084 * @param {Object} attributes
1085 * Attributes to set on the element.
1086 *
1087 * @return {Element}
1088 * The embeded Flash DOM element.
1089 */
1090Flash.embed = function(swf, flashVars, params, attributes) {
1091 const code = Flash.getEmbedCode(swf, flashVars, params, attributes);
1092
1093 // Get element by embedding code and retrieving created element
1094 const obj = Dom.createEl('div', { innerHTML: code }).childNodes[0];
1095
1096 return obj;
1097};
1098
1099/**
1100 * Only use for non-iframe embeds.
1101 *
1102 * @param {Object} swf
1103 * The videojs-swf object.
1104 *
1105 * @param {Object} flashVars
1106 * Names and values to use as flash option variables.
1107 *
1108 * @param {Object} params
1109 * Style parameters to set on the object.
1110 *
1111 * @param {Object} attributes
1112 * Attributes to set on the element.
1113 *
1114 * @return {Element}
1115 * The embeded Flash DOM element.
1116 */
1117Flash.getEmbedCode = function(swf, flashVars, params, attributes) {
1118 const objTag = '<object type="application/x-shockwave-flash" ';
1119 let flashVarsString = '';
1120 let paramsString = '';
1121 let attrsString = '';
1122
1123 // Convert flash vars to string
1124 if (flashVars) {
1125 Object.getOwnPropertyNames(flashVars).forEach(function(key) {
1126 flashVarsString += `${key}=${flashVars[key]}&amp;`;
1127 });
1128 }
1129
1130 // Add swf, flashVars, and other default params
1131 params = mergeOptions({
1132 movie: swf,
1133 flashvars: flashVarsString,
1134 // Required to talk to swf
1135 allowScriptAccess: 'always',
1136 // All should be default, but having security issues.
1137 allowNetworking: 'all'
1138 }, params);
1139
1140 // Create param tags string
1141 Object.getOwnPropertyNames(params).forEach(function(key) {
1142 paramsString += `<param name="${key}" value="${params[key]}" />`;
1143 });
1144
1145 attributes = mergeOptions({
1146 // Add swf to attributes (need both for IE and Others to work)
1147 data: swf,
1148
1149 // Default to 100% width/height
1150 width: '100%',
1151 height: '100%'
1152
1153 }, attributes);
1154
1155 // Create Attributes string
1156 Object.getOwnPropertyNames(attributes).forEach(function(key) {
1157 attrsString += `${key}="${attributes[key]}" `;
1158 });
1159
1160 return `${objTag}${attrsString}>${paramsString}</object>`;
1161};
1162
1163// Run Flash through the RTMP decorator
1164FlashRtmpDecorator(Flash);
1165
1166if (Tech.getTech('Flash')) {
1167 videojs.log.warn('Not using videojs-flash as it appears to already be registered');
1168 videojs.log.warn('videojs-flash should only be used with video.js@6 and above');
1169} else {
1170 videojs.registerTech('Flash', Flash);
1171}
1172
1173Flash.VERSION = VERSION;
1174
1175export default Flash;