Commit 80f8771d authored by Francois Marier's avatar Francois Marier
Browse files

Add audio plugin 3.2.1 for flowplayer from upstream

http://flowplayer.org/plugins/streaming/audio.html

Signed-off-by: default avatarFrancois Marier <francois@catalyst.net.nz>
parent bea63ab8
The MIT License
Copyright (c) 2008, 2009 Flowplayer Oy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Flowplayer in Mahara
=====================
Website: http://flowplayer.org/plugins/streaming/audio.html
Version: 3.2.1
This flash mp3 player is used by the internal media blocktype.
Changes:
* None
Version history:
3.2.1
-----
Changes:
- Supports cover images via a 'coverImage' configuration option
- now works with securestreaming plugin
Fixes:
- fix to work properly if accessing the ID3 tag fails because Flash security prevents it
- works better if the file does not have the ID3 tag
3.2.0
-----
- added a new plugin event "onDuration" that is dispatched whenever a new duration value is estimated and the
clip.duration value was changed. The new duration value is passed as event argument.
3.1.3
-----
- added timeProvider setter as required by the changed StreamProvider interface
- now checks the crossdomain.xml file to allow reading of the ID3 tag when this file is present in the domain
hosting the audio file
3.1.2
-----
- compatible with the new ConnectionProvider and URLResolver API
3.1.1
-----
Fixes:
- calling closeBuffering() after the audio had finished caused an exception
3.1.0
-----
- compatibility with core 3.1 StreamProvider interface
3.0.4
-----
- fixed to stop audio when stop() is called
3.0.3
-----
- changed to recalculate the duration until the end of the file has been reached,
this is needed to correctly estimate the duration of variable bitrate MP3's
3.0.2
-----
- dispatches the LOAD event when initialized (needed for flowplayer 3.0.2 compatibility)
- fixed crashes of Mac Safari when navigating out of a page that had a playing audio
3.0.1
-----
- First public beta release
version=3.2.1
devkit-dir=../flowplayer.devkit
\ No newline at end of file
<project name="Audio plugin for flowplayer" default="deploy">
<property file="build.generated.properties"/>
<property file="${user.home}/plugin.properties" />
<property file="build.properties" />
<property file="${devkit-dir}/plugin-build.properties" />
<import file="${devkit-dir}/plugin-build.xml"/>
<property name="flowplayer_lib" value="${devkit-dir}/flowplayer.swc" />
<property name="basename" value="flowplayer.audio" />
<property name="releasedir" value="flowplayer.audio" />
<property name="plugin-binary" value="${basename}.swf" />
<property name="plugin-binary-versioned" value="${basename}-${version}.swf" />
<property name="plugin-swc" value="${basename}.swc" />
<property name="plugin-main-class" value="org/flowplayer/audio/AudioProviderFactory.as" />
<target name="release" description="makes a release" depends="build">
<copyrelease targetdir="flowplayer.audio">
<releasefiles>
<fileset dir="${build-dir}">
<include name="${plugin-binary-versioned}"/>
</fileset>
</releasefiles>
</copyrelease>
</target>
</project>
\ No newline at end of file
/* * Copyright 2008, 2009 Flowplayer Oy * * This file is part of FlowPlayer. * * FlowPlayer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * FlowPlayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with FlowPlayer. If not, see <http://www.gnu.org/licenses/>. */ package org.flowplayer.audio { import flash.media.ID3Info; import flash.media.SoundLoaderContext; import flash.net.NetConnection; import flash.net.NetStream; import flash.utils.Dictionary; import org.flowplayer.controller.ConnectionProvider; import org.flowplayer.controller.StreamProvider; import org.flowplayer.controller.TimeProvider; import org.flowplayer.controller.VolumeController; import org.flowplayer.controller.ClipURLResolverHelper; import org.flowplayer.model.Clip; import org.flowplayer.model.Clip; import org.flowplayer.model.ClipEvent; import org.flowplayer.model.ClipEvent; import org.flowplayer.model.ClipEventType; import org.flowplayer.model.DisplayProperties; import org.flowplayer.model.Playlist; import org.flowplayer.model.Plugin; import org.flowplayer.model.PluginEventType; import org.flowplayer.model.PluginModel; import org.flowplayer.model.MediaSize; import org.flowplayer.util.Log; import org.flowplayer.view.Flowplayer; import flash.display.DisplayObject; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.events.TimerEvent; import flash.media.Sound; import flash.media.SoundChannel; import flash.net.URLRequest; import flash.system.Capabilities; import flash.utils.Timer; import flash.display.Loader; /** * @author api */ public class AudioProvider implements StreamProvider, Plugin { private var log:Log = new Log(this); private var _sound:Sound; private var _playing:Boolean; private var _paused:Boolean; private var _durationSeconds:Number; private var _clip:Clip; private var _pausedPosition:Number; private var _channel:SoundChannel; private var _playlist:Playlist; private var _progressTimer:Timer; private var _seeking:Boolean; private var _started:Boolean; private var _volumeController:VolumeController; private var _pauseAfterStart:Boolean; private var _bufferFullDispatched:Boolean; private var _timeProvider:TimeProvider; private var _model:PluginModel; private var _lastDurationDispatched:Number = 0; private var _imageLoader:Loader; private var _imageDisplay:DisplayObject = null; private var context:SoundLoaderContext; private var _screen:DisplayProperties; private var _clipUrlResolverHelper:ClipURLResolverHelper; public function stopBuffering():void { closeSound(); resetState(); } public function stop(event:ClipEvent, closeStream:Boolean = false):void { seek(null, 0); if (_channel) { log.debug("in stop(), stopping channel"); _channel.stop(); } if (closeStream) { closeSound(); } resetState(); if (event && _clip) { _clip.dispatchEvent(event); } } private function closeSound():void { try { _sound.close(); } catch (e:Error) { // ignore } } private function resetState():void { _playing = false; _paused = false; _started = false; _bufferFullDispatched = false; _durationSeconds = 0; _pausedPosition = 0; if (_progressTimer) { _progressTimer.stop(); } } public function attachStream(video:DisplayObject):void { } private function doLoad():void { } public function load(event:ClipEvent, clip:Clip, pauseAfterStart:Boolean = true):void { log.debug("load()"); resetState(); if (_clip == clip) { log.debug("load() reusing existing sound object"); addListeners(_sound); play(0); _clip.dispatch(ClipEventType.BEGIN); _clip.dispatch(ClipEventType.START); } else { log.debug("load() creating new sound object"); _clip = clip; _sound = new Sound(); context = new SoundLoaderContext(1000, true); if (clip.getCustomProperty("coverImage")) { var cover:Object = getCoverImage(clip); log.debug("Loading Artwork For Audio " + cover.url); _imageLoader = new Loader(); _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageComplete); _imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onImageError); _imageLoader.load(new URLRequest(cover.url)); } else { playAudio(); } } _pauseAfterStart = pauseAfterStart; } private function getCoverImage(clip:Clip):Object { var cover:Object = clip.getCustomProperty("coverImage"); if (cover is String) return { url: "" + cover }; if (cover.hasOwnProperty("scaling")) { _clip.setScaling(cover["scaling"]); } return cover; } private function playAudio():void { addListeners(_sound); _clipUrlResolverHelper.resolveClipUrl(_clip, function onClipUrlResolved(clip:Clip):void { _sound.load(new URLRequest(clip.url), context); play(0); }); } private function onImageError(error:IOErrorEvent):void { log.debug("Cover artwork doesn't exist playing now"); playAudio(); } private function onImageComplete(event:Event):void { log.debug("Cover downloaded playing now"); _imageDisplay = _imageLoader.content; _clip.originalWidth = _imageDisplay.width; _clip.originalHeight = _imageDisplay.height; playAudio(); } private function addListeners(sound:Sound):void { sound.addEventListener(ProgressEvent.PROGRESS, onProgress); sound.addEventListener(Event.SOUND_COMPLETE, onComplete); sound.addEventListener(IOErrorEvent.IO_ERROR, onIoError); sound.addEventListener(Event.ID3, onId3); _progressTimer = new Timer(200); _progressTimer.addEventListener(TimerEvent.TIMER, onProgressTimer); _progressTimer.start(); } private function onIoError(event:IOErrorEvent):void { log.error("Unable to load audio file: " + event.text); //dispatching this error causes crashes in Safari: // _clip.dispatch(ClipEventType.ERROR, "Unable to load audio file: " + event.text); } private function addId3Metadata():void { var metadata:Object = new Object(); try { var tag:ID3Info = _sound.id3; } catch (e:Error) { log.warn("unable to access ID3 tag: " + e); } for (var prop:String in tag) { log.debug(prop + ": " + _sound.id3[prop]); metadata[prop] = _sound.id3[prop]; } _clip.metaData = metadata; } private function onId3(event:Event):void { log.debug("onId3(), _started == " + _started); addId3Metadata(); if (_started) return; log.debug("dispatching START"); _clip.dispatch(ClipEventType.METADATA); _clip.dispatch(ClipEventType.START); _started = true; if (_pauseAfterStart) { pause(new ClipEvent(ClipEventType.PAUSE)); } } private function onProgress(event:ProgressEvent):void { if (_playing) return; _playing = true; _clip.dispatch(ClipEventType.BEGIN); if (!_clip.metaData) { _clip.dispatch(ClipEventType.START); } } private function onProgressTimer(event:TimerEvent):void { estimateDuration(); if (! _sound.bytesTotal > 0) return; if (! _sound.bytesLoaded > 0) return; if(_sound.isBuffering == true && _sound.bytesTotal > _sound.bytesLoaded) { _clip.dispatch(ClipEventType.BUFFER_EMPTY); } else if (! _bufferFullDispatched){ _clip.dispatch(ClipEventType.BUFFER_FULL); _bufferFullDispatched = true; } } private function estimateDuration():void { var durationSecs:Number = (_sound.length/(_sound.bytesLoaded/_sound.bytesTotal))/1000; if (Math.abs(_lastDurationDispatched - durationSecs) >= 0.5) { _lastDurationDispatched = durationSecs; log.debug("dispatching onDuration(), " + durationSecs); _model.dispatch(PluginEventType.PLUGIN_EVENT, "onDuration", durationSecs); } _clip.durationFromMetadata = durationSecs; } private function onComplete(event:Event):void { // dispatch a before event because the finish has default behavior that can be prevented by listeners _clip.dispatchBeforeEvent(new ClipEvent(ClipEventType.FINISH)); } public function getVideo(clip:Clip):DisplayObject { log.debug("getVideo() " + _imageDisplay); return _imageDisplay; } public function resume(event:ClipEvent):void { log.debug("resume"); _paused = false; play(_pausedPosition); if (event) { _clip.dispatchEvent(event); } } public function pause(event:ClipEvent):void { log.debug("pause"); _paused = true; _pausedPosition = _channel.position; _channel.stop(); if (event) { _clip.dispatchEvent(event); } } public function seek(event:ClipEvent, seconds:Number):void { if (! _channel) return; _channel.stop(); _seeking = true; play(seconds * 1000); if (event && _clip) { _clip.dispatchEvent(event); } if (_paused) { _pausedPosition = _channel.position; _channel.stop(); } } private function play(posMillis:Number):void { _channel = _sound.play(posMillis, 0); _volumeController.soundChannel = _channel; } public function get stopping():Boolean { return false; } public function get allowRandomSeek():Boolean { return false; } public function get bufferStart():Number { return 0; } public function get playlist():Playlist { return _playlist; } public function get time():Number { if (_timeProvider) { return _timeProvider.getTime(null); } return _channel ? _channel.position / 1000 : 0; } public function get bufferEnd():Number { return _sound && _clip ? _sound.bytesLoaded / _sound.bytesTotal * _clip.duration : 0; } public function get fileSize():Number { return _sound ? _sound.bytesLoaded : 0; } public function set playlist(playlist:Playlist):void { _playlist = playlist; } public function set netStreamClient(client:Object):void { } public function set volumeController(controller:VolumeController):void { _volumeController = controller; } public function onConfig(model:PluginModel):void { _model = model; model.dispatchOnLoad(); } public function getDefaultConfig():Object { return null; } public function onLoad(player:Flowplayer):void { _screen = player.pluginRegistry.getPlugin("screen") as DisplayProperties; _clipUrlResolverHelper = new ClipURLResolverHelper(player, this); } public function addConnectionCallback(name:String, listener:Function):void { } public function addStreamCallback(name:String, listener:Function):void { } public function get netStream():NetStream { return null; } public function get netConnection():NetConnection { return null; } public function getDefaultConnectionProvider():ConnectionProvider { return null; } public function set timeProvider(timeProvider:TimeProvider):void { _timeProvider = timeProvider; } /** * the value of this property is "audio" */ public function get type():String { return "audio"; } public function switchStream(event:ClipEvent, clip:Clip, netStreamPlayOptions:Object = null):void { } public function get streamCallbacks():Dictionary { return null; } } }
\ No newline at end of file
/* * Copyright 2008, 2009 Flowplayer Oy * * This file is part of FlowPlayer. * * FlowPlayer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * FlowPlayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with FlowPlayer. If not, see <http://www.gnu.org/licenses/>. */ package org.flowplayer.audio { import org.flowplayer.model.PluginFactory; import flash.display.Sprite; /** * @author api */ public class AudioProviderFactory extends Sprite implements PluginFactory { public function newPlugin():Object { return new AudioProvider(); } }}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment