I'm trying to play multiple video streams simultaneously. However, I cannot synchronize these videos to play at the same rate.
---- details --------
I have three 45-second videos in FLV format and I use flash.net.NetStream to play these videos. I call netstream.play() of these netstream at the same time (by using a for-loop). However, these videos are out-of-sync even all videos files are on my local machine.
For example, when the wall clock is at 10th second, the first video is at 7th second, the second video is at 10th second, and the last video is at 5th second.
I think it may be affected by different jitter delays when streaming. However, I still cannot find the way to solve this problem.
here are my research results.
code:
package
{
public class Main extends Sprite
{
private var zeroBG:Sprite;
private var oneBG:Sprite;
private var twoBG:Sprite;
private var arr:Array = new Array();
private var oldSchoolMC:MovieClip;
public function Main():void
{
oldSchoolMC = new MovieClip();
addChild(oldSchoolMC);
oldSchoolMC.x = 400;
oldSchoolMC.y = 350;
oldSchoolMC.buttonMode = true;
addFrames();
//the string below is just a way to get about +15% CPU load (on Intel Dual-Core T4400), comment it out if you don't need it
oldSchoolMC.addEventListener(Event.ENTER_FRAME, onEnterFrame);
oldSchoolMC.addEventListener(MouseEvent.CLICK, onClick);
zeroBG = new Sprite();
oneBG = new Sprite();
twoBG = new Sprite();
oneBG.x = 350;
twoBG.x = 700;
addChild(zeroBG);
addChild(oneBG);
addChild(twoBG);
genVideoSampleOnDefaultClasses(zeroBG);
genVideoSampleOnDefaultClasses(oneBG);
genVideoSampleOnDefaultClasses(twoBG);
}
private function onClick(e:MouseEvent):void {
var secs:int = 0;
if ((arr[0] as NetStream).time != 0 && (arr[0] as NetStream).time != (arr[arr.length - 1] as NetStream).time) {
secs = Math.ceil((arr[0] as NetStream).time);
}
for (var i:int = 0; i < arr.length; i++) {
var ns:NetStream = arr[i] as NetStream;
if(ns.time == 0){
ns.play('res/ghost_in_the_shell.flv');
continue;
}else {
trace('i = ' + i + ' time = ' + ns.time);
if (secs != 0) {
ns.seek(secs);
}
}
}
}
private function addFrames():void {
for (var i:int = 0 ; i < 0xffffff ; i+=100000) {
oldSchoolMC.addChild(genColRect(i));
if (oldSchoolMC.numChildren > 0) {
oldSchoolMC.getChildAt(oldSchoolMC.numChildren - 1).scaleX = (250 - oldSchoolMC.numChildren) / 250;
oldSchoolMC.getChildAt(oldSchoolMC.numChildren - 1).scaleY = (250 - oldSchoolMC.numChildren) / 250;
}
}
}
private function onEnterFrame(e:Event):void {
for (var i:int = 0 ; i < oldSchoolMC.numChildren ; i++) {
oldSchoolMC.getChildAt(i).rotation += (oldSchoolMC.numChildren - i);
}
}
private function genColRect(col:int = 0xffffff):Shape {
var spr:Shape = new Shape();
spr.graphics.beginFill(col);
spr.graphics.drawRect( -50, -50, 100, 100);
spr.graphics.endFill();
return spr;
}
private function genVideoSampleOnDefaultClasses(spr:Sprite):void {
var vid:Video = new Video();
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.client = new Object();
ns.client.onMetaData = function(info:Object):void { };
vid.attachNetStream(ns);
spr.addChild(vid);
arr.push(ns);
}
}
}
i can mention two synchronization problems:
- on start: no matter if i use a
for
loop or just hardcode 3 lines ((arr[0] as NetStream).play('res/ghost_in_the_shell.flv');
- this way) the output on second click (after play is inited) is like this (traces from second and third clicks):
click N 2
i = 0 time = 5.251
i = 1 time = 5.251
i = 2 time = 5.538
click N 3
i = 0 time = 37.721
i = 1 time = 37.721
i = 2 time = 37.721
first and second streams are ok, but the third is always 287ms late (it depends on the onClick function code, previous version always gave 183ms delay)
- after 600 - 800 seconds: there is an uncertain delta in streams' times (about 100ms usually), traces from 2 next clicks:
click N 4
i = 0 time = 756.44
i = 1 time = 756.558
i = 2 time = 756.558
click N 5
i = 0 time = 4466.965
i = 1 time = 4466.965
i = 2 time = 4466.965
and screenshots (first part was shot after the first click (before any synchronization), second after click N 4):
flv size is about 207mb btw
UPD: i added 5 more sprites for videos, a textfield for stats and a timer (with 1000ms interval) to call the onClick
function which was modified as follows:
private function onClick(e:Event):void {
tf.text = '';
var secs:int = 0;
if ((arr[0] as NetStream).time != 0 && (arr[0] as NetStream).time != (arr[arr.length - 1] as NetStream).time) {
secs = Math.ceil((arr[0] as NetStream).time);
trace(counter++ + ' : time = ' + secs);
}
for (var i:int = 0; i < arr.length; i++) {
var ns:NetStream = arr[i] as NetStream;
if(ns.time == 0){
ns.play('res/ghost_in_the_shell.flv');
if (i == arr.length - 1) {
streamTimer.start();
}
continue;
}else {
tf.appendText('# ' + i + ' [' + ns.time + ']\n');
if (secs != 0) {
ns.seek(secs);
}
}
}
}
it had about 20 sync problems per 100 seconds (traces, not issues i could see) because it used a huge amount of system resources, but video objects played smooth enough even if there was a trace about seeking.
here's the picture:
Oups.... didn't see it was 2 years old......
I think that you should try to preload your files in each player instance, wait for the load to be completed and then, you can start the videos. Relying on NetStream event is not so great (several times the same notification, or missing notification depending on the file played....) but it should work.
function Preload() : void {
aNet = new NetConnection();
aNet.connect(null);
stream = new NetStream( aNet );
stream.client = this;
stream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus, false, 0, true );
stream.addEventListener(IOErrorEvent.IO_ERROR, errSnd, false, 0, true );
stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError, false, 0, true );
stream.play("your file");
}
// Here you wait for the load notification, and then pause the video.
private function onNetStatus( e : NetStatusEvent ) : void {
switch( e.info.code ) {
case "NetStream.Buffer.Full" :
if (bNotified) return;
stream.pause();
// Store that the file is loaded
bNotified = true;
// Dispatch an event
dispatchEvent( new Event("VIDEO LOADED") );
break;
}
}
private function errSnd(e: IOErrorEvent ) : void {
// error handling
}
private function onAsyncError(e: AsyncErrorEvent ) : void {
// Error handling
}