Brion VIBBER has uploaded a new change for review. https://gerrit.wikimedia.org/r/165478
Change subject: Work in progress: ogv.js media player for Safari/IE (2 of 3) ...................................................................... Work in progress: ogv.js media player for Safari/IE (2 of 3) Plugin to MwEmbedPlayer for using ogv.js JavaScript or Flash playback of Ogg Theora/Vorbis media in Safari and IE. The actual JS and Flash libraries are in the previous commit in order to make this commit easier to review. This commit adds only the plugin for the desktop player and support code that's common with the mobile player coming in the next commit. Bug: 61823 Change-Id: Ia6b2e66864a596dbb4ba5841b9da135021f38276 --- M MwEmbedModules/EmbedPlayer/EmbedPlayer.php M MwEmbedModules/EmbedPlayer/i18n/en.json M MwEmbedModules/EmbedPlayer/i18n/qqq.json A MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvJs.js A MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvSwf.js M MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js M MwEmbedModules/EmbedPlayer/resources/mw.MediaElement.js M MwEmbedModules/EmbedPlayer/resources/mw.MediaPlayers.js M MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js A resources/ext.tmh.OgvJsSupport.js 10 files changed, 524 insertions(+), 6 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/TimedMediaHandler refs/changes/78/165478/1 diff --git a/MwEmbedModules/EmbedPlayer/EmbedPlayer.php b/MwEmbedModules/EmbedPlayer/EmbedPlayer.php index c60d41c..95efa94 100644 --- a/MwEmbedModules/EmbedPlayer/EmbedPlayer.php +++ b/MwEmbedModules/EmbedPlayer/EmbedPlayer.php @@ -68,6 +68,11 @@ 'scripts'=> "resources/mw.EmbedPlayerVLCApp.js", 'dependencies' => array( 'mediawiki.Uri' ) ), + "mw.EmbedPlayerOgvJs" => array( + 'scripts' => 'resources/mw.EmbedPlayerOgvJs.js', + 'dependencies' => 'ext.tmh.OgvJsSupport', + ), + "mw.EmbedPlayerOgvSwf" => array( 'scripts'=> "resources/mw.EmbedPlayerOgvSwf.js" ), "mw.EmbedPlayerImageOverlay" => array( 'scripts'=> "resources/mw.EmbedPlayerImageOverlay.js" ), "mw.EmbedPlayerVlc" => array( 'scripts'=> "resources/mw.EmbedPlayerVlc.js" ), diff --git a/MwEmbedModules/EmbedPlayer/i18n/en.json b/MwEmbedModules/EmbedPlayer/i18n/en.json index d3221a2..7539465 100644 --- a/MwEmbedModules/EmbedPlayer/i18n/en.json +++ b/MwEmbedModules/EmbedPlayer/i18n/en.json @@ -50,6 +50,8 @@ "mwe-embedplayer-ogg-player-webmNative": "HTML5 WebM player", "mwe-embedplayer-ogg-player-oggPlugin": "Generic Ogg plugin", "mwe-embedplayer-ogg-player-vlcAppPlayer": "VLC for iOS app", + "mwe-embedplayer-ogg-player-ogvJsPlayer": "JavaScript Ogg player", + "mwe-embedplayer-ogg-player-ogvSwfPlayer": "Flash Ogg player", "mwe-embedplayer-ogg-player-quicktime-mozilla": "QuickTime plugin", "mwe-embedplayer-ogg-player-quicktime-activex": "QuickTime ActiveX", "mwe-embedplayer-ogg-player-cortado": "Java Cortado", diff --git a/MwEmbedModules/EmbedPlayer/i18n/qqq.json b/MwEmbedModules/EmbedPlayer/i18n/qqq.json index 0b17710..3e3de78 100644 --- a/MwEmbedModules/EmbedPlayer/i18n/qqq.json +++ b/MwEmbedModules/EmbedPlayer/i18n/qqq.json @@ -24,6 +24,8 @@ "mwe-embedplayer-ogg-player-aacNative": "name of AAC player in configuration screen", "mwe-embedplayer-ogg-player-cortado": "{{optional}}", "mwe-embedplayer-ogg-player-selected": "{{Identical|Selected}}", + "mwe-embedplayer-ogg-player-ogvJsPlayer": "name of ogv.js Ogg JavaScript player in configuration screen", + "mwe-embedplayer-ogg-player-ogvSwfPlayer": "name of ogv.swf Ogg Flash player in configuration screen", "mwe-embedplayer-for_best_experience": "Shown when user's browser doesn't support playing videos. Parameters:\n* $1 - An empty <a> tag. Don't use this parameter.", "mwe-embedplayer-download-warn": "Parameters:\n* $1 - URL", "mwe-embedplayer-do_not_warn_again": "Standard message for disabling\nfuture identical warnings messages", diff --git a/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvJs.js b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvJs.js new file mode 100644 index 0000000..d2ef1b6 --- /dev/null +++ b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvJs.js @@ -0,0 +1,171 @@ +( function( mw, $ ) { "use strict"; + +mw.EmbedPlayerOgvJs = { + + // Instance name: + instanceOf: 'OgvJs', + + // Supported feature set of the cortado applet: + supports: { + 'playHead' : false, // seeking not supported yet + 'pause' : true, + 'stop' : true, + 'fullscreen' : false, + 'sourceSwitch': true, // todo + 'timeDisplay' : true, + 'volumeControl' : false, + 'overlays': true + }, + + /** + * Output the the embed html + */ + embedPlayerHTML: function (optionalCallback) { + + $( this ).text( '... loading ...' ); + + if (mw.isIOS()) { + this._initializeAudioForiOS(); + } + var _this = this; + _this._loadOgvJs( function() { + var options = { + webGL: true, // auto-detect accelerated YUV conversion + base: _this._ogvJsBase() // where to find the Flash shims for IE + }; + if (_this._iOSAudioContext) { + options.audioContext = _this._iOSAudioContext; + } + var player = new OgvJsPlayer(options); + player.id = _this.pid; + player.width = parseInt( _this.getWidth() ); + player.height = parseInt( _this.getHeight() ); + player.src = _this.getSrc(); + if ( _this.getDuration() ) { + player.durationHint = parseFloat( _this.getDuration() ); + } + $( _this ).empty().append( player ); + player.play(); + + // Start the monitor: + _this.monitor(); + + if ( optionalCallback ) { + optionalCallback(); + } + }); + }, + + _iOSAudioContext: undefined, + + _initializeAudioForiOS: function() { + // iOS Safari Web Audio API must be initialized from an input event handler + // This is a temporary (?) hack while we load OgvJsPlayer support code async + if ( this._iOSAudioContext ) { + return; + } + this._iOSAudioContext = mw.OgvJsSupport.initAudioContext(); + }, + + _loadOgvJs: function( callback ) { + if ( typeof OgvJsPlayer === 'undefined' ) { + $.ajax({ + dataType: 'script', + cache: true, + url: this._findOgvJs() + }).done(function() { + callback(); + }); + } else { + callback(); + } + }, + + _ogvJsBase: function() { + return mw.getEmbedPlayerPath() + '/binPlayers/ogv.js'; + }, + + _findOgvJs: function() { + var url = this._ogvJsBase() + '/ogvjs.js?version=0.1.5'; + return url; + }, + + /** + * Get the embed player time + */ + getPlayerElementTime: function() { + this.getPlayerElement(); + var currentTime = 0; + if ( this.playerElement ) { + currentTime = this.playerElement.currentTime; + try { + currentTime = this.playerElement.currentTime; + } catch ( e ) { + mw.log( 'EmbedPlayerOgvJs:: Could not get time from jPlayer: ' + e ); + } + }else{ + mw.log("EmbedPlayerOgvJs:: Could not find playerElement " ); + } + return currentTime; + }, + + /** + * Update the playerElement instance with a pointer to the embed object + */ + getPlayerElement: function() { + if( !$( '#' + this.pid ).length ) { + return false; + }; + this.playerElement = $( '#' + this.pid ).get( 0 ); + return this.playerElement; + }, + + /** + * Issue the doPlay request to the playerElement + * calls parent_play to update interface + */ + play: function() { + this.getPlayerElement(); + this.parent_play(); + if ( this.playerElement ) { + try{ + this.playerElement.play(); + }catch( e ){ + mw.log("EmbedPlayerOgvJs::Could not issue play request"); + } + } + }, + + /** + * Pause playback + * calls parent_pause to update interface + */ + pause: function() { + this.getPlayerElement(); + // Update the interface + this.parent_pause(); + // Call the pause function if it exists: + if ( this.playerElement ) { + try{ + this.playerElement.pause(); + }catch( e ){ + mw.log("EmbedPlayerOgvJs::Could not issue pause request"); + } + } + }, + + playerSwitchSource: function( source, switchCallback, doneCallback ){ + var _this = this; + var src = source.getSrc(); + var vid = this.getPlayerElement(); + vid.stop(); + + switchCallback(); + + // Currently have to tear down the player and make a new one + this.embedPlayerHTML( doneCallback ); + } + +}; + +} )( window.mediaWiki, window.jQuery ); diff --git a/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvSwf.js b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvSwf.js new file mode 100644 index 0000000..afbdd42 --- /dev/null +++ b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayerOgvSwf.js @@ -0,0 +1,175 @@ +( function( mw, $ ) { "use strict"; + +mw.EmbedPlayerOgvSwf = { + + // Instance name: + instanceOf: 'OgvSwf', + + // Supported feature set of the cortado applet: + supports: { + 'playHead' : false, // seeking not supported yet + 'pause' : true, + 'stop' : true, + 'fullscreen' : false, // todo + 'sourceSwitch': true, + 'timeDisplay' : true, + 'volumeControl' : false, + 'overlays': (function() { + if (navigator.userAgent.indexOf('Trident') != -1) { + // IE of some version + if (navigator.userAgent.indexOf('MSIE') == -1) { + // IE 11 drops the 'MSIE' token, and is known + // to handle the overlay case correctly. + return true; + } else if (navigator.userAgent.indexOf('MSIE 10.') !== -1) { + // IE 10 still has MSIE token, but is known + // to handle the overlay case correctly. + } else { + // IE 9 doesn't handle overlay case well; + // at least it doesn't pass mouse events + // so once the control bar disappears we + // never get it back. + return false; + } + } + + // Who knows? Might work! + return true; + })() + }, + + /** + * Output the the embed html + */ + embedPlayerHTML: function () { + + $( this ).text( '... loading ...' ); + + var _this = this; + _this._loadOgvSwf( function() { + var player = new OgvSwfPlayer({ + base: _this._OgvSwfBase() // where to find the Flash .swf files + }); + player.id = _this.pid; + player.width = parseInt( _this.getWidth() ); + player.height = parseInt( _this.getHeight() ); + if (!_this.supports.overlays) { + // Squish to leave space for fixed control bar on IE 9 + player.height -= _this.controlBuilder.getHeight(); + } + player.src = _this.getSrc(); + if ( _this.getDuration() ) { + player.durationHint = parseFloat( _this.getDuration() ); + } + $( _this ).empty().append( player ); + player.play(); + + // Start the monitor: + _this.monitor(); + }); + }, + + _loadOgvSwf: function( callback ) { + if ( typeof OgvSwfPlayer === 'undefined' ) { + $.ajax({ + dataType: 'script', + cache: true, + url: this._findOgvSwf() + }).done(function() { + callback(); + }); + } else { + callback(); + } + }, + + _OgvSwfBase: function() { + return mw.getEmbedPlayerPath() + '/binPlayers/ogv.js'; + }, + + _findOgvSwf: function() { + var url = this._OgvSwfBase() + '/ogvswf.js?version=0.1.2'; + return url; + }, + + /** + * Get the embed player time + */ + getPlayerElementTime: function() { + this.getPlayerElement(); + var currentTime = 0; + if ( this.playerElement ) { + currentTime = this.playerElement.currentTime; + try { + currentTime = this.playerElement.currentTime; + } catch ( e ) { + mw.log( 'EmbedPlayerOgvSwf:: Could not get time from jPlayer: ' + e ); + } + }else{ + mw.log("EmbedPlayerOgvSwf:: Could not find playerElement " ); + } + return currentTime; + }, + + /** + * Update the playerElement instance with a pointer to the embed object + */ + getPlayerElement: function() { + if( !$( '#' + this.pid ).length ) { + return false; + }; + this.playerElement = $( '#' + this.pid ).get( 0 ); + return this.playerElement; + }, + + /** + * Issue the doPlay request to the playerElement + * calls parent_play to update interface + */ + play: function() { + this.getPlayerElement(); + this.parent_play(); + if ( this.playerElement ) { + try{ + this.playerElement.play(); + }catch( e ){ + mw.log("EmbedPlayerOgvSwf::Could not issue play request"); + } + } + }, + + /** + * Pause playback + * calls parent_pause to update interface + */ + pause: function() { + this.getPlayerElement(); + // Update the interface + this.parent_pause(); + // Call the pause function if it exists: + if ( this.playerElement ) { + try{ + this.playerElement.pause(); + }catch( e ){ + mw.log("EmbedPlayerOgvSwf::Could not issue pause request"); + } + } + }, + + playerSwitchSource: function( source, switchCallback, doneCallback ){ + var _this = this; + var src = source.getSrc(); + var vid = this.getPlayerElement(); + if (!this.paused) { + vid.pause(); + } + + switchCallback(); + + // Currently have to tear down the player and make a new one + this.embedPlayerHTML( doneCallback ); + } + +}; + +} )( window.mediaWiki, window.jQuery ); diff --git a/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js index 3749f80..6ee8ccf 100644 --- a/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js +++ b/MwEmbedModules/EmbedPlayer/resources/mw.EmbedTypes.js @@ -89,6 +89,25 @@ 'video/webm; codecs="vp8, vorbis"', ], 'VLCApp' ); +var ogvJsPlayer = new mw.MediaPlayer( 'ogvJsPlayer', [ + 'video/ogg', + 'video/ogg; codecs="theora"', + 'video/ogg; codecs="theora, vorbis"', + 'audio/ogg', + 'audio/ogg; codecs="vorbis"', + 'audio/ogg; codecs="opus"', + 'application/ogg' +], 'OgvJs' ); + +var ogvSwfPlayer = new mw.MediaPlayer( 'ogvSwfPlayer', [ + 'video/ogg', + 'video/ogg; codecs="theora"', + 'video/ogg; codecs="theora, vorbis"', + 'audio/ogg', + 'audio/ogg; codecs="vorbis"', + 'application/ogg' +], 'OgvSwf' ); + // Generic plugin //var oggPluginPlayer = new mw.MediaPlayer( 'oggPlugin', ['video/ogg', 'application/ogg'], 'Generic' ); @@ -314,6 +333,48 @@ if ( mw.isIOS() ) { this.mediaPlayers.addPlayer( vlcAppPlayer ); } + + // ogv.js / ogv.swf detection + var hasTypedArrays = ( window.Uint32Array ), + hasWebAudio = ( window.AudioContext || window.webkitAudioContext ); + + // don't use mw.supportsFlash() as it's hardcoded to false + // we want to use Flash for free codecs here! + var reallyHasFlash = false; + if ( $.client.profile().name === 'msie' ) { + reallyHasFlash = this.testActiveX( 'ShockwaveFlash.ShockwaveFlash' ); + } else { + // check plugins... + } + + if ( hasTypedArrays && hasWebAudio ) { + // ogv.js emscripten version + // + // Works great in Safari, as fast or faster than Flash. + // + // Current Firefox, Chrome, Opera all work great, but use + // native playback by default of course! + // + // Works in IE 10/11 but requires a Flash audio shim and + // doesn't perform quite as well as the pure-Flash build + // on slower machines. Currently disabled for this case, + // but future versions of IE with Web Audio should start + // working one hopes. + // + this.mediaPlayers.addPlayer( ogvJsPlayer ); + } + if ( reallyHasFlash ) { + // ogv.swf crossbridge version + // + // Currently used for IE 9/10/11. Older IE browsers might work, + // but IE 8 doesn't seem to run the base player code. + // + // Other browsers without Web Audio might work, but are currently + // not detected. + // + this.mediaPlayers.addPlayer( ogvSwfPlayer ); + } + // Allow extensions to detect and add their own "players" mw.log("EmbedPlayer::trigger:embedPlayerUpdateMediaPlayersEvent"); $( mw ).trigger( 'embedPlayerUpdateMediaPlayersEvent' , this.mediaPlayers ); diff --git a/MwEmbedModules/EmbedPlayer/resources/mw.MediaElement.js b/MwEmbedModules/EmbedPlayer/resources/mw.MediaElement.js index 02a9d9a..ca878bb 100644 --- a/MwEmbedModules/EmbedPlayer/resources/mw.MediaElement.js +++ b/MwEmbedModules/EmbedPlayer/resources/mw.MediaElement.js @@ -255,10 +255,11 @@ // Prefer native playback ( and prefer WebM over ogg and h.264 ) var namedSourceSet = {}; + var useBogoSlow = false; // use benchmark only for ogv.js/ogv.swf $.each( playableSources, function(inx, source ){ var mimeType = source.mimeType; var player = mw.EmbedTypes.getMediaPlayers().defaultPlayer( mimeType ); - if ( player && player.library == 'Native' ) { + if ( player && ( player.library == 'Native' || player.library == 'OgvJs' || player.library == 'OgvSwf' ) ) { switch( player.id ){ case 'mp3Native': var shortName = 'mp3'; @@ -267,6 +268,11 @@ var shortName = 'aac'; break; case 'oggNative': + var shortName = 'ogg'; + break; + case 'ogvJsPlayer': + case 'ogvSwfPlayer': + useBogoSlow = true; var shortName = 'ogg'; break; case 'webmNative': @@ -309,9 +315,69 @@ // select based on size: // Set via embed resolution closest to relative to display size var minSizeDelta = null; + + // unless we're really slow... + // @fixme move most of this to ogv.js embed code + // @fixme avoid rerunning benchmark all the time, but don't get + // weirdly stuck at slow speed by mistake if opened in debug mode + var bench = function() { + var timer; + if (window.performance && window.performance.now) { + timer = function() { + return window.performance.now(); + } + } else { + timer = function() { + return Date.now(); + } + } + + var ops = 0; + var fibonacci = function(n) { + ops++; + if (n < 2) { + return n; + } else { + return fibonacci(n - 2) + fibonacci(n - 1); + } + }; + + var start = timer(); + var result = fibonacci(30); + var delta = timer() - start; + var bogoSpeed = (ops / delta); + + // 2012 Retina MacBook Pro (Safari 7) ~150,000 + // 2009 Dell T5500 (IE 11) ~100,000 + // iPad Air (iOS 7) ~65,000 + // 2010 MBP / OS X 10.9 (Safari 7) ~62,500 + // 2010 MBP / Win7 VM (IE 11) ~50,000+- + // ^ these play 360p ok + // ----------- line of moderate doom ---------- + // v these play 160p ok + // iPad Mini non-Retina (iOS 8 beta) ~25,000 + // Dell Inspiron Duo (IE 11) ~25,000 + // Surface RT (IE 11) ~18,000 + // iPod Touch 5th-gen (iOS 8 beta) ~16,000 + // ------------ line of total doom ------------ + // v these play only audio, if that + // iPod 4th-gen (iOS 6.1) ~6,750 + // iPhone 3Gs (iOS 6.1) ~4,500 + var bogoSpeedCutoff = 50000; + + mw.log('MediaElement::autoSelectSource::bench: ' + bogoSpeed + ' ops per ms bogo test (' + ops + ' / ' + delta + ')'); + return (bogoSpeed < bogoSpeedCutoff); + }; + var isBogoSlow = (useBogoSlow ? bench() : false); + if( this.parentEmbedId ){ var displayWidth = $('#' + this.parentEmbedId).width(); $.each( namedSourceSet[ codec ], function(inx, source ){ + if ( isBogoSlow && source.height > 240 ) { + // On iOS or slow Windows devices, large videos decoded in JavaScript are a bad idea! + // continue + return true; + } if( source.width && displayWidth ){ var sizeDelta = Math.abs( source.width - displayWidth ); mw.log('MediaElement::autoSelectSource: size delta : ' + sizeDelta + ' for s:' + source.width ); diff --git a/MwEmbedModules/EmbedPlayer/resources/mw.MediaPlayers.js b/MwEmbedModules/EmbedPlayer/resources/mw.MediaPlayers.js index 19e7fde..6f1af43 100644 --- a/MwEmbedModules/EmbedPlayer/resources/mw.MediaPlayers.js +++ b/MwEmbedModules/EmbedPlayer/resources/mw.MediaPlayers.js @@ -33,10 +33,10 @@ this.defaultPlayers['application/vnd.apple.mpegurl'] = ['Native']; - this.defaultPlayers['video/ogg'] = ['Native', 'Vlc', 'Java', 'Generic', 'VLCApp']; + this.defaultPlayers['video/ogg'] = ['Native', 'Vlc', 'OgvJs', 'OgvSwf', 'Java', 'Generic', 'VLCApp']; this.defaultPlayers['video/webm'] = ['Native', 'Vlc', 'VLCApp']; - this.defaultPlayers['application/ogg'] = ['Native', 'Vlc', 'Java', 'Generic', 'VLCApp']; - this.defaultPlayers['audio/ogg'] = ['Native', 'Vlc', 'Java', 'VLCApp']; + this.defaultPlayers['application/ogg'] = ['Native', 'Vlc', 'OgvJs', 'OgvSwf','Java', 'Generic', 'VLCApp']; + this.defaultPlayers['audio/ogg'] = ['Native', 'Vlc', 'OgvJs', 'OgvSwf', 'Java', 'VLCApp']; this.defaultPlayers['audio/mpeg']= ['Native', 'Kplayer']; this.defaultPlayers['audio/mp3']= ['Native', 'Kplayer']; this.defaultPlayers['audio/mp4']= ['Native']; diff --git a/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js b/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js index 98602da..ee48a3a 100644 --- a/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js +++ b/MwEmbedModules/EmbedPlayer/resources/skins/mw.PlayerControlBuilder.js @@ -2214,12 +2214,17 @@ }) ); } + var addedSources = {}; $.each( this.embedPlayer.mediaElement.getPlayableSources(), function( sourceIndex, source ) { // Output the player select code: var supportingPlayers = mw.EmbedTypes.getMediaPlayers().getMIMETypePlayers( source.getMIMEType() ); for ( var i = 0; i < supportingPlayers.length ; i++ ) { - if( supportingPlayers[i].library == 'Native' ){ - addToSourceMenu( source ); + var lib = supportingPlayers[i].library; + if( lib === 'Native' || lib === 'OgvJs' || lib === 'OgvSwf' ){ // @fixme use supports.sourceSwitch ... if preloaded? + if ( !( source.getSrc() in addedSources ) ) { + addedSources[source.getSrc()] = true; + addToSourceMenu( source ); + } } } }); diff --git a/resources/ext.tmh.OgvJsSupport.js b/resources/ext.tmh.OgvJsSupport.js new file mode 100644 index 0000000..a65db29 --- /dev/null +++ b/resources/ext.tmh.OgvJsSupport.js @@ -0,0 +1,31 @@ +( function( $, mw ) { + + mw.OgvJsSupport = { + /** + * Return a stub audio context + */ + initAudioContext: function() { + var AudioContext = window.AudioContext || window.webkitAudioContext; + if (AudioContext) { + var context = new AudioContext(), + node; + if (context.createScriptProcessor) { + node = context.createScriptProcessor(1024, 0, 2); + } else if (context.createJavaScriptNode) { + node = context.createJavaScriptNode(1024, 0, 2); + } else { + throw new Error("Bad version of web audio API?"); + } + + // Don't actually run any audio, just start & stop the node + node.connect(context.destination); + node.disconnect(); + + return context; + } else { + return null; + } + } + } + +} )( jQuery, mw ); -- To view, visit https://gerrit.wikimedia.org/r/165478 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia6b2e66864a596dbb4ba5841b9da135021f38276 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/TimedMediaHandler Gerrit-Branch: master Gerrit-Owner: Brion VIBBER <br...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits