Array Shuffle/Randomization in ActionScript 3
There are two main methods that are used in programming. Both methods originate in Fisher–Yates mathematical shuffle algorithm. In a nutshell the idea is to switch members of series in a such way that all members’ positions are affected at least once.
Read full post on my new host.
Dragging Object Around Circle in Flash with ActionScript 3
Dragging Object Around Circle in Flash with ActionScript 3
Challenge
Create a UI that allows dragging of DisplayObject around a circle.
Solution
The following two critical parts are used for implementation:
- We will employ
MouseEvent.MOUSE_MOVEevent so that our object reacts to the mouse pointer movements. - Object position is calculated using
Mathpackage trigonometry methods. Moving around circle is all about angles.
There is not much to talk about. Below is a code that accomplishes what we want. All the “magic” happens in onMove() function. The result is: when you push mouse button down over the arrow and hold it – mouse move makes arrow rotate around the circle. Please read code comments.
Timeline Version
// UI container
var dial:Sprite;
// Draggable object. Can be anything. I chose arrow.
var arrow:Sprite;
// circle radius - play with changing this value
var radius:Number = 150;
// calculated angle
var angle:Number = 0;
// convert to radians - do it once to save on processing power
var angleConvert:Number = 180 / Math.PI;
// initiate application
init();
function init():void
{
makeDial();
makeArrow();
}
/**
* MouseEvent.MOUSE_MOVE handler.
* @param e
*/
function onMove(e:MouseEvent):void
{
// calculate angle between mouse position and the center of dial
angle = Math.atan2(mouseY - dial.y, mouseX - dial.x);
// reposition arrow according to angle
arrow.x = radius * Math.cos(angle);
arrow.y = radius * Math.sin(angle);
// rotate arrow so that it always point outward
arrow.rotation = angleConvert * angle + 90;
}
/**
* MouseEvent.MOUSE_DOWN handler
* @param e
*/
function onArrowDown(e:MouseEvent):void
{
/**
* MouseEvents listeners are added to STAGE - not arrow.
* This way event if Mouse is not over arrow - interaction is preserved.
*/
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onStageUp);
}
/**
* MouseEvent.MOUSE_UP handler
* @param e
*/
function onStageUp(e:MouseEvent):void
{
// by removing listeners we stop interaction.
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onStageUp);
}
/**
* Instantiates dial container and draws circle inside it.
*/
function makeDial():void {
dial = new Sprite();
var g:Graphics = dial.graphics;
g.lineStyle(1, 0x004000);
g.drawCircle(0, 0, radius);
dial.x = dial.y = radius + 50;
addChild(dial);
}
/**
* Instantiates arrow, draws triangle and adds arrow to dial display list.
*/
function makeArrow():void {
arrow = new Sprite();
// draw triangle
var g:Graphics = arrow.graphics;
g.beginFill(0x000080);
g.moveTo(0, -15);
g.lineTo(7, 6);
g.lineTo( -7, 6);
g.endFill();
// position arrow on the top of circle
arrow.y = -radius;
dial.addChild(arrow);
// make arrow interactive
arrow.buttonMode = arrow.useHandCursor = true;
arrow.addEventListener(MouseEvent.MOUSE_DOWN, onArrowDown);
}
Class Version
package
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
public class Dial extends Sprite
{
// UI container
private var dial:Sprite;
// Draggable object. Can be anything. I chose arrow.
private var arrow:Sprite;
// circle radius - play with changing this value
private var radius:Number = 150;
// convert to radians - do it once to save on processing power
private var angleConvert:Number = 180 / Math.PI;
// calculated angle
private var angle:Number = 0;
/**
* Constructor.
*/
public function Dial()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
/**
* Initializes UI
* @param e
*/
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
makeDial();
makeArrow();
}
/**
* MouseEvent.MOUSE_MOVE handler.
* @param e
*/
private function onMove(e:MouseEvent):void
{
// calculate angle between mouse position and the center of dial
angle = Math.atan2(mouseY - dial.y, mouseX - dial.x);
// reposition arrow according to angle
arrow.x = radius * Math.cos(angle);
arrow.y = radius * Math.sin(angle);
// rotate arrow so that it always point outward
arrow.rotation = angleConvert * angle + 90;
}
/**
* MouseEvent.MOUSE_DOWN handler
* @param e
*/
private function onArrowDown(e:MouseEvent):void
{
/**
* MouseEvents listeners are added to STAGE - not arrow.
* This way event if Mouse is not over arrow - interaction is preserved.
*/
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onStageUp);
}
/**
* MouseEvent.MOUSE_UP handler
* @param e
*/
private function onStageUp(e:MouseEvent):void
{
// by removing listeners we stop interaction.
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onStageUp);
}
/**
* Instantiates dial container and draws circle inside it.
*/
private function makeDial():void {
dial = new Sprite();
var g:Graphics = dial.graphics;
g.lineStyle(1, 0x004000);
g.drawCircle(0, 0, radius);
dial.x = dial.y = radius + 50;
addChild(dial);
}
/**
* Instantiates arrow, draws triangle and adds arrow to dial display list.
*/
private function makeArrow():void {
arrow = new Sprite();
// draw triangle
var g:Graphics = arrow.graphics;
g.beginFill(0x000080);
g.moveTo(0, -15);
g.lineTo(7, 6);
g.lineTo( -7, 6);
g.endFill();
// position arrow on the top of circle
arrow.y = -radius;
dial.addChild(arrow);
// make arrow interactive
arrow.buttonMode = arrow.useHandCursor = true;
arrow.addEventListener(MouseEvent.MOUSE_DOWN, onArrowDown);
}
}
}
Arranging Objects into 2D Dynamic Grid with ActionScript 3
Challenge
There are N objects (DisplayObject instances such as MovieClip, Sprite, TextField, Bitmap, etc.). These objects have to be positioned on the screen in even rows and columns dynamically so that resulting visual is a grid (table) with each cell containing one of the objects.
Solution
Although this post is very verbose, please do not fear. The essence of the solution takes only two lines of code.
In application development the most elegant, efficient, and scalable approaches are created when problems are restated in as abstract manner as possible.
For some novices the word “abstract” represents something that Gods do which is not true. Abstract means simple with all the “water” squeezed out! Also it means finding a minimalist way based on as little prerequisites as possible. In the context of this post it is not about objects or even programming language but about defining mathematical skeleton on which one can put any kind of meat.
Let’s restate our starting point.
We have N number of objects. N is the only information we have so far. Essentially we are dealing with a series of integers between 1 and N. Our task becomes finding cell coordinates (address) in two-dimensional space which in plain language states “current integer in the series belongs to column N and row M.” Everything else will easily fall into place once we figure out coordinates.
One more consideration. Because we know that Array is zero-based it is prudent to imagine our series of integers from 0 to N-1.
Math behind it is very simple.
Let’s find column and row an integer belong to in a grid that has 5 columns. The main trick is to utilize often under appreciated modulo % operator.
Say, we are interested in an integer 13. Modulo is: 13 % 5 = 3. Floor of the division is: int(13 / 5) = 2. One can use Math.floor() but casting to int is as good.
From grid perspective the result is: integer 13 belong to a cell in column 3 and row 2. Remember we use zero-based approach, so our first row is actually row 0 and first column is column 0.
Let’s go a little wild and see what column and row integer 1056 belongs to when our grid has 5 columns.
1056 % 5 = 1
int(1056 / 5) = 211
The answer is: integer 1056 belongs to column 1 and row 211 in a 5-column grid.
If you have time – you can plug in different number of columns and integers in, say, Excel.
What about row-based calculations? It is the same except we will use number of required rows as modulo and divide integer by by number of rows.
With the integer 1056 in a 5-row grid it will belong to the cell with:
row: 1056 % 5 = 1; column: int(1056 / 5) = 211.
As you see results of calculations are the same but row and column values are switched.
Our final formulas are:
| Column Based | Row Based | |
|---|---|---|
| column = | integer % numberOfColumns | int(integer / numberOfColumns) |
| row = | int(integer / numberOfColumns) | integer % numberOfColumns |
We have devised an abstract mathematical model that will allow us to calculate column and row value for any integer in any grid no matter if we use it ActionScript, JavaScript, Excel or do it on a piece of paper.
Basically, this is it! But we want to apply our model to ActionScript 3.
We will use for loop to iterate trough object indexes and calculate the cell object belongs to based on the loop iterator and number of desired columns or rows.
With this approach, if number of columns in the grid is used – objects are placed left-right/top-down; if number of rows – objects are placed top-down/left-right.
The code below draws 20 Sprites and places them into grid with 5 columns (read comments). It looks like a lot of code but 90% of it is written for illustration purposes – the topic-related logic is in for loop only.
makeGrid();
/**
* Creates grid holder and populates it with objects.
*/
function makeGrid():void {
// Sprite that holds grid
var gridContainer:Sprite = new Sprite();
// number of objects to place into grid
var numObjects:int = 20;
// number of columns in the grid
var numCols:int = 5;
// current column
var column:int = 0;
// current row
var row:int = 0;
// distance between objects
var gap:Number = 2;
// object that populates grid cell
var cell:Sprite;
for (var i:int = 0; i < numObjects; i++) {
// calculate current column using modulo operator
column = i % numCols;
// calculate current row
row = int(i / numCols);
// make object to place into grid
cell = makeObject(i, row, column);
// position object based on its width, height, column a row
cell.x = (cell.width + gap) * column;
cell.y = (cell.height + gap) * row;
gridContainer.addChild(cell);
trace(i, "\tcolumn =", column, "row =", row);
}
gridContainer.x = gridContainer.y = 20;
addChild(gridContainer);
}
/**
* Creates Sprite instance and draws its visuals.
* Arguments passed are used to create label.
* @param index
* @param row
* @param column
*/
function makeObject(index:int, row:int, column:int):Sprite {
var s:Sprite = new Sprite();
var g:Graphics = s.graphics;
g.lineStyle(1, 0xCCCCCC);
g.beginFill(0xF2F2F2);
g.drawRoundRect(0, 0, 100, 60, 5);
g.endFill();
// add label to the Sprite instance
var lbl:TextField = label(index, row, column);
lbl.x = lbl.y = 5;
s.addChild(lbl);
return s;
}
/**
* Creates TextField instance and writes text based on
* the passed arguments.
* @param index
* @param row
* @param column
*/
function label(index:int, row:int, column:int):TextField {
var tf:TextField = new TextField();
tf.width = 80;
tf.autoSize = TextFieldAutoSize.LEFT;
tf.multiline = tf.wordWrap = true;
tf.defaultTextFormat = new TextFormat("Arial", 12, 0x004824);
tf.text = "cell = " + index + "\nrow = " + row + "\ncolumn = " + column;
return tf;
}
This is the outcome:
Highlights:
The main logic is in the for loop. The only two values that are calculated are column and row which takes only two lines of code. The rest of the script uses parameterized values.
If we change numObjects to 18, grid will still have 5 columns and 4 rows except two rightmost places in the bottom row will be empty:
If you change numCols to 4 – grid will contain 4 columns and 5 rows:
Please inspect the values of cell in the images. This value increments as grid is being populated from left-to-right/top-to-bottom.
Now let’s use number of rows as a variable that affects how grid is composed. Theoretically you don’t have to do any more than just swap column and row variables where they are used. But to avoid confusion we rewrite makeGrid function and rename its variables for clarity sake. Comments outline changes I made to the previous code.
function makeGrid():void {
var gridContainer:Sprite = new Sprite();
var numObjects:int = 20;
// Change 1: number of ROWs in the grid
var numRows:int = 4;
var column:int = 0;
var row:int = 0;
var gap:Number = 2;
var cell:Sprite;
for (var i:int = 0; i < numObjects; i++) {
// Change 2: calculate current ROW using modulo operator
// in the previous code column was calculated this way
row = i % numRows;
// Change 3: calculate current COLUMN
// in the previous code row was calculated this way
column = int(i / numRows);
cell = makeObject(i, row, column);
cell.x = (cell.width + gap) * column;
cell.y = (cell.height + gap) * row;
gridContainer.addChild(cell);
trace(i, "\tcolumn =", column, "row =", row);
}
gridContainer.x = gridContainer.y = 20;
addChild(gridContainer);
}
Result is still a grid of 4 rows and 5 columns. BUT look at the cell number value. It iterates top-to-bottom/left-to-right as opposed to left-to-right/top-to-bottom when number of columns is used. Row and column values remain the same.
I used dynamic object creation for illustration purposes. Nevertheless, this code can easily fit other objectives. One of, perhaps, most popular use cases when this solution can be utilized is creating thumbnail grid for a slide show. Image loading is out of scope of this post but, in principal, what one can do is to create an array of loaded images and then use shown here loop to create a grid of thumbnails.
Here is an abstract example. Changes are commented:
// array of images or display objects
var images:Array = [image1, image2, image3, ... imageN];
makeGrid();
function makeGrid():void {
var gridContainer:Sprite = new Sprite();
// numObjects is the number of objects in the array
var numObjects:int = images.length;
var numCols:int = 5;
var column:int = 0;
var row:int = 0;
var gap:Number = 2;
var cell:DisplayObject;
for (var i:int = 0; i < numObjects; i++) {
column = i % numCols;
row = int(i / numCols);
// get corresponding object from the array
cell = images[i];
cell.x = (cell.width + gap) * column;
cell.y = (cell.height + gap) * row;
gridContainer.addChild(cell);
trace(i, "\tcolumn =", column, "row =", row);
}
gridContainer.x = gridContainer.y = 20;
addChild(gridContainer);
}
Often developers attempt more elaborate and cumbersome solutions with conditional logic. Presented here implementation is very efficient from both coding and performance standpoints.
Number Formatting with ActionScript 3 and RegExp
Snippets below format number to two standards.
American standard: commas precede every three digits and decimals are separated by period with a matrix ###,###,###.#### with optional decimals.
String(number).replace(String(number).indexOf(".") > -1 ? /(?<=\d)(?=(\d\d\d)+(?!\d)(?:\.\d*))/g : /(?<=\d)(?=(\d\d\d)+(?!\d))/g, ",").replace(/,{2,}/g, ",");
International standard: periods precede every three digits and decimals are separated by comma with a matrix ###.###.###,#### with optional decimals.
String(number).replace(".", ",").replace(String(number).indexOf(".") > -1 ?/(?<=\d)(?=(\d\d\d)+(?!\d)(?:\,\d*))/g : /(?<=\d)(?=(\d\d\d)+(?!\d))/g, ".").replace(/\.{2,}/g, ".");
Here is a test case with functions that perform formatting:
function americanFormat(number:Number):String {
return String(number).replace(String(number).indexOf(".") > -1 ? /(?<=\d)(?=(\d\d\d)+(?!\d)(?:\.\d*))/g : /(?<=\d)(?=(\d\d\d)+(?!\d))/g, ",").replace(/,{2,}/g, ",");
}
function internationalFormat(number:Number):String {
return String(number).replace(".", ",").replace(String(number).indexOf(".") > -1 ?/(?<=\d)(?=(\d\d\d)+(?!\d)(?:\,\d*))/g : /(?<=\d)(?=(\d\d\d)+(?!\d))/g, ".").replace(/\.{2,}/g, ".");
}
var num:Number = Math.random() * 100000000000;
trace(americanFormat(num));
trace(internationalFormat(num));
Flash Custom Video Player Tutorial :: Part 4
Putting it together
Although discussion suggested to use url parsing directly as arguments passed into NetConnection and NetStream instances, a much better practice is to separate functionally distinct tasks. This approach is more scalable to say the least. What I mean in the context of our task is that we should preprocess url BEFORE we move onto triggering video playback sequence.
We will create a special Object that will serve as a repository of values that we utilize as arguments passed into NetConnection.connect() and NetStream.play() methods.
Diagram represents this requirement change. Green lines describe how and when our configuration Object will be used.

Below is a full code for our player that takes into consideration what was previously written in this tutorial. Please read comments. Diagram’s step numbers are shown in the comments as well.
Also note that I added four methods:
- onMetaData,
- onBWDone,
- onXMPData,
- onPlayStatus.
When we discussed client earlier I stated that NetStream expects several functions to be present in the code. The methods related to client in our code are the most often used ones. How to utilize these functions is outside of the scope of this tutorial. If you are interested in expending your knowledge additional material can be easily found. Good place to start is Adobe AS3 documentation.
// point to objects we need to play video
import flash.events.NetStatusEvent;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.media.Video;
// video address - use your own
var videoURL:String = "myVideo.flv";
// object that holds url parsing results
// STEP 1 - Parse URL
var streamData:Object = parseURL(videoURL);;
// declare variables
var nc:NetConnection;
var ns:NetStream;
var video:Video;
/**
* STEP 2 - Establish connection
* To remedy multi-frame timeline
* re-instantiation when timeline returns to Frame 1
* we use conditional
* in plain language:
* if connection doesn't exist - create it
* otherwise - do nothing.
*/
if(!nc) connect();
/**
* Creates NetConnection instance,
* adds listener to NetConnection,
* attempts connection.
*/
function connect():void {
nc = new NetConnection();
nc.client = this;
/**
* STEP 3 - Wait for connection feedback
* listen to events related to connection
*/
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
trace(streamData.ncParam );
nc.connect(streamData.ncParam);
}
/**
* Listens to related to playback events.
*/
function onNetStatus(e:NetStatusEvent):void {
switch(e.info.code) {
/**
* STEP 4
* ONLY when connection is successful
* we call function that creates NetStream instance.
*/
case "NetConnection.Connect.Success":
/**
* STEP 5 - Establish stream
*/
stream();
break;
}
}
/**
* Creates NetStream instance
*/
function stream():void {
ns = new NetStream(nc);
ns.client = this;
// STEP 6 - Play stream in video
startVideo();
}
/**
* Instantiates video and makes it show stream.
*/
function startVideo():void {
video = new Video(640, 385);
video.x = video.y = 20;
addChild(video);
video.attachNetStream(ns);
ns.play(streamData.nsParam);
}
/**
* Creates an Object,
* Parses url and assigns values to
* 1. ncParam - argument that is passed into NetConnection.connect()
* 3. nsParam = argument that is passed into NetStream.play()
* @param url
* @return
*/
function parseURL(url:String):Object {
var object:Object = { };
object.ncParam = url.match(/^rtmp\w?\:\/\/.+\//);
object.nsParam = nsPath(url);
return object;
}
/**
* Translates url into string that is used as an argument
* in NetStream.play() method.
* If video is streaming:
* 1. Extracts file name
* 2. Truncates .flv extension
* 3. Prepends with mp4: if necessary
*
* If video is progressive - returns unchanged url
*
* @param url
* @return
*/
function nsPath(url:String):String {
/**
* if video is streaming - process url,
* progressive url will be returned unchanged
*/
if (url.match(/^rtmp\w?/)) {
// get file name
url = url.match(/(?<=\/)(\w+)((\.\w+(?=\?))|(\.\w+)$)/g)[0];
/**
* Abbreviated syntax for the logic:
* if extension is .flv - then replace it with empty string
* otherwise - prepend it with mp:4
*/
url = url.match(/\.\w+$/)[0].toLocaleLowerCase() == ".flv" ? url.replace(/\.\w+$/, "") : "mp4:" + url;
}
return url;
}
/** METHODS CALLED BY NetStream **/
/**
* Video data in form of object
* @param info - video data
*/
function onMetaData(info:Object):void {
trace("********** onMetaData START");
for (var prop:String in info) {
trace(prop, info[prop]);
}
trace("********** onMetaData END");
}
/**
* Bandwidth detection.
* @param info
*/
function onBWDone(info = null):void {
trace("********** onBWDone START");
for (var prop:String in info) {
trace(prop, info[prop]);
}
trace("********** onBWDone END");
}
/**
* XML representation of video data.
* @param info
*/
function onXMPData (info:Object):void {
trace("********** onXMPData START");
for (var prop:String in info) {
trace(prop, info[prop]);
}
trace("********** onXMPData END");
}
/**
* Usually indicates the end of streaming playback stop
* @param info
*/
function onPlayStatus(info:Object):void {
trace("********** onPlayStatus START");
for (var prop:String in info) {
trace(prop, info[prop]);
}
trace("********** onPlayStatus END");
}
This concludes tutorial. Please let me know if you need further clarifications. I will try to accommodate all requests to make this tutorial as comprehensive as possible.
Flash Custom Video Player Tutorial :: Part 3
Video Address (URL)
What has been written here so far conveys, I hope, a strong message that video asset’s address and (in the majority of cases) address ONLY is the entity that holds information about asset itself. Thus, the first thing to start player with is to devise a comprehensive engine that parses video url into bits and pieces we can utilize in our code.
Diagram below outlines url parsing activities:

It may look scary but it boils down to two questions only with Boolean (yes/no) answers:
- Is video Streaming?
- Is file flv?
URL Parsing
Because, as I mentioned earlier, I don’t have live examples to use I will rely on fake urls. Parts that are of interest to us are highlighted in red.
Progressive videos urls:
file///C:/Users/Joe/my%20project/videos/myFLVVideo.flv
http://myserver.mydomain.com/whatever/myFLVVideo.flv
https://subdomain.mydomain.com/videos/myHDVideo.mp
myLocalVideo.mov
Streaming videos urls:
rtmp://streamingserver.mydomain.com/application/assets/myStreamingFLVVideo.flv
rtmpt://tunneledstream.mydomain.com/app/myvideos/myStreamingHDVideo.mp4
rtmpe://protectedstream.mydomain.com/apl/videos/anotherVideo.f4v
I declare these urls as Strings.
// progressive urls var progressive0:String = "file:///C:/Users/Joe/my%20project/videos/localFLVVideo.flv"; var progressive1:String = "http://myserver.mydomain.com/whatever/FLVVideo.flv"; var progressive2:String = "https://subdomain.mydomain.com/videos/HDVideo.mp4"; var progressive3:String = "myLocalVideo.mov"; // streaming urls var streaming1:String = "rtmp://streamingserver.mydomain.com/application/assets/StreamingFLVVideo.flv"; var streaming2:String = "rtmpt://tunneledstream.mydomain.com/app/myvideos/StreamingHDVideo.mp4"; var streaming3:String = "rtmpe://protectedstream.mydomain.com/apl/videos/anotherVideo.f4v";
Now let’s write a mechanism that will parse urls so that we can use information in our code.
But before we do that let’s rephrase what we have learned so far from a perspective of NetConnection and NetStream separately in the context of url string.
Wouldn’t it be nice to do it in one line of code? For instance, something like that (desired conditional logic is enclosed in square brackets[]):
var nc:NetConnection = new NetConnection(); nc.connect([if video is streaming - write directory, otherwise - null]); var ns:NetStream = new NetStream(nc); ns.play([if video is NOT streaming - write entire url, otherwise - write file name]);
We will use RegExp for our needs. Click here for a post devoted to parsing urls with regular expressions.
From NetConnection perspective the following regular expression will do the job:
// regular expression pattern to look for
var pattern:RegExp = /^rtmp\w?\:\/\/.+\//;
// read each url
trace("progressive0", progressive0.match(pattern));
trace("progressive1", progressive1.match(pattern));
trace("progressive2", progressive2.match(pattern));
trace("progressive3", progressive3.match(pattern));
trace("streaming1", streaming1.match(pattern));
trace("streaming2", streaming2.match(pattern));
trace("streaming3", streaming3.match(pattern));
The code writes the following trace outputs:
progressive0 null
progressive1 null
progressive2 null
progressive3 null
streaming1 rtmp://streamingserver.mydomain.com/application/assets/
streaming2 rtmpt://tunneledstream.mydomain.com/app/myvideos/
streaming3 rtmpe://protectedstream.mydomain.com/apl/videos/
It is EXACTLY what we want! RegExp returns null for progressive and address for streaming videos. Now we can write our NetConnection just like that:
var nc:NetConnection = new NetConnection(); // url is any video address nc.connect(url.match(/^rtmp\w?\:\/\/.+\//));
Isn’t it elegant?
Approaching NetStream is a bit trickier for there are more than two conditions to check and, consequently, act upon. Let’s create a function that will do it for us. Code below processes url into a string that we can later pass as the first argument of NetStream.play() method. For now I will show a function and traces that use it to display what is returned on our sample urls.
function nsPath(url:String):String {
/**
* if video is streaming - process url,
* progressive url will be returned unchanged
*/
if (url.match(/^rtmp\w?/)) {
// get file name
url = url.match(/(?<=\/)(\w+)((\.\w+(?=\?))|(\.\w+)$)/g)[0];
/**
* Abbreviated syntax for the logic:
* if extension is .flv - then replace it with empty string
* otherwise - prepend it with mp:4
*/
url = url.match(/\.\w+$/)[0].toLocaleLowerCase() == ".flv" ? url.replace(/\.\w+$/, "") : "mp4:" + url;
}
return url;
}
trace("progressive0", nsPath(progressive0));
trace("progressive1", nsPath(progressive1));
trace("progressive2", nsPath(progressive2));
trace("progressive3", nsPath(progressive3));
trace("streaming1", nsPath(streaming1));
trace("streaming2", nsPath(streaming2));
trace("streaming3", nsPath(streaming3));
Traces are:
progressive0 file:///C:/Users/Joe/my%20project/videos/localFLVVideo.flv
progressive1 http://myserver.mydomain.com/whatever/FLVVideo.flv
progressive2 https://subdomain.mydomain.com/videos/HDVideo.mp4
progressive3 LocalVideo.mov
streaming1 StreamingFLVVideo
streaming2 mp4:StreamingHDVideo.mp4
streaming3 mp4:anotherVideo.f4v
Again, this is what we need.
Now we have enough information to write entire player. Part 4 puts is all together.
Flash Custom Video Player Tutorial :: Part 2
Video delivery types
First important consideration is how asset is delivered. There are two video types from Flash perspective:
- Progressive video
- Streaming video
In a nutshell, the differences are:
Progressive playback
With progressive playback video file is loaded into client’s PC memory completely. This is if you wait long enough. I made “long enough” comment because what matters the most is that in order for user to see video to the end it must be downloaded to the last byte. This doesn’t mean, however, that user has to wait for all 100MB to start enjoying the show. Video can start playing as soon as the first milliseconds of it are in the buffer. Once video is finished downloading – it will stay in the memory until you close browser or clear browser cache. Benefit of caching is that if user wants to watch the video again – playback is practically instant: file is played from computer’s memory and doesn’t have to be loaded again even if you ask Flash to reload it.
By the way, this caching reality is true for any kind of asset – images, sounds, XMLs, etc.
From Flash perspective progressive video playback is not much different from any other file in a sense that server doesn’t have to have a video-specific functionality in order to return video file back to Flash.
Since the server doesn’t have special handling, it generally doesn’t have to (and it doesn’t have to have to) react to connection request. Nevertheless, NetStream requires an established connection. To accommodate this we pass null as the first NetConnection parameter. On the NetStream side we will pass entire video file address.
If video address is:
http://www.mydomain.com/videos/interstingVideo.flv
our code can be just:
// other code is omitted for brevity
function connect():void {
nc = new NetConnection();
nc.client = this;
// STEP 2
// note null
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.connect(null);
}
function startVideo():void {
video = new Video(640, 385);
video.x = video.y = 20;
addChild(video);
video.attachNetStream(ns);
// entire url is passed into play() method
ns.play("http://www.mydomain.com/videos/interstingVideo.flv");
}
The same will go with local files. If file is in the same directory as SWF, and file name is the same, we write:
function startVideo():void {
video = new Video(640, 385);
video.x = video.y = 20;
addChild(video);
video.attachNetStream(ns);
// entire url is passed into play() method
ns.play("interstingVideo.flv");
}
Streaming playback
Streaming video is, basically, chopped into small pieces by a server and these pieces are fetched to the player. This makes it
- start playback faster;
- possible to jump (seek) from one part of the video to another at will (with progressive video one cannot see what is not yet downloaded);
- play very large files (think HD) on practically any computer and low bandwidth;
- free memory from pieces that are already played, one of the benefits of which is performance improvement.
This may lead to a question “how do we know what type of video we are dealing with?”
At present Adobe supports four streaming protocols:
- rtmp
- rtmpt
- rtmpe
- rtmps.
A sufficient in 99% of the cases conclusion is: if request protocol doesn’t start with rtmp – it is a progressive video and vice versa.
The major differences between progressive and streaming playbacks is that with streaming video file is preprocessed (chopped) before it is returned to Flash player. This is a special server side technology. Streaming requires a persistent staying alive connection so that all these crumbs of video keep coming over time.
We need to connect to a special place (application) on the server that performs “chopping” – null will not do. Address of the server application is part of url. Servers can be, of course, configured in many ways but in the majority of case it is enough to connect to a directory where video file is hosted.
Assuming our video is on a streaming server and its address is:
rtmp://www.mydomain.com/videos/interstingVideo.flv
here is how we have to pass the argument into NetConnection.connect() method:
function connect():void {
nc = new NetConnection();
nc.client = this;
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
// connect to the directory where file is hosted
nc.connect("rtmp://www.mydomain.com/videos/");
}
function startVideo():void {
video = new Video(640, 385);
video.x = video.y = 20;
addChild(video);
video.attachNetStream(ns);
// point to the file itself
ns.play("interstingVideo");
}
Note that we truncated .flv – we will talk about it in the next section.
Video encoding (codec) types
I am not going into deep details with this topic. What we need know is just if there is any hint that indicates how video was encoded when we play streaming video. This is not to satisfy our curiosity (after all, who cares?) but because we are forced to consider it.
Right off the bat, for progressive video codec doesn’t really matter from coding standpoint.
The gist of it is that for whatever reason (don’t ask me why – I am not guilty) Adobe decided that when streaming video is encoded with H.264 standard NetStream.play() first argument has to be prefixed with mp4. The most used file extensions for this encoding standard are mp4 and f4v. If I lost you (as, at some point, they lost me) here is an example:
Say, our video url is:
rtmp://whatever.domain.com/directory/interestingVideo.mp4
You cannot just play interstingVideo file. It has to be prefixed with mp4:. The string must take the following form:
mp4:interestingVideo.mp4
The last thing that affects streaming playback is whether extension is flv. If it is – we will need to truncate it. Other extension types must remain intact. With progressive file it is not necessary.
By now we know that video asset url serves as a container of important data. Part 3 explores how to extract information from url and use it in our program.
Flash Custom Video Player Tutorial :: Part 1
Introduction
This four-part post is about minimal (sort of) path to establishing video playback in Flash. I will not cover video controls, positioning, etc. The aim is to present reader with bare necessities.
I don’t have resources to publicly host video assets. If you would like to test the code in this tutorial, please use your own urls or point to video files in your local environment. Also, you will definitely benefit from installing Adobe’s Flash Media Server (FMS) on you machine – developer’s edition is free and fully functional. Knowing FMS’ capabilities will greatly expand your understanding of streaming media.
One more note. This tutorial is tailored to developers who use Flash IDE timeline. This is not my preference but I realize that the majority of readers will be beginners who tend to start with timeline based approaches.
Concept of playing video in Flash
To play video in Flash, one has to use at least three objects:
- NetConnection
- NetStream
- Video
Diagram shows a sequence of steps one has to take in order to establish video playback as well as relationships between instances:

One of the ways to look at the sequence is: open tunnel (connection) through which video file will be streamed and, then, place stream into video.
Here is how our diagram translates into a code. Note, I reserved placers for urls. We will cover asset address in a special section. Understanding of video url and how it affects coding approaches is crucial.
// Establish Connection var nc:NetConnection = new NetConnection(); nc.connect([directory to connect to]); // Establish Stream // note, we use previously instantiated NetConnection var ns:NetStream = new NetStream(nc); // start playback ns.play([file to play]); // Play Stream in a Video // note, we use NetStream instance var video:Video = new Video(); video.attachNetStream(ns);
Often, the first time developer encounters NetConnection and NetStream is when s(he) attempts to play video. Some people may ask “Why is it so complicated? Why not to have a single class instead of three?”
The thing is that NetConnection and NetStream have much wider applications. One can connect to a server for other than playing media purposes. Many different file formats can be streamed to client. Video is just one of the uses of NetConnection/NetStream.
It is not obvious at a first glance that there is one more step we should take. The reality is that when player operates on Internet there is always a time lag between the moment we call connect() on a NetConnection and get back response about connection attempt outcome. Result can be different too: connection can be successful, fail, etc. In addition, NetStream doesn’t accept an instance of NetConnection that hasn’t gone through the round trip to server and proclaimed that connection has been successful (error #2126 will thrown).
NetConnection can always let you know its status by dispatching events. With that said, use case for video playback implementation is:

The following code implements scenario outlined in the diagram. Steps are numbered to link code pieces to the diagram. Commented steps in the code label function calls – not functions themselves (I think it is more intuitive for it describes actions – not consequences of them). Please read comments.
// declare variables
var nc:NetConnection;
var ns:NetStream;
var video:Video;
/**
* STEP 1
* To remedy multi frame timeline
* re-instantiation when timeline returns to Frame 1
* we use conditional
* in plain language:
* if connection doesn't exist - create it
* otherwise - do nothing.
*/
if(!nc) connect();
/**
* Creates NetConnection instance,
* adds listener to NetConnection,
* attempts connection.
*/
function connect():void {
nc = new NetConnection();
nc.client = this;
// STEP 2
// listen to events related to connection
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.connect(["connection directory"]);
}
/**
* Listens to related to playback events.
*/
function onNetStatus(e:NetStatusEvent):void {
switch(e.info.code) {
// STEP 3
// ONLY when connection is successful
// we call function that creates NetStream instance.
case "NetConnection.Connect.Success":
// STEP 4
stream();
break;
}
}
/**
* Creates NetStream instance
*/
function stream():void {
ns = new NetStream(nc);
ns.client = this;
// STEP 5
startVideo();
}
/**
* Instantiates video and makes it show stream.
*/
function startVideo():void {
video = new Video(640, 385);
video.x = video.y = 20;
addChild(video);
video.attachNetStream(ns);
ns.play(["file name"]);
}
Important highlights of the code are:
- We have enclosed each block corresponding to diagram actions into district functions. This way we stay in full control and make it possible to invoke each piece of functionality as needed and when it is appropriate from standpoint of program flow readiness for the next step.
- Event
NetStatusEventthat is captured byonNetStatus()handler has a propertyinfo– this property is anObjectthat, in turn, has a propertycode. We rely oncodeto determine what happened (note past tense). Read more aboutNetStatusEventhere. This event is dispatched byNetStreamas well which invaluable when monitoring playback progress but it is outside of the scope of this tutorial.
The following deserves special elaboration.
We introduced client assignment to both NetConnection and NetStream instances: nc.client = this and ns.client = this. Although this is not academically correct, for visualization purposes, it is worth looking at NetConnection and NetStream as small pieces of software that have internal workings (actually this functionality is written on the server side). As such, they are not completely independent in a sense that they rely on a program that uses them. Hence the term client – or “consumer program” of the connection and stream. By default client is null but over the life span of these two entities they require that client does exist. Most notably NetConnection expects the program to implement several functions. If these functions are not found in the client‘s code – errors are thrown.
We need to remember that we have to provide instances of NetConnection and NetStream with references to an Object that has the functions NetConnection and NetStream instances call. By stating ns.client = this we essentially pledge that NetStream will find functions it invokes in the current scope. To fulfill this promise we must implement these functions.
Before we start writing code I would like to talk about video asset itself. Understanding of how Flash is addressing video properties and file delivery specifics is crucial for establishing of a successful playback. In addition, knowing how different video features affect functionality will lead to writing of a more scalable player.
Parsing url string with regular expressions (RegExp) in ActionScript 3
Dynamic parsing of url (uri) is often a part of scalable application. This tutorial demonstrates techniques of reading different parts of url String with ActionScrip 3 RegExp class.
The snippets presented here do not cover all the possible variations that you can come across. But the beauty of regular expressions is that they can be easily tailored to accommodate each specific case.
Regular expressions are wonderful when they work. They are not easy to understand though It takes time to get used to the syntax and, also, it seems, they never work as promised. This is especially true with lookahead and lookbehind syntax. I guess, as with anything, practice leads to perfection.
Here is a common url in an abstract form:
protocol://www.domainName.domainExtension/directory1/directory2/directoryN/fileName.fileExtension?var1=var1Value&var2=var2Value.
We will use this url to output string segments.
Request protocol pattern
var protocolPattern:RegExp = /^\w+(?=:\/\/)/;
trace("protocol =", url.match(protocolPattern)); // protocol = protocol
Full domain pattern
var protocolPattern:RegExp = /^\w+(?=:\/\/)/;
trace("full domain name =", url.match(protocolPattern)); // full domain name = www.domainName.domainExtension
This pattern will also match if port number is present in the url as in
protocol://www.domainName.domainExtension:1935/directory.
Domain with optional protocol pattern
var domainWithOptionalProtocol:RegExp = /^(\w+:\/\/)?[\w+.]+(?=(\/|:\d+))/;
trace("domain with optional protocol =", url.match(domainWithOptionalProtocol)[0]);
//domain with optional protocol = protocol://www.domainName.domainExtension
Note that we introduced [0] index. Actual output of this match is an array with three elements:
protocol://www.domainName.domainExtension,protocol://,:1935
Port number pattern
var portNumberPattern:RegExp = /(?<=\:)\d+(?=\/)/g;
trace("port number =", url.match(portNumberPattern)); // port number = 1935
Subdirectories pattern
var directoriesPattern:RegExp = /(?<=\/)(\w+)(?=\/)/g;
trace("directories = ", url.match(directoriesPattern));
// directories = directory1,directory2,directoryN
Again, note that trace is an Array
Full path to file directory pattern
var filePath:RegExp = /^.+\//;
trace("file directory = ", url.match(filePath));
//file directory = protocol://www.domainName.domainExtension:1935/directory1/directory2/directoryN/
File name with extension pattern
var fileWithExtension:RegExp = /(?<=\/)(\w+)((\.\w+(?=\?))|(\.\w+)$)/g;
trace("file name with extension =", url.match(fileWithExtension));
// file name with extension = fileName.fileExtension
Note, this pattern will read file name with or without url query appended.
File name without extension pattern
var fileNameNoExt:RegExp = /(?<=\/)(\w+)(?=\.\w+(\?.*)*$)/g;
trace("file name no extension =", url.match(fileNameNoExt));
// file name no extension = fileName
Again, url query string is taken into account.
File extension pattern
var fileExtensionPattern:RegExp = /(?!\/\w+\.)(\w+$)|(?!\/\w+\.)(\w+)(?=\?.*$)/g;
trace("file extension =", url.match(fileExtensionPattern)[0]);
// file extension = fileExtension
Url query pattern
var queryPattern:RegExp = /(?<=\?).+$/;
trace("url query =", url.match(queryPattern));
// url query = var1=var1Value&var2=var2Value
It would be just natural to come up with a mechanism that reads url query variables. The following code extracts url variable/value pairs and stores them in an object.
var queryPattern:RegExp = /(?<=\?).+$/;
// parse url query String
var query:Array = url.match(queryPattern);
trace("url query =", query[0]);
// parse url variable/value pairs
var pairs:Array = query[0].split("&");
trace("pairs =" , pairs);
// Object that holds url variables
var urlVars:Object = { };
// Array that holds pair split
var pairSplit:Array;
for each(var s:String in query[0].split("&")) {
// split using equal character as delimiter
pairSplit = s.split("=");
// first element of split becomes Object's property; second element - value
urlVars[pairSplit[0]] = pairSplit[1];
}
// output urlVars' properties and values
trace("read object:");
for (var prop:String in urlVars) {
trace("\t", prop, "=", urlVars[prop]);
}
Conclusion
Using regular expressions present us with very abbreviated (id not the most abbreviated) ways to parse strings. Alternatives that utilize loop to search for patterns look very cumbersome after one gets her/his hands dirty with RegExp.
Using Date and RegExp classes to format time display
Challenge: display time countdown in hours, minutes, seconds formatted as 00:00:00.
Cases when this is needed are time countdown, video/audio playback progression, etc. First thing many developers attempt is calculating hours, minutes, seconds based on milliseconds with division, modulus, etc. and then convert numbers into strings adding preceding zeros if number is less than 10.
Here is a typical solution that is used:
function formatTime (time:Number):String
{
var date:Date = new Date(time);
var dateString:String = date.toUTCString();
var r:RegExp = /(\w+\s){2}+\d+\s{1}|\s\d+\s\w+/g;
trace("DATE", dateString, dateString.replace(r, "#"));
var remainder:Number;
var hours:Number = time / (60 * 60);
remainder = hours - (Math.floor (hours));
hours = Math.floor (hours);
var minutes = remainder * 60;
remainder = minutes - (Math.floor(minutes));
minutes = Math.floor (minutes);
var seconds = remainder * 60;
remainder = seconds - (Math.floor(seconds));
seconds = Math.floor(seconds);
var hString:String = hours < 10 ? "0" + hours : "" + hours;
var mString:String = minutes < 10 ? "0" + minutes : "" + minutes;
var sString:String = seconds < 10 ? "0" + seconds : "" + seconds;
if ( time < 0 || isNaN(time)) return "00:00";
if ( hours > 0 )
{
return hString + ":" + mString + ":" + sString;
}
else
{
return mString + ":" + sString;
}
}
This code contains 29 lines!
AS3 Date and RegExp (regular expressions) classes will do it for you with no additional math. As I will demonstrate time formatting can be accomplished with a single line of code.
For simplicity sake we will use Timer instance although any value that represents time in milliseconds can be used. One of the most popular usages is video playback time display.
Here is a first take on a code that formats time in a much more concise manner.
var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(e:TimerEvent):void {
trace("return string", formatTime(getTimer()));
}
function formatTime(time:Number):String {
var date:Date = new Date(time);
trace(date);
var dateString:String = date.toUTCString();
trace(dateString);
var pattern:RegExp = /(\w+\s){2}\d+\s{1}|\s\d+\s\w+/g;
dateString = dateString.replace(pattern, "");
return dateString;
}
The code is already much shorter. Not to mention fewer variable declarations.
Let’s explore how it works.
First, we declare instance of Date and pass time value into it. Tracing of date produces the following output:Wed Dec 31 19:00:01 GMT-0500 1969.
What is going on is that Date uses epoch time that starts at midnight January 1, 1970. In other words, 12AM of January 1, 1970 is a zero time.
But we are interested in time part of date only. We want our time to start from zero and progress in one second increments. In this context, how do we deal with the fact that although our date instance contains information about time it starts with 19:00:00? Because Date class obviously reads our local time by default, how do we remedy that and convert time into zero-based value?
The answer is toUTCString() method of Date. This method converts local time into UTC (Coordinated Universal Time) time.
Here is a trace of date.toUTCString():Thu Jan 1 00:00:01 1970 UTC.
Now our date gives us desired zero-based time. Thus, we set the value of our date String to UTC value.
Our next task is to extract time value from date. There are several ways to do that and Date class has methods that will get any part of the date but the aim here is to have as an abbreviated solution as possible. Abbreviation can be easily attained with String manipulations using regular expressions. Regular expressions are patterns in a string. Once you find a desired pattern you can edit String to your requirements.
In our case we want to find patterns that DO NOT reflect time and replace them with empty strings so that resulting string is, say 00:00:01. These unneeded parts of the date string are (example above): Thu Jan 1[space] and [space]1970 UTC. Note that we include spaces.
Here is what our variable pattern does:
(\w+\s)describesThu[space]orJan[space]pattern: alphabetical characters (Thu or Jan) followed by space;(\w+\s){2}describes above groups following each other two times as inThu Jan[space];(\w+\s){2}\d+\s{1}describes above plus digits\d+that are followed by space\sone time{1}.
We also need to get rid of [space]1970 UTC part of date string. Here is a break down:
\s\d+: space followed by digits –[space]1970;\s\d+\s: above followed by space –[space]1970[space];\s\d+\s\w+: above followed by alphabetical characters –[space]1970[space]UTC.
The last element of our pattern is pipe “|” that is OR operator. In other words, our pattern is either Thu[space]Jan[space]1[space] OR [space]1970[space]UTC.
Next step is to replace pattern with nothing. Here is where we use replace() method:
dateString = dateString.replace(pattern, "").
Resulting string comes out now the way we want: 00:00:01.
I promised that all this will take one line of code. Here it is coming to life:
function formatTime(time:Number):String {
return new Date(time).toUTCString().replace(/(\w+\s){2}+\d+\s{1}|\s\d+\s\w+/g, "");
}
Here are some variations.
The following function returns minutes and seconds formatted as 00:00.
function formatTime(time:Number):String {
return new Date(time).toUTCString().replace(/(\w+\s){2}+\d+\s{1}\d+\:|\s\d+\s\w+/g, "");
}
Another trick of the trade. This one formats time as hours: 00 minutes: 00 seconds: 01
function formatTime(time:Number):String {
return "hours: " + new Date(time).toUTCString().replace(/(\w+\s){2}+\d+\s{1}|\s\d+\s\w+/g, "").replace(/:/, " minutesX ").replace(/:/, " secondsX ").replace(/X/ig, ":");
}



