Establishing an RTMP connection with Socket and ByteArray

RTMP header:

first byte :

    0×03 in case of a 12 byte length header
    or
    0×43 in case of a 8 byte length header
    or
    0xC3 in case of a 0 byte length header

next three bytes:

    unknown, can be 0×00

next three bytes:

    body length, without inter-chunk headers
    maximum body size is FFFFFF = 16777215 bytes
   
next byte:

    body type
   
    0×14 : invoke with AMF data
  
next four bytes:

    unknown, can be 0×00
   
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:
   
        0×02 followed by the size of the string on two bytes, max length: 65535
   
    null:
   
        0×05
       
    Object
   
        0×03 closed by 0×00 0×00 0×09
        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...                     }             }     }
Share
Posted in FLASH. Tags: , , , , . »