FMS, Live Streaming and Sync Between Publisher and Subscribers

Here I would like to discuss the FMS, Live Streaming and Sync issues between publisher and subscribers. Also I would discuss about possible solutions 🙂

The problem: Let’s say we are publishing a live stream using FMLE (Flash Media Live Encoder) or our custom publisher to FMS (Flash Media Server). There are 5 subscribers who are watching this live stream (given that those subscribers connected to stream after the stream has been already published). Now NetStream.time will not give absolute stream time for any of the subscriber connected to live stream because it gives time since the playback of live stream is started by subscriber. Now let’s say I (as a publisher) want to take certain action (such as displaying an image, or some message, or slide) on subscribers on specific point of time (in terms of viewing the live stream). Let’s say from publisher side, when I see (or speak) something (a tortoise in scene!) on screen, I want to show another picture of that same thing to all subscribers (dancing tortoises!) exactly at the same time when each one of them see tortoise on their live stream.

SharedObject can not be used because that will not provide any kind of sync (in terms of live streaming) because they are asynchronous and live streams may get delayed due to slow bandwidths on subscribers.

Even we can not directly call a function on FMS which would in turn call functions to all subscribers because that process would be instantaneous (compared to delay in live stream) and if there are number of subscribers this process may hang FMS (and publisher) application for a fraction of second.

The Solutions:

1) Somehow give all subscribers the absolute stream time. This would be an ideal solution but not easy. From FMLE, I managed to find a solutions and that is timecode. Let’s say if the publishing video frame rate is 15 fps then FMLE would encode a keyframe at each group of 15 frames (given that timecode interval is same as frame rate of video). Now the timecode is embedded as system time and date which we need to take care of. The condition here is the video must be recorded on server so that we can know the start time of publishing. On subscribers we get onFI() event at each 1 second which would give you current date and time. From this current time subtract the initial time (which is found by playing recorded stream) and that would give you absolute live stream time 🙂

2) Another way is kind of events instead of focusing on time but yet it keeps solid track of sync. From publisher side, call NetStream.send() function along with necessary parameters. This will actually embed metadata on current frame being encoded. When subscribers get this frame, they get the embedded event and they can take the necessary action by analyzing the data given in that event. Usually I would prefer to use onMetaData event for this stuff 🙂

Phue! It’s been late night now (1:43 AM already) and I am going for sleep now. If any one have any questions, post me a comment and I will be back there right away 🙂

Thanks, Naresh

FMS, Remote SharedObject and Arrays

I have bad experience with above 3 at the same time. When ever you use an array in FMS side coding and pass along that in Remote SharedObject you would be in trouble in in terms of bandwidth and memory usage of application. When I change anything an array i.e. add an item, update an item or delete an item, you would need to pass the whole array to shared object and that will send the full array to all connected clients to that shared object. So even if you have change single item in the full array it eats the bandwidth for full array.

So, what I learnt for it? NEVER use an array in FMS side coding when you have to pass it to Shared Object. Always use property names of shared object to identify the objects inside the shared object.

Thanks,
Naresh

Live Streaming, FMS and Absolute Stream Time

Think of a live stream is being published to FMS and all subscribers are watching that live stream. Now one of the subscriber is late in joining this live stream but still he does that successfully and watches the live stream from the point when he joined it. Everything looks perfect!

But wait, one thing is not perfect definitely – the time of the stream been published. Yes, the subscribers does not see the absolute stream time from start of its publish. So what does that mean? They are not in sync of each other. Let’s say I want to notify all subscribers at certain point of time in video. Now I would be watching something at time t and I will send message to all subscribers. What I want is : all subscribers should see this message at exact point of time when they see what I am watching in video. But as there is no time sync, subscribers won’t know that at what point of time the message should be displayed.

As work around, what I did is I passed timecode from FMLE. That means that at each frame of video, FMLE would encode system time in the frame. All subscribers would get onFI() event with that time code at each second (given that the timecode rate and frame rate of video is same at FMLE). A subscriber would have to save the first timecode as the start point of time. On next onFI() event they can just subtract the start timecode from the current timecode and that would give it a absolute time of stream!!

Once absolute time of stream is found, you can synchronize the message around 🙂 I did not written down any code here but if any one wants the code, let me know – I can provide it.

Thanks,
Naresh

Resize a movieclip snapshot in ActionScript 3.0

Let’s say you want to create a snapshot of a movieclip inside your Flash/Flex application. The constraint here is you need to resize the snapshot to the values (width and height) entered by user. Here is how you could do this :

var bitmapData:BitmapData = new BitmapData(clip.width, clip.height);                //clip is a movieclip whose snapshot is to be taken
bitmapData.draw(clip);

var bitmap:Bitmap = new Bitmap(bitmapData.clone());
bitmap.width = int(width);                 //rounding width and height to integer values
bitmap.height = int(height);              //This is important to resize the child (bitmap) here as you can not resize the sprite (sp) itself as sprite is going to be drawn inside bitmap data.
var sp:Sprite = new Sprite();
sp.addChild(bitmap);

var bmd:BitmapData = new BitmapData(sp.width, sp.height);                //As the child of sprite is already resized it will think that actual width and height of sprite are unscaled and hence it can map pixel to pixel on full width and height.
bmd.draw(sp);          //now draw sprite over bitmap data.

var encoder:JPGEncoder = new JPGEncoder(100);
var byteArray:ByteArray = encoder.encode(bmd);           //encode bitmap data using adobe’s corelib’s JPGEncoder to get byte array.

That’s it. Again keep in mind that the clip which you are going to draw over bitmap data should not be resized because that will not affect the drawing of bitmap data and you could see some unusual results.

There may be other ways to achieve this 🙂 but I found (may be others found before me) this method convenient for me.

Thanks,

Naresh Khokhaneshiya

Searching Array using Array.indexOf() function

Searching in Array using Array.indexOf() function is very easy :). For example,

var toys:Array = new Array();
toys.push("ball");
toys.push("bat");
toys.push("glows");
toys.push("pads");
toys.push(23);
toys.push(14);

var searchIndex:int = toys.indexOf(“ball”);
if(searchIndex == -1)
{
trace(“No! how can it be there”);
}
else
{
trace(“Yup! it’s there”); //output will be from here
}

So, it works fine when the value inside the indexed array is primitive – i.e. Number, String, Boolean etc. BUT it will not work when array contains objects of type Object. For example,

var toys:Array = new Array();
toys.push({name:"ball", count:2});
toys.push({name:"bat", count:2});
toys.push({name:"glows", count:2});
toys.push({name:"pads", count:2});
toys.push({name:"runs", count:200});
toys.push({name:"wickets", count:2});

var searchIndex:int = toys.indexOf({name:”ball”, count:2});
if(searchIndex == -1)
{
trace(“No! how can it be there”); //output will always be from here if you search an object
}
else
{
trace(“Yup! it’s there”);
}

So, that’s the case for objects. In the syntax of Array.indexOf(itemToSearch:*, fromIndex:int = 0), we notice that the first argument is * data type – i.e. it can be any data type, BUT it’s not working for Object. Would any one have lights on this?

Set TextField horizontal scroll to initial position

If you want to set the position of horizontal scroll position inside TextField, you can use TextField.scrollH.

Let’s say I am typing in the input TextField and I typed beyond the width of TextField. You will see the last characters visible you typed. Now due to some other event, say user clicks on other than this TextField, focus is changed to some other object than this TextField, you may want to reset the scroll position of TextField to starting characters in it.
You will use :
TextField.scrollH = 0;

This will set the horizontal scroll of TextField to 0 and hence showing the starting characters in TextField.

Loading Image using Loader.loadBytes()

Loader class can load image from ByteArray. I used this method before a year ago to set  a part of a screen for Screen Sharing application. Right now I was having a case where I need to create an image from Base64 encoded string. I am using following code to do this ::

var str:String = "";    //It won't be an empty string :) because it will a base64 encoded string of image.

var byteArr:ByteArray = Base64.converToByteArray(str);    //Here you can use any ready made library to decode and convert the String into ByteArray. I used ready made library from here.

var loader:Loader = new Loader();

loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);

loader.loadBytes(byteArr);

function onImageLoaded(e:Event):void{

addChild(loader);

trace(loader.width + " : " + loader.height);

}

That’s it. This can be useful where you can not load the image from server because you don’t have the image but it’s data as string. May this can be useful to some one 🙂

Built-In Context Menu Items on Flash TextField

Is there any way to hide default context menu items on Flash TextField? Some one will try to answer with ContextMenu.hideBuiltInItems() function, but this function does not really work with Flash TextField. It does not hide the built in items.

So following code does work but not as expected ::

var myContextMenu:ContextMenu = new ContextMenu();
myContextMenu.hideBuiltInItems();

var txt:TextField = new TextField();
txt.contextMenu = myContextMenu;
addChild(txt);

Now if you right click on TextField then you will see that you can see all the built in item. Any one knows why?

Flash: TextField.htmlText and Automatic Line Breaks

Problem :: When you deal with TextField.htmlText don’t append any html strings directly to htmlText property of TextField. Because the TextField appends “<br>” tag automatically before it appends any html string to htmlText property, it will set your string automatically in next line instead of the current line. This mistake is done to save memory as we don’t have to create a temporary string.

Example :

textField.htmlText = “”;                 //clear any previous texts

for(var i:uint = 0; i < 3; i++)
{
var str:String = “”;
str += “Hello World”;
str += “<br>”;                    //As you’re thinking of breaking line from here
textField.htmlText += str;
}

//output would be something like following ::
Hello World
//This line break because of <br> tag
Hello World         //This is an automatic line break
//This line break because of <br> tag
Hello World         //This is an automatic line break
//output finished

Possible Solution :: Create an empty string and append all the html strings to this string. When the final string is ready, assign this string to htmlText property of TextField. This will not create any unnecessary line breaks inside TextField.

Example :

textField.htmlText = “”;                 //clear any previous texts
var htmlStr:String = “”;                  //Temporary empty string

for(var i:uint = 0; i < 3; i++)
{
var str:String = “”;
str += “Hello World”;
str += “<br>”;                    //As you’re thinking of breaking line from here
htmlStr += str;
}

textField.htmlText = htmlStr;

//output would be something like following ::
Hello World
Hello World
Hello World
//output finished

Hope this would prevent someone from mistakes which I did 🙁

FlashDevelop and SVN Integration

Though FlashDevelop guys are working on integrating FlashDevelop with SVN, we can go with this handy tool.

To integrate SVN with FlashDevelop, you should have installed one of the SVN clients (say tortoiseSVN) on your machine.

FlashDevelop can be integrated with SVN using following steps ::

Step 1:

Create a file named build.bat in the directory containing your FlashDevelop project file and paste following code in that file :

@ECHO OFF

set TORTOISESVN_PATH = C:\Program Files\TortoiseSVN\bin
set CURRENT_PATH=%CD%

“%TORTOISESVN_PATH%\TortoiseProc.exe” /command:commit /path:”%CURRENT_PATH%\*”/logmsgfile:”%CURRENT_PATH%\logmsg.txt” /notempfile /closeonend:3

“%TORTOISESVN_PATH%\SubWCRev.exe” “%CURRENT_PATH%” “%CURRENT_PATH%\version.a” “%CURRENT_PATH%\version.txt”

pause

Step 2:

Also create another file named “version.a” with following content ::

major=0;
minor=1;
build=$WCREV$ ;
date=”$WCDATE$” ;

Step 3:

Now go to Flash Develop Project Explorer  Right clik on project  Properties  Build
In the Post-Build Command Line, add following line ::

$(ProjectDir)\build.bat

Step 4:

Press F8, when you want to commit files inside the FlashDevelop project.

You can fine more information regaring this on : FlashDevelop and SVN Integration