329 lines
No EOL
9.6 KiB
ActionScript
Executable file
329 lines
No EOL
9.6 KiB
ActionScript
Executable file
/**
|
|
* FLV model class of the players MCV pattern.
|
|
* Handles playback of FLV files, HTTP streams and RTMP streams.
|
|
*
|
|
* @author Jeroen Wijering
|
|
* @version 1.13
|
|
**/
|
|
|
|
|
|
import com.jeroenwijering.players.*;
|
|
|
|
|
|
class com.jeroenwijering.players.FLVModel extends AbstractModel {
|
|
|
|
|
|
/** array with extensions used by this model **/
|
|
private var mediatypes:Array = new Array(
|
|
"flv","rtmp","mp4","m4v","m4a","mov","3gp","3g2");
|
|
/** NetConnection object reference **/
|
|
private var connectObject:NetConnection;
|
|
/** NetStream object reference **/
|
|
private var streamObject:NetStream;
|
|
/** Sound object reference **/
|
|
private var soundObject:Sound;
|
|
/** interval ID of the buffer update function **/
|
|
private var loadedInterval:Number;
|
|
/** current percentage of the video that's loaded **/
|
|
private var currentLoaded:Number = 0;
|
|
/** interval ID of the position update function **/
|
|
private var positionInterval:Number;
|
|
/** current state of the video that is playing **/
|
|
private var currentState:Number;
|
|
/** Current volume **/
|
|
private var currentVolume:Number;
|
|
/** MovieClip with "display" video Object **/
|
|
private var videoClip:MovieClip;
|
|
/** object with keyframe times and positions, saved for PHP streaming **/
|
|
private var metaKeyframes:Object = new Object();
|
|
/** Boolean to check whether a stop event is fired **/
|
|
private var stopFired:Boolean = false;
|
|
/** Boolean to check whether a flusk event is fired **/
|
|
private var flushFired:Boolean = false;
|
|
/** Switch for FLV type currently played **/
|
|
private var flvType:String;
|
|
/** check h264 for time offset **/
|
|
private var isH264:Boolean;
|
|
/** check h264 for time offset **/
|
|
private var timeOffset:Number = 0;
|
|
/** reference to the captions object for parsing captionate data **/
|
|
public var capView:Object;
|
|
/** buffer iterator (prevents buffericon showing on slow PC's) **/
|
|
public var bufferCount:Number = 0;
|
|
|
|
|
|
|
|
/** Constructor **/
|
|
function FLVModel(vws:Array,ctr:AbstractController,cfg:Object,
|
|
fed:Object,fcl:MovieClip) {
|
|
super(vws,ctr,cfg,fed);
|
|
connectObject = new NetConnection();
|
|
videoClip = fcl;
|
|
if(config["smoothing"] == "false") {
|
|
videoClip.display.smoothing = false;
|
|
} else {
|
|
videoClip.display.smoothing = true;
|
|
}
|
|
videoClip.display.deblocking = config["deblocking"];
|
|
videoClip.createEmptyMovieClip("snd",videoClip.getNextHighestDepth());
|
|
soundObject = new Sound(videoClip.snd);
|
|
};
|
|
|
|
|
|
/** Check which FLV type we use **/
|
|
private function setItem(idx:Number) {
|
|
super.setItem(idx);
|
|
if(isActive == true) {
|
|
if(config["streamscript"] != undefined) {
|
|
flvType = "HTTP";
|
|
} else if(feeder.feed[currentItem]["type"] == "rtmp") {
|
|
flvType = "RTMP";
|
|
} else {
|
|
flvType = "FLV";
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/** Start a specific video **/
|
|
private function setStart(pos:Number) {
|
|
stopFired = false;
|
|
flushFired = false;
|
|
if (pos != undefined) { currentPosition = pos; }
|
|
if(pos < 1) {
|
|
pos = 0;
|
|
} else if (pos > feeder.feed[currentItem]["duration"] - 1) {
|
|
pos = feeder.feed[currentItem]["duration"] - 1;
|
|
}
|
|
if (flvType=="RTMP" && feeder.feed[currentItem]["id"] != currentURL) {
|
|
connectObject.connect(feeder.feed[currentItem]["file"]);
|
|
currentURL = feeder.feed[currentItem]["id"];
|
|
setStreamObject(connectObject);
|
|
streamObject.play(currentURL);
|
|
} else if(flvType != "RTMP" &&
|
|
feeder.feed[currentItem]["file"] != currentURL) {
|
|
connectObject.connect(null);
|
|
currentURL = feeder.feed[currentItem]["file"];
|
|
if(flvType == "HTTP" ) {
|
|
setStreamObject(connectObject);
|
|
if(config["streamscript"] == "lighttpd") {
|
|
streamObject.play(currentURL);
|
|
} else {
|
|
streamObject.play(config["streamscript"] +
|
|
"?file=" + currentURL);
|
|
}
|
|
} else {
|
|
setStreamObject(connectObject);
|
|
streamObject.play(currentURL);
|
|
}
|
|
} else {
|
|
if(flvType == "HTTP" && pos != undefined) {
|
|
playKeyframe(currentPosition);
|
|
} else if (flvType != "HTTP" && pos != undefined) {
|
|
streamObject.seek(currentPosition);
|
|
} else if(flvType == "RTMP" && currentPosition > 0 &&
|
|
feeder.feed[currentItem]["duration"] == 0) {
|
|
connectObject.connect(feeder.feed[currentItem]["file"]);
|
|
setStreamObject(connectObject);
|
|
streamObject.play(currentURL);
|
|
} else {
|
|
streamObject.pause(false);
|
|
}
|
|
}
|
|
videoClip._visible = true;
|
|
videoClip._parent.thumb._visible = false;
|
|
clearInterval(positionInterval);
|
|
positionInterval = setInterval(this,"updatePosition",100);
|
|
clearInterval(loadedInterval);
|
|
loadedInterval = setInterval(this,"updateLoaded",100);
|
|
};
|
|
|
|
|
|
/** Read and broadcast the amount of the flv that's currently loaded **/
|
|
private function updateLoaded() {
|
|
var pct:Number = Math.round(streamObject.bufferLength/
|
|
streamObject.bufferTime*100);
|
|
if(flvType == "FLV") {
|
|
pct = Math.round(streamObject.bytesLoaded/
|
|
streamObject.bytesTotal*100);
|
|
}
|
|
if(isNaN(pct)) {
|
|
currentLoaded = 0;
|
|
sendUpdate("load",0);
|
|
} else if (pct > 95) {
|
|
clearInterval(loadedInterval);
|
|
currentLoaded = 100;
|
|
sendUpdate("load",100);
|
|
} else if (pct != currentLoaded) {
|
|
currentLoaded= pct;
|
|
sendUpdate("load",currentLoaded);
|
|
}
|
|
};
|
|
|
|
|
|
/** Read and broadcast the current position of the song **/
|
|
private function updatePosition() {
|
|
var pos = streamObject.time + timeOffset;
|
|
if(pos == currentPosition && currentState != 1 && stopFired != true) {
|
|
if(bufferCount == 5) {
|
|
currentState = 1;
|
|
sendUpdate("state",1);
|
|
bufferCount = 0;
|
|
} else {
|
|
bufferCount++;
|
|
}
|
|
} else if (pos != currentPosition && currentState != 2) {
|
|
bufferCount = 0;
|
|
currentState = 2;
|
|
sendUpdate("state",2);
|
|
} else {
|
|
bufferCount = 0;
|
|
}
|
|
if (pos != currentPosition) {
|
|
currentPosition = pos;
|
|
sendUpdate("time",currentPosition,
|
|
Math.max(feeder.feed[currentItem]["duration"]-currentPosition,0));
|
|
} else if (stopFired == true ||
|
|
(flushFired == true && flvType != "RTMP" && bufferCount == 5)) {
|
|
currentState = 3;
|
|
videoClip._visible = false;
|
|
videoClip._parent.thumb._visible = true;
|
|
sendUpdate("state",3);
|
|
sendCompleteEvent();
|
|
stopFired = false;
|
|
flushFired = false;
|
|
}
|
|
};
|
|
|
|
|
|
/** Pause the video that's currently playing. **/
|
|
private function setPause(pos:Number) {
|
|
if(pos < 1) { pos = 0; }
|
|
clearInterval(positionInterval);
|
|
if(pos != undefined) {
|
|
currentPosition = pos;
|
|
sendUpdate("time",currentPosition,
|
|
Math.abs(feeder.feed[currentItem]["duration"]-currentPosition));
|
|
streamObject.seek(currentPosition);
|
|
}
|
|
streamObject.pause(true);
|
|
currentState = 0;
|
|
sendUpdate("state",0);
|
|
};
|
|
|
|
|
|
/** Stop video and clear data. **/
|
|
private function setStop(pos:Number) {
|
|
clearInterval(loadedInterval);
|
|
clearInterval(positionInterval);
|
|
videoClip._visible = false;
|
|
delete currentURL;
|
|
delete currentLoaded;
|
|
delete currentPosition;
|
|
delete metaKeyframes;
|
|
currentLoaded = 0;
|
|
stopFired = false;
|
|
timeOffset = 0;
|
|
streamObject.close();
|
|
delete streamObject;
|
|
};
|
|
|
|
|
|
/** Set volume of the sound object. **/
|
|
private function setVolume(vol:Number) {
|
|
super.setVolume(vol);
|
|
currentVolume = vol;
|
|
soundObject.setVolume(vol);
|
|
};
|
|
|
|
|
|
/** Connect a new stream object to video/audio/callbacks **/
|
|
private function setStreamObject(cnt:NetConnection) {
|
|
_root.tf.text = 'metadata!';
|
|
var ref = this;
|
|
currentLoaded = 0;
|
|
sendUpdate("load",0);
|
|
streamObject = new NetStream(cnt);
|
|
streamObject.setBufferTime(config["bufferlength"]);
|
|
streamObject.onMetaData = function(obj) {
|
|
if(obj.duration > 1) {
|
|
ref.feeder.feed[ref.currentItem]["duration"] = obj.duration;
|
|
}
|
|
if(obj.width > 10) {
|
|
ref.sendUpdate("size",obj.width,obj.height);
|
|
}
|
|
if(obj.seekpoints != undefined) {
|
|
ref.isH264 = true;
|
|
ref.metaKeyframes = new Object();
|
|
ref.metaKeyframes.times = new Array();
|
|
ref.metaKeyframes.filepositions = new Array();
|
|
for (var j in obj.seekpoints) {
|
|
ref.metaKeyframes.times.unshift(
|
|
Number(obj.seekpoints[j]['time']));
|
|
ref.metaKeyframes.filepositions.unshift(
|
|
Number(obj.seekpoints[j]['time']));
|
|
}
|
|
} else {
|
|
ref.metaKeyframes = obj.keyframes;
|
|
}
|
|
if(ref.feeder.feed[ref.currentItem]['start'] > 0) {
|
|
if(ref.flvType == "HTTP") {
|
|
ref.playKeyframe(ref.feeder.feed[ref.currentItem]['start']);
|
|
} else if (ref.flvType == "RTMP") {
|
|
ref.setStart(ref.feeder.feed[ref.currentItem]['start']);
|
|
}
|
|
}
|
|
delete obj;
|
|
delete this.onMetaData;
|
|
};
|
|
streamObject.onStatus = function(object) {
|
|
trace("status: "+object.code);
|
|
if(object.code == "NetStream.Play.Stop" && ref.flvType!='RTMP') {
|
|
ref.stopFired = true;
|
|
} else if (object.code == "NetStream.Play.StreamNotFound") {
|
|
ref.currentState = 3;
|
|
ref.videoClip._visible = false;
|
|
ref.sendUpdate("state",3);
|
|
ref.sendCompleteEvent();
|
|
ref.stopFired = false;
|
|
ref.flushFired = false;
|
|
} else if (object.code == "NetStream.Buffer.Flush") {
|
|
ref.flushFired = true;
|
|
}
|
|
};
|
|
streamObject.onPlayStatus = function(object) {
|
|
if( object.code == "NetStream.Play.Complete" ||
|
|
object.code == "NetStream.Play.Stop") {
|
|
ref.stopFired = true;
|
|
}
|
|
};
|
|
streamObject.onCaption = function(cap:Array) {
|
|
ref.capView.onCaptionate(cap);
|
|
};
|
|
videoClip.display.attachVideo(streamObject);
|
|
videoClip.snd.attachAudio(streamObject);
|
|
};
|
|
|
|
|
|
/** Play from keyframe position from metadata **/
|
|
private function playKeyframe(pos:Number) {
|
|
for (var i=0; i< metaKeyframes.times.length; i++) {
|
|
if((metaKeyframes.times[i] <= pos) &&
|
|
(metaKeyframes.times[i+1] >= pos)) {
|
|
if(config["streamscript"] == "lighttpd") {
|
|
streamObject.play(currentURL+"?start="+
|
|
metaKeyframes.filepositions[i]);
|
|
if(isH264 == true) {
|
|
timeOffset = metaKeyframes.filepositions[i];
|
|
}
|
|
} else {
|
|
streamObject.play(config["streamscript"]+"?file="+
|
|
currentURL+"&pos="+metaKeyframes.filepositions[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
} |