mirror of
https://github.com/videojs/video.js.git
synced 2025-04-15 11:56:32 +02:00
IE8 compatiblity fixes - Babel loose mode and ES5-shim Reverted to old isPlainObject to fix IE8 Lodash.isplainobject was throwing a "Member not found error" on elements, specifically the 'custom' track element being passed in options. (turned out to be that we were using lodash modern, not compat) Fixed full-window mode in IE8 by fixing fullscreen API check Fixed the swf events by creating the object in component.createEl fixes #2184 Added es5 shim and sham to the sandbox example related to #1687
407 lines
11 KiB
JavaScript
407 lines
11 KiB
JavaScript
/**
|
|
* @fileoverview VideoJS-SWF - Custom Flash Player with HTML5-ish API
|
|
* https://github.com/zencoder/video-js-swf
|
|
* Not using setupTriggers. Using global onEvent func to distribute events
|
|
*/
|
|
|
|
import Tech from './tech';
|
|
import * as Dom from '../utils/dom.js';
|
|
import * as Url from '../utils/url.js';
|
|
import { createTimeRange } from '../utils/time-ranges.js';
|
|
import FlashRtmpDecorator from './flash-rtmp';
|
|
import Component from '../component';
|
|
import window from 'global/window';
|
|
import assign from 'object.assign';
|
|
|
|
let navigator = window.navigator;
|
|
/**
|
|
* Flash Media Controller - Wrapper for fallback SWF API
|
|
*
|
|
* @param {Player} player
|
|
* @param {Object=} options
|
|
* @param {Function=} ready
|
|
* @constructor
|
|
*/
|
|
class Flash extends Tech {
|
|
|
|
constructor(options, ready){
|
|
super(options, ready);
|
|
|
|
// If source was supplied pass as a flash var.
|
|
if (options.source) {
|
|
this.ready(function(){
|
|
this.setSource(options.source);
|
|
});
|
|
}
|
|
|
|
// Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
|
|
// This allows resetting the playhead when we catch the reload
|
|
if (options.startTime) {
|
|
this.ready(function(){
|
|
this.load();
|
|
this.play();
|
|
this.currentTime(options.startTime);
|
|
});
|
|
}
|
|
|
|
// Add global window functions that the swf expects
|
|
// A 4.x workflow we weren't able to solve for in 5.0
|
|
// because of the need to hard code these functions
|
|
// into the swf for security reasons
|
|
window.videojs = window.videojs || {};
|
|
window.videojs.Flash = window.videojs.Flash || {};
|
|
window.videojs.Flash.onReady = Flash.onReady;
|
|
window.videojs.Flash.onEvent = Flash.onEvent;
|
|
window.videojs.Flash.onError = Flash.onError;
|
|
}
|
|
|
|
createEl() {
|
|
let options = this.options();
|
|
|
|
// Generate ID for swf object
|
|
let objId = options.techId;
|
|
|
|
// Merge default flashvars with ones passed in to init
|
|
let flashVars = assign({
|
|
|
|
// SWF Callback Functions
|
|
'readyFunction': 'videojs.Flash.onReady',
|
|
'eventProxyFunction': 'videojs.Flash.onEvent',
|
|
'errorEventProxyFunction': 'videojs.Flash.onError',
|
|
|
|
// Player Settings
|
|
'autoplay': options.autoplay,
|
|
'preload': options.preload,
|
|
'loop': options.loop,
|
|
'muted': options.muted
|
|
|
|
}, options.flashVars);
|
|
|
|
// Merge default parames with ones passed in
|
|
let params = assign({
|
|
'wmode': 'opaque', // Opaque is needed to overlay controls, but can affect playback performance
|
|
'bgcolor': '#000000' // Using bgcolor prevents a white flash when the object is loading
|
|
}, options.params);
|
|
|
|
// Merge default attributes with ones passed in
|
|
let attributes = assign({
|
|
'id': objId,
|
|
'name': objId, // Both ID and Name needed or swf to identify itself
|
|
'class': 'vjs-tech'
|
|
}, options.attributes);
|
|
|
|
this.el_ = Flash.embed(options.swf, flashVars, params, attributes);
|
|
this.el_.tech = this;
|
|
|
|
return this.el_;
|
|
}
|
|
|
|
play() {
|
|
this.el_.vjs_play();
|
|
}
|
|
|
|
pause() {
|
|
this.el_.vjs_pause();
|
|
}
|
|
|
|
src(src) {
|
|
if (src === undefined) {
|
|
return this.currentSrc();
|
|
}
|
|
|
|
// Setting src through `src` not `setSrc` will be deprecated
|
|
return this.setSrc(src);
|
|
}
|
|
|
|
setSrc(src) {
|
|
// Make sure source URL is absolute.
|
|
src = Url.getAbsoluteURL(src);
|
|
this.el_.vjs_src(src);
|
|
|
|
// Currently the SWF doesn't autoplay if you load a source later.
|
|
// e.g. Load player w/ no source, wait 2s, set src.
|
|
if (this.autoplay()) {
|
|
var tech = this;
|
|
this.setTimeout(function(){ tech.play(); }, 0);
|
|
}
|
|
}
|
|
|
|
setCurrentTime(time) {
|
|
this.lastSeekTarget_ = time;
|
|
this.el_.vjs_setProperty('currentTime', time);
|
|
super.setCurrentTime();
|
|
}
|
|
|
|
currentTime(time) {
|
|
// when seeking make the reported time keep up with the requested time
|
|
// by reading the time we're seeking to
|
|
if (this.seeking()) {
|
|
return this.lastSeekTarget_ || 0;
|
|
}
|
|
return this.el_.vjs_getProperty('currentTime');
|
|
}
|
|
|
|
currentSrc() {
|
|
if (this.currentSource_) {
|
|
return this.currentSource_.src;
|
|
} else {
|
|
return this.el_.vjs_getProperty('currentSrc');
|
|
}
|
|
}
|
|
|
|
load() {
|
|
this.el_.vjs_load();
|
|
}
|
|
|
|
poster() {
|
|
this.el_.vjs_getProperty('poster');
|
|
}
|
|
|
|
// poster images are not handled by the Flash tech so make this a no-op
|
|
setPoster() {}
|
|
|
|
buffered() {
|
|
return createTimeRange(0, this.el_.vjs_getProperty('buffered'));
|
|
}
|
|
|
|
supportsFullScreen() {
|
|
return false; // Flash does not allow fullscreen through javascript
|
|
}
|
|
|
|
enterFullScreen() {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Create setters and getters for attributes
|
|
const _api = Flash.prototype;
|
|
const _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(',');
|
|
const _readOnly = 'error,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight'.split(',');
|
|
|
|
function _createSetter(attr){
|
|
var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
|
|
_api['set'+attrUpper] = function(val){ return this.el_.vjs_setProperty(attr, val); };
|
|
}
|
|
function _createGetter(attr) {
|
|
_api[attr] = function(){ return this.el_.vjs_getProperty(attr); };
|
|
}
|
|
|
|
// Create getter and setters for all read/write attributes
|
|
for (let i = 0; i < _readWrite.length; i++) {
|
|
_createGetter(_readWrite[i]);
|
|
_createSetter(_readWrite[i]);
|
|
}
|
|
|
|
// Create getters for read-only attributes
|
|
for (let i = 0; i < _readOnly.length; i++) {
|
|
_createGetter(_readOnly[i]);
|
|
}
|
|
|
|
/* Flash Support Testing -------------------------------------------------------- */
|
|
|
|
Flash.isSupported = function(){
|
|
return Flash.version()[0] >= 10;
|
|
// return swfobject.hasFlashPlayerVersion('10');
|
|
};
|
|
|
|
// Add Source Handler pattern functions to this tech
|
|
Tech.withSourceHandlers(Flash);
|
|
|
|
/**
|
|
* The default native source handler.
|
|
* This simply passes the source to the video element. Nothing fancy.
|
|
* @param {Object} source The source object
|
|
* @param {Flash} tech The instance of the Flash tech
|
|
*/
|
|
Flash.nativeSourceHandler = {};
|
|
|
|
/**
|
|
* Check Flash can handle the source natively
|
|
* @param {Object} source The source object
|
|
* @return {String} 'probably', 'maybe', or '' (empty string)
|
|
*/
|
|
Flash.nativeSourceHandler.canHandleSource = function(source){
|
|
var type;
|
|
|
|
function guessMimeType(src) {
|
|
var ext = Url.getFileExtension(src);
|
|
if (ext) {
|
|
return `video/${ext}`;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
if (!source.type) {
|
|
type = guessMimeType(source.src);
|
|
} else {
|
|
// Strip code information from the type because we don't get that specific
|
|
type = source.type.replace(/;.*/, '').toLowerCase();
|
|
}
|
|
|
|
if (type in Flash.formats) {
|
|
return 'maybe';
|
|
}
|
|
|
|
return '';
|
|
};
|
|
|
|
/**
|
|
* Pass the source to the flash object
|
|
* Adaptive source handlers will have more complicated workflows before passing
|
|
* video data to the video element
|
|
* @param {Object} source The source object
|
|
* @param {Flash} tech The instance of the Flash tech
|
|
*/
|
|
Flash.nativeSourceHandler.handleSource = function(source, tech){
|
|
tech.setSrc(source.src);
|
|
};
|
|
|
|
/**
|
|
* Clean up the source handler when disposing the player or switching sources..
|
|
* (no cleanup is needed when supporting the format natively)
|
|
*/
|
|
Flash.nativeSourceHandler.dispose = function(){};
|
|
|
|
// Register the native source handler
|
|
Flash.registerSourceHandler(Flash.nativeSourceHandler);
|
|
|
|
Flash.formats = {
|
|
'video/flv': 'FLV',
|
|
'video/x-flv': 'FLV',
|
|
'video/mp4': 'MP4',
|
|
'video/m4v': 'MP4'
|
|
};
|
|
|
|
Flash.onReady = function(currSwf){
|
|
let el = Dom.getEl(currSwf);
|
|
let tech = el && el.tech;
|
|
|
|
// if there is no el then the tech has been disposed
|
|
// and the tech element was removed from the player div
|
|
if (tech && tech.el()) {
|
|
// check that the flash object is really ready
|
|
Flash.checkReady(tech);
|
|
}
|
|
};
|
|
|
|
// The SWF isn't always ready when it says it is. Sometimes the API functions still need to be added to the object.
|
|
// If it's not ready, we set a timeout to check again shortly.
|
|
Flash.checkReady = function(tech){
|
|
// stop worrying if the tech has been disposed
|
|
if (!tech.el()) {
|
|
return;
|
|
}
|
|
|
|
// check if API property exists
|
|
if (tech.el().vjs_getProperty) {
|
|
// tell tech it's ready
|
|
tech.triggerReady();
|
|
} else {
|
|
// wait longer
|
|
this.setTimeout(function(){
|
|
Flash['checkReady'](tech);
|
|
}, 50);
|
|
}
|
|
};
|
|
|
|
// Trigger events from the swf on the player
|
|
Flash.onEvent = function(swfID, eventName){
|
|
let tech = Dom.getEl(swfID).tech;
|
|
tech.trigger(eventName);
|
|
};
|
|
|
|
// Log errors from the swf
|
|
Flash.onError = function(swfID, err){
|
|
const tech = Dom.getEl(swfID).tech;
|
|
const msg = 'FLASH: '+err;
|
|
|
|
if (err === 'srcnotfound') {
|
|
tech.trigger('error', { code: 4, message: msg });
|
|
|
|
// errors we haven't categorized into the media errors
|
|
} else {
|
|
tech.trigger('error', msg);
|
|
}
|
|
};
|
|
|
|
// Flash Version Check
|
|
Flash.version = function(){
|
|
let version = '0,0,0';
|
|
|
|
// IE
|
|
try {
|
|
version = new window.ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
|
|
|
// other browsers
|
|
} catch(e) {
|
|
try {
|
|
if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin){
|
|
version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
|
}
|
|
} catch(err) {}
|
|
}
|
|
return version.split(',');
|
|
};
|
|
|
|
// Flash embedding method. Only used in non-iframe mode
|
|
Flash.embed = function(swf, flashVars, params, attributes){
|
|
const code = Flash.getEmbedCode(swf, flashVars, params, attributes);
|
|
|
|
// Get element by embedding code and retrieving created element
|
|
const obj = Dom.createEl('div', { innerHTML: code }).childNodes[0];
|
|
|
|
return obj;
|
|
};
|
|
|
|
Flash.getEmbedCode = function(swf, flashVars, params, attributes){
|
|
const objTag = '<object type="application/x-shockwave-flash" ';
|
|
let flashVarsString = '';
|
|
let paramsString = '';
|
|
let attrsString = '';
|
|
|
|
// Convert flash vars to string
|
|
if (flashVars) {
|
|
Object.getOwnPropertyNames(flashVars).forEach(function(key){
|
|
flashVarsString += `${key}=${flashVars[key]}&`;
|
|
});
|
|
}
|
|
|
|
// Add swf, flashVars, and other default params
|
|
params = assign({
|
|
'movie': swf,
|
|
'flashvars': flashVarsString,
|
|
'allowScriptAccess': 'always', // Required to talk to swf
|
|
'allowNetworking': 'all' // All should be default, but having security issues.
|
|
}, params);
|
|
|
|
// Create param tags string
|
|
Object.getOwnPropertyNames(params).forEach(function(key){
|
|
paramsString += `<param name="${key}" value="${params[key]}" />`;
|
|
});
|
|
|
|
attributes = assign({
|
|
// Add swf to attributes (need both for IE and Others to work)
|
|
'data': swf,
|
|
|
|
// Default to 100% width/height
|
|
'width': '100%',
|
|
'height': '100%'
|
|
|
|
}, attributes);
|
|
|
|
// Create Attributes string
|
|
Object.getOwnPropertyNames(attributes).forEach(function(key){
|
|
attrsString += `${key}="${attributes[key]}" `;
|
|
});
|
|
|
|
return `${objTag}${attrsString}>${paramsString}</object>`;
|
|
};
|
|
|
|
// Run Flash through the RTMP decorator
|
|
FlashRtmpDecorator(Flash);
|
|
|
|
Component.registerComponent('Flash', Flash);
|
|
export default Flash;
|