Low level AS3 - Establishing an RTMP connection with Socket and ByteArray

Second Part - Conclusion and Realisation
Milan Toth
Milan
Toth is the Chief Flash Developer of Jasmin Media Group, he created one
of the world's biggest flash media server system. He loves Eclipse and
OS X, AS3 and JAVA, sci-fi and horror, metal and electronic.
RTMP header:
first byte :
0x03 in case of a 12 byte length header
or
0x43 in case of a 8 byte length header
or
0xC3 in case of a 0 byte length header
next three bytes:
unknown, can be 0x00
next three bytes:
body length, without inter-chunk headers
maximum body size is FFFFFF = 16777215 bytes
next byte:
body type
0x14 : invoke with AMF data
next four bytes:
unknown, can be 0x00
Body bytes in case of invoke
invoke identifier encoded as an AMF string
followed by a number in double precision floating point number ( maybe result request? )
followed by the AMF-encoded arguments
at connection, an AMF encoded object with compulsory properties needed:
- app : the application identifier to connect to
- swfURL : referrer of the swf
- flashVer : agent
- audioCodecs
- videoCodes
- pageURL
body have to be split up into 128-byte length chunks inserting a 0-byte rtmp header ( 0xC3 ) between chunks
AMF encoding
String:
0x02 followed by the size of the string on two bytes, max length: 65535
null:
0x05
Object
0x03 closed by 0x00 0x00 0x09
key - value pairs are defined inside them, keys as amf strings
values as amf encoded data
Fortunately we don't need to know how to encode AMF data, because ByteArray has a built-in AMF encoder function, and it is also faster than a simple implementation.
And that's all. Let's see how to realize it:
package
{
import flash.net.Socket;
import flash.net.ObjectEncoding;
import flash.utils.ByteArray;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.display.Sprite;
public class OpenRTMPConnection extends Sprite
{
// connection status : inactive, pending, handshake, active
public var status : String;
public var socket : Socket;
public function OpenRTMPConnection ( )
{
// reset status
status = "inactive";
// create new socket
socket = new Socket( );
// initializing events
socket.addEventListener( Event.CLOSE , onClose );
socket.addEventListener( Event.CONNECT , onConnect );
socket.addEventListener( IOErrorEvent.IO_ERROR , onIOError );
socket.addEventListener( ProgressEvent.SOCKET_DATA , onData );
socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR , onSecurityError );
// connect
socket.connect( "localhost" , 1935 );
}
public function onClose ( event:Event ):void
{
trace( "onClose: " + event );
}
public function onIOError ( event:IOErrorEvent ):void
{
trace( "onIOError: " + event );
}
public function onSecurityError ( event:SecurityErrorEvent ):void
{
trace( "onSecurityError " + event );
}
public function onData ( event:ProgressEvent ):void
{
trace( "onData " + status );
switch ( status )
{
case "active" : break;
case "pending" : sendConnection( ); break;
case "inactive" : break;
case "handshake" : openConnection( ); break;
}
}
public function onConnect ( event:Event ):void
{
trace( "onConnect" );
status = "pending";
var count : int = -1;
var bytes : ByteArray = new ByteArray( );
// send first handshake : 0x03 followed by 1536 bytes
// write header byte
bytes.writeByte( 0x03 );
// write 1536 random ( zero in my case ) bytes
while ( ++count < 1536 ) bytes.writeByte( 0x00 );
socket.writeBytes( bytes );
// send data
socket.flush( );
}
public function sendConnection ( ):void
{
status = "handshake";
// create instances
var agentInfo : Object = new Object( );
var copyBytes : ByteArray = new ByteArray( );
var rtmpBytes : ByteArray = new ByteArray( );
var bodyBytes : ByteArray = new ByteArray( );
// CREATE PLAYER SETTINGS
// application to connect to
agentInfo["app" ] = "milgra";
// referrer
agentInfo["swfUrl" ] = "Kilroy was here...";
// page url
agentInfo["pageUrl" ] = "She sells sea shells...";
// agent
agentInfo["flashVer" ] = "DOS 5.00 with Norton Commander";
agentInfo["tcUrl" ] = "rtmp://localhost/milgra";
agentInfo["audioCodecs" ] = 615;
agentInfo["videoCodecs" ] = 76;
agentInfo["videoFunction" ] = 0;
agentInfo["objectEncoding" ] = 0;
// CREATE BODY
// we use ByteArray's built-in AMF encoder, amf0 is needed
bodyBytes.objectEncoding = ObjectEncoding.AMF0;
// write method id
// ( AMF String id is 0x03 then string length on 2 bytes, then UTF-encoded string )
bodyBytes.writeObject( "connect" );
// a 64-bit double-precision floating point number is next
// ( AMF Number is 0x00 then number on 8 bytes as a signed, little - endian encoded
// double precision floating point number )
bodyBytes.writeObject( 1 );
// encoding agent info
// ( AMF object starts with the id 0x03 , ends with 0x00 0x00 0x09,
// keys are AMF strings without starting 0x03, values are standard AMF values
bodyBytes.writeObject( agentInfo );
// CREATE FIRST chunk
// cloning second 1536 bytes from server respose
socket.readByte( );
socket.readBytes( copyBytes , 0 , 1536 );
socket.readBytes( copyBytes , 0 , 1536 );
// CREATE RTMP header
// first byte, header size is 12 byte
rtmpBytes.writeByte( 0x03 );
// next three bytes
rtmpBytes.writeByte( 0x00 );
rtmpBytes.writeByte( 0x00 );
rtmpBytes.writeByte( 0x00 );
// body size in three bytes
var firstByte : int = bodyBytes.length >> 16;
rtmpBytes.writeByte( firstByte );
rtmpBytes.writeShort( bodyBytes.length );
// body type in one byte = 0x14 == invoke
rtmpBytes.writeByte( 0x14 );
// last four bytes
rtmpBytes.writeByte( 0x00 );
rtmpBytes.writeByte( 0x00 );
rtmpBytes.writeByte( 0x00 );
rtmpBytes.writeByte( 0x00 );
// split body into 128 byte chunks with zero-byte rtmp headers
var counter : int = -1;
var newBody : ByteArray = new ByteArray( );
bodyBytes.position = 0;
while( ++counter < bodyBytes.length )
{
// write header
if ( counter % 128 == 0 && counter != 0 ) newBody.writeByte( 0xc3 );
// copy byte
newBody.writeByte( bodyBytes.readByte( ) );
}
// CREATE MESSAGE
socket.writeBytes( copyBytes );
socket.writeBytes( rtmpBytes );
socket.writeBytes( newBody );
// send data
socket.flush( );
}
public function openConnection ( ):void
{
trace( "openConnection" );
// server log :
// agent: DOS 5.00 with Norton Commander
// referrer: Kilroy was here...
}
}
}
A few useful links on RTMP and AMF:
http://osflash.org/documentation/rtmp
http://osflash.org/_media/rtmp_spec.jpg
http://osflash.org/documentation/amf/astypes
http://osflash.org/documentation/amf/datatypes
http://osflash.org/documentation/amf3
Spread The Word
3 Responses to "Low level AS3 - Establishing an RTMP connection with Socket and ByteArray" 
|
said this on 26 Aug 2007 11:06:47 AM CDT
onConnect
onData pending Error: Error #2030: End at flash.net::Socket/r at a at asc::main/o onData h openConnection |
|
said this on 24 Nov 2007 2:46:46 PM CDT
u need 2 delete
socket.r |
|
said this on 18 Dec 2008 10:11:02 AM CDT
Hi,
I have a ByteArray Thank |



Author/Admin)