Commit 98d822e2 authored by Daniel Rossi's avatar Daniel Rossi

- #163 possible fixes for playlist replacement memory leak. unbind and clear...

- #163 possible fixes for playlist replacement memory leak. unbind and clear all timer events and listeners where completed, use weak references when unable to set listener method callbacks, initialise and reset playlist clips list, reset screen display list when changing playlists. stop and close the connection and stream when replacing clips.
parent f88c8445
......@@ -286,14 +286,18 @@ package org.flowplayer.view {
private function startTimer():void {
_hideTimer = new Timer(_model.displayTime * 1000, 1);
_hideTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
function(event:TimerEvent):void {
log.debug("display time complete");
hide(_model.fadeSpeed);
_hideTimer.stop();
});
_hideTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onHideComplete);
_hideTimer.start();
}
private function onHideComplete(event:TimerEvent):void
{
log.debug("display time complete");
hide(_model.fadeSpeed);
_hideTimer.reset();
_hideTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onHideComplete);
_hideTimer = null;
}
CONFIG::freeVersion
public function setModel(model:Logo):void {
......
......@@ -481,7 +481,12 @@ package org.flowplayer.view {
var time:Number = _player.status.time;
if (_playDetectTimer) {
_playDetectTimer.reset();
_playDetectTimer = null;
}
_playDetectTimer = new Timer(200);
//#163 use weak reference event here
_playDetectTimer.addEventListener(TimerEvent.TIMER,
function(event:TimerEvent):void {
var currentTime:Number = _player.status.time;
......@@ -494,7 +499,7 @@ package org.flowplayer.view {
} else {
log.debug("not started yet, currentTime " + currentTime + ", time " + time);
}
});
}, false, 0, true);
log.debug("doStart(), starting timer");
_playDetectTimer.start();
}
......
......@@ -84,13 +84,20 @@ package org.flowplayer.controller {
private function createDurationTracker(clip:Clip):void {
if (durationTracker) {
durationTracker.stop();
clearDurationTracker();
}
durationTracker = new PlayTimeTracker(clip, this);
durationTracker.addEventListener(TimerEvent.TIMER_COMPLETE, durationReached);
durationTracker.start();
}
private function clearDurationTracker():void
{
durationTracker.stop();
durationTracker.removeEventListener(TimerEvent.TIMER_COMPLETE, durationReached);
durationTracker = null;
}
public function get time():Number {
if (!durationTracker) return 0;
var time:Number = durationTracker.time;
......@@ -166,8 +173,8 @@ package org.flowplayer.controller {
private function stop(event:ClipEvent, closeStream:Boolean, silent:Boolean = false):void {
log.debug("stop " + durationTracker);
if (durationTracker) {
durationTracker.stop();
durationTracker.time = 0;
clearDurationTracker();
}
doStop(silent ? null : event, closeStream);
}
......
......@@ -64,6 +64,7 @@ package org.flowplayer.controller {
log.debug("stop()");
if (_timer && _timer.running) {
_timer.stop();
_timer.removeEventListener(TimerEvent.TIMER, onTimer);
}
}
......
......@@ -17,30 +17,30 @@
*/
package org.flowplayer.controller {
import flash.display.DisplayObject;
import flash.errors.IOError;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Dictionary;
import flash.utils.Timer;
import org.flowplayer.model.Clip;
import org.flowplayer.model.ClipError;
import org.flowplayer.model.ClipEvent;
import org.flowplayer.model.ClipEventType;
import org.flowplayer.model.Playlist;
import org.flowplayer.model.PluginModel;
import org.flowplayer.model.ProviderModel;
import org.flowplayer.util.Assert;
import org.flowplayer.util.Log;
import org.flowplayer.view.Flowplayer;
CONFIG::FLASH_10_1 {
import org.flowplayer.view.StageVideoWrapper;
}
import flash.display.DisplayObject;
import flash.errors.IOError;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.utils.Dictionary;
import flash.utils.Timer;
import org.flowplayer.model.Clip;
import org.flowplayer.model.ClipError;
import org.flowplayer.model.ClipEvent;
import org.flowplayer.model.ClipEventType;
import org.flowplayer.model.Playlist;
import org.flowplayer.model.PluginModel;
import org.flowplayer.model.ProviderModel;
import org.flowplayer.util.Assert;
import org.flowplayer.util.Log;
import org.flowplayer.view.Flowplayer;
CONFIG::FLASH_10_1 {
import org.flowplayer.view.StageVideoWrapper;
}
/**
* A StreamProvider that does it's job using the Flash's NetStream class.
......@@ -117,7 +117,7 @@ CONFIG::FLASH_10_1 {
}
private function _load(clip:Clip, pauseAfterStart:Boolean, attempts:int = 3):void {
Assert.notNull(clip, "load(clip): clip cannot be null");
Assert.notNull(clip, "load(clip): clip cannot be null");
_paused = false;
_stopping = false;
_attempts = attempts;
......@@ -832,6 +832,13 @@ CONFIG::FLASH_10_1 {
if (_seekTarget < 0) return;
if (_seekTargetWaitTimer && _seekTargetWaitTimer.running) return;
log.debug("starting seek target wait timer");
if (_seekTargetWaitTimer) {
_seekTargetWaitTimer.reset();
_seekTargetWaitTimer.removeEventListener(TimerEvent.TIMER, onSeekTargetWait);
_seekTargetWaitTimer = null;
}
_seekTargetWaitTimer = new Timer(200);
_seekTargetWaitTimer.addEventListener(TimerEvent.TIMER, onSeekTargetWait);
_seekTargetWaitTimer.start();
......@@ -840,7 +847,8 @@ CONFIG::FLASH_10_1 {
private function onSeekTargetWait(event:TimerEvent):void {
//#104 if the updated time is a fraction less than the seek target time ie for HDS, use a bitwise rounding so the seek time can stop.
if ((time|0) >= (_seekTarget|0)) {
_seekTargetWaitTimer.stop();
_seekTargetWaitTimer.reset();
_seekTargetWaitTimer.removeEventListener(TimerEvent.TIMER, onSeekTargetWait);
log.debug("dispatching onSeek");
dispatchPlayEvent(ClipEventType.SEEK, _seekTarget);
_seekTarget = -1;
......@@ -876,12 +884,14 @@ CONFIG::FLASH_10_1 {
try {
netStream.close();
_netStream.removeEventListener(NetStatusEvent.NET_STATUS, _onNetStatus);
_netStream = null;
} catch (e:Error) {
}
if (_connection) {
_connection.close();
_connection.removeEventListener(NetStatusEvent.NET_STATUS, _onNetStatus);
_connection = null;
}
......
......@@ -90,10 +90,11 @@ package org.flowplayer.controller {
doConnect(_rtmpConnector, _proxyType, objectEncoding, connArgs);
// RTMPT connect is started after 250 ms
//#163 weak reference
var delay:Timer = new Timer(_failOverDelay, 1);
delay.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void {
doConnect(_rtmptConnector, _proxyType, objectEncoding, connArgs);
});
}, false, 0 , true);
delay.start();
} else {
......
......@@ -57,7 +57,8 @@ package org.flowplayer.controller {
if (_connectionClient) {
_connection.client = _connectionClient;
}
_connection.addEventListener(NetStatusEvent.NET_STATUS, _onConnectionStatus);
//#163 weak reference to listener
_connection.addEventListener(NetStatusEvent.NET_STATUS, _onConnectionStatus, false, 0 , true);
log.debug("netConnectionUrl is " + _url);
if (connectionArgs && connectionArgs.length > 0) {
......
......@@ -329,7 +329,9 @@ package org.flowplayer.controller {
}
private function replacePlaylistAndPlay(clips:Object):void {
stop();
//#163 stop the connection and stream
_state.stop(true, true);
if (clips is Clip) {
_playList.replaceClips(clips as Clip);
} else {
......
......@@ -28,7 +28,6 @@ package org.flowplayer.controller {
private var _onLastSecondDispatched:Boolean;
private var _controller:MediaController;
private var _endDetectTimer:Timer;
private var _wasPaused:Boolean = false;
private var _lastTimeDetected:Number;
public function PlayTimeTracker(clip:Clip, controller:MediaController) {
......@@ -52,7 +51,9 @@ package org.flowplayer.controller {
public function stop():void {
if (!_progressTimer) return;
_storedTime = time;
_progressTimer.stop();
_progressTimer.reset();
_progressTimer.removeEventListener(TimerEvent.TIMER, checkProgress);
_progressTimer = null;
log.debug("stopped at time " + _storedTime);
}
......@@ -129,27 +130,29 @@ package org.flowplayer.controller {
private function startEndTimer(clip:Clip):void {
bindEndListeners();
_endDetectTimer.addEventListener(TimerEvent.TIMER,
function(event:TimerEvent):void {
log.debug("last time detected == " + _lastTimeDetected);
if(time == _lastTimeDetected && _endDetectTimer.running || durationReached) {
log.debug("clip has reached his end, timer stopped");
_endDetectTimer.reset();
completelyPlayed();
}
_lastTimeDetected = time;
}
);
_endDetectTimer.addEventListener(TimerEvent.TIMER, onEndTime);
log.debug("starting end detect timer");
_endDetectTimer.start();
}
private function onEndTime(event:TimerEvent):void
{
log.debug("last time detected == " + _lastTimeDetected);
if(time == _lastTimeDetected && _endDetectTimer.running || durationReached) {
log.debug("clip has reached his end, timer stopped");
_endDetectTimer.reset();
completelyPlayed();
}
_lastTimeDetected = time;
}
private function completelyPlayed():void {
if(_endDetectTimer.running) {
unbindEndListeners();
_endDetectTimer.reset();
_endDetectTimer.removeEventListener(TimerEvent.TIMER, onEndTime);
_endDetectTimer = null;
}
......@@ -182,6 +185,8 @@ package org.flowplayer.controller {
log.debug("this cuepoint already fired");
}
}
points = null;
}
private function collectCuepoints(clip:Clip, timeRounded:Number):Array {
......
......@@ -104,6 +104,7 @@ package org.flowplayer.controller {
dispatchEvent(PlayerEvent.volume(this.volume));
if (!_storeDelayTimer.running) {
log.info("starting delay timer");
_storeDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerDelayComplete);
_storeDelayTimer.start();
}
}
......@@ -123,6 +124,7 @@ package org.flowplayer.controller {
private function storeVolume(muted:Boolean = false):void {
log.info("persisting volume level");
_storeDelayTimer.stop();
_storeDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerDelayComplete);
_storedVolume.volume = _soundTransform.volume;
_storedVolume.muted = muted;
_storedVolume.persist();
......
......@@ -23,8 +23,8 @@ package org.flowplayer.model {
* @author api
*/
public class ClipEventSupport extends ClipEventDispatcher {
private var _clips:Array;
private var _commonClip:Clip;
protected var _clips:Array;
protected var _commonClip:Clip;
public function ClipEventSupport(commonClip:Clip, clips:Array = null) {
_commonClip = commonClip;
......
......@@ -17,6 +17,8 @@
*/
package org.flowplayer.model {
import org.flowplayer.flow_internal;
use namespace flow_internal;
......@@ -27,28 +29,28 @@ package org.flowplayer.model {
private var _currentPos:Number;
private var _inStreamClip:Clip;
private var _commonClip:Clip;
private var _clips:Array;
public function Playlist(commonClip:Clip = null) {
if (commonClip == null) {
commonClip = new NullClip();
}
super(commonClip);
_commonClip = commonClip;
//#163 inherit clips and common clip
super(commonClip, new Array());
_commonClip.setParentPlaylist(this);
initialize();
}
private function initialize(newClips:Array = null):void {
_clips = new Array();
private function initialize(newClips:Array = null):void {
//#163 reuse clips and initialize
if (_clips) _clips.length = 0;
_inStreamClip = null;
if (newClips) {
for (var i:Number = 0; i < newClips.length; i++) {
doAddClip(newClips[i]);
}
}
super.setClips(_clips);
_currentPos = 0;
log.debug("initialized, current clip is " + current);
}
......@@ -73,21 +75,31 @@ package org.flowplayer.model {
for (var i:Number = 0; i < clips.length; i++) {
doAddClip(clips[i], -1, false);
}
super.setClips(_clips);
}
private function doReplace(newClips:Array, silent:Boolean = false):void {
var oldClips:Array = _clips.concat([]);
initialize(newClips);
//#163 detach netstream on the current clip
if (_clips[_currentPos].getContent().hasOwnProperty("attachNetStream")) {
_clips[_currentPos].getContent().attachNetStream(null);
}
initialize(newClips);
if (! silent) {
dispatchPlaylistReplace(oldClips);
}
oldClips.length = 0;
oldClips = null;
}
flow_internal function dispatchPlaylistReplace(oldClips:Array = null):void {
log.debug("dispatchPlaylistReplace");
var oldClipsEventHelper:ClipEventSupport = new ClipEventSupport(_commonClip, oldClips || []);
doDispatchEvent(new ClipEvent(ClipEventType.PLAYLIST_REPLACE, oldClipsEventHelper), true); }
doDispatchEvent(new ClipEvent(ClipEventType.PLAYLIST_REPLACE, oldClipsEventHelper), true);
}
/**
......@@ -98,7 +110,6 @@ package org.flowplayer.model {
* @see ClipEventType#CLIP_ADD
*/
public function addClip(clip:Clip, pos:int = -1, silent:Boolean = false):void {
var index:Number = positionOf(pos);
if (clip.position >= 0 || clip.position == -1 || clip.position == -2) {
addChildClip(clip, pos);
return;
......@@ -114,7 +125,6 @@ package org.flowplayer.model {
log.debug("addClip(), moving to next clip");
next();
}
super.setClips(_clips);
}
if (! silent) {
doDispatchEvent(new ClipEvent(ClipEventType.CLIP_ADD, pos >= 0 ? pos : clips.length - 1), true);
......@@ -151,28 +161,29 @@ package org.flowplayer.model {
}
private function doAddClip(clip:Clip, pos:int = -1, dispatchEvents:Boolean = true):void {
log.debug("doAddClip() " + clip);
//log.debug("doAddClip() " + clip);
clip.setParentPlaylist(this);
var currentInPos:Clip;
if (pos == -1) {
_clips.push(clip);
} else {
currentInPos = clips[pos];
_clips.splice(_clips.indexOf(currentInPos.preroll || currentInPos), 0, clip);
}
var nested:Array = clip.playlist;
for (var i:int = 0; i < nested.length; i++) {
var nestedClip:Clip = nested[i] as Clip;
addChildClip(nestedClip, pos, dispatchEvents);
}
log.debug("clips now " + _clips);
if (clip != _commonClip) {
//#163 reposition order of clip add
if (clip != _commonClip) {
clip.onAll(_commonClip.onClipEvent);
log.debug("adding listener to all before events, common clip listens to other clips");
clip.onBeforeAll(_commonClip.onBeforeClipEvent);
}
for (var i:int = 0; i < clip.playlist.length; i++) {
addChildClip(clip.playlist[i], pos, dispatchEvents);
}
//log.debug("clips now " + _clips);
if (pos == -1) {
_clips.push(clip);
} else {
_clips.splice(_clips.indexOf(clips[pos].preroll || clips[pos]), 0, clip);
}
}
/**
......
......@@ -17,11 +17,13 @@
*/
package org.flowplayer.util {
import flash.utils.describeType;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import org.flowplayer.util.Log;
import flash.system.System;
/**
* PropertyBinder is used to populate object's properties by copying values
* from other objects. The target object should be an instance of a class that contains
......@@ -42,7 +44,7 @@ package org.flowplayer.util {
* @param extraProps a property name for all properties for which the target does not provide an accessor or a setter function
*/
public function PropertyBinder(object:Object, extraProps:String = null) {
log.info("created for " + getQualifiedClassName(object));
//log.info("created for " + getQualifiedClassName(object));
_object = object;
_extraProps = extraProps;
_objectDesc = describeType(_object);
......@@ -57,7 +59,10 @@ package org.flowplayer.util {
copyProperty(prop, source[prop]);
}
}
log.debug("done with " + getQualifiedClassName(_object));
//#163 cleanup xml object
System.disposeXML(_objectDesc);
//log.debug("done with " + getQualifiedClassName(_object));
return _object;
}
......
......@@ -263,7 +263,7 @@ package org.flowplayer.view {
} else {
log.info("previous fadeout was canceled, will not remove " + view + " from panel");
}
});
}, false, 0, true);
} else if (view.parent != _panel) {
_panel.addView(view, null, plugin);
}
......@@ -337,12 +337,12 @@ package org.flowplayer.view {
playable.addEventListener(GoEvent.COMPLETE,
function(event:GoEvent):void {
onComplete(view, playable, completeCallback);
});
}, false, 0, true);
if (updateCallback != null) {
playable.addEventListener(GoEvent.UPDATE,
function(event:GoEvent):void {
updateCallback(view);
});
}, false, 0, true);
}
playable.start();
......
......@@ -69,11 +69,7 @@ package org.flowplayer.view {
public function resizeClipTo(clip:Clip, mediaSize:MediaSize, force:Boolean = false):void {
log.debug("resizeClipTo, clip " + clip);
if ( _resizerTimer ) {
log.debug("Killing old resize timer");
_resizerTimer.reset();
_resizerTimer = null;
}
var resizer:MediaResizer = resizers[clip];
if (! resizer) {
......@@ -92,16 +88,26 @@ package org.flowplayer.view {
screen.resized(clip);
}
};
if ( resizer.hasOrigSize() ) {
log.debug("we have a size, resizing now !");
resizingFunc();
} else {
if ( _resizerTimer ) {
log.debug("Killing old resize timer");
_resizerTimer.reset();
_resizerTimer.removeEventListener(TimerEvent.TIMER, resizingFunc);
_resizerTimer = null;
}
// delayed one
log.warn("we don't have a size now, delaying the resize");
_resizerTimer = new Timer(500, 5);
_resizerTimer.addEventListener(TimerEvent.TIMER, resizingFunc);
_resizerTimer.start();
//_resizerTimer.start();
}
}
......
......@@ -16,57 +16,58 @@
* along with Flowplayer. If not, see <http://www.gnu.org/licenses/>.
*/
package org.flowplayer.view {
import flash.display.BlendMode;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import flash.system.Capabilities;
import flash.system.Security;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.*;
import org.flowplayer.config.Config;
import org.flowplayer.config.ConfigParser;
import org.flowplayer.config.ExternalInterfaceHelper;
import org.flowplayer.config.VersionInfo;
import org.flowplayer.controller.PlayListController;
import org.flowplayer.controller.ResourceLoader;
import org.flowplayer.controller.ResourceLoaderImpl;
import org.flowplayer.flow_internal;
import org.flowplayer.model.Callable;
import org.flowplayer.model.Clip;
import org.flowplayer.model.ClipEvent;
import org.flowplayer.model.ClipEventType;