<?xml version="1.0" encoding="utf-8"?>
<chapter id="rtmp">
  <title>RTMP Protocol</title>
  
  <para>
    This document is based mostly on my own reverse engineering of the
    RTMP protocol and AMF format. <emphasis>tcpdump</emphasis> and
    <emphasis>ethereal</emphasis> are your friend. Some additional info that got
    me started was from the <ulink  type="http"
	url="http://www.osflash.org/red5">Red5</ulink>
    project. <emphasis>Red5</emphasis> is the only other open source SWF
    server. So some details are still vague, but as the implementation
    appears to work, we'll figure out what they are later.
  </para>

  <para>
    The Real Time Messaging Protocol was created by MacroMedia (now
    Adobe) for delivering SWF objects and video over a network
    connection. Currently the only servers which support this format
    are the MacroMedia Media sever, and the Open Source Red5 project.
  </para>

  <para>
    This is a simple protocol, optimized for poor bandwidth
    connections. It can support up to 64 concurrent streams over the
    same network connection. Part of each AMF packet header contains
    the index number of the stream. A single RTMP message can contain
    multiple AMF packets.
  </para>

  <para>
    An RTMP connection uses Tcp/ip port 1935. It is also possible to
    tunnel RTMP over an HTTP connection using port 80. Each AMF packet
    is 128 bytes long except for streaming audio, which has 64 byte
    packets.
  </para>

  <para>
    The basics of the RTMP protocol are as follows. All communications
    are initiated by the client.
    <mediaobject>
      <imageobject>
	<imagedata align="center" fileref="images/rtmp.png"/>
      </imageobject>
    </mediaobject>
  </para>

  <para>
    The client starts the RTMP connection by sending a single byte
    with a value of 0x3. This byte is followed by a data block of 1536
    bytes. The format if this data block is unknown, but it appears to
    not be actually used by the protocol except as a handshake.
  </para>

  <para>
    The server receives this packet, stores the 1536 byte data block,
    and then send a single byte with the value of 0x3, followed by two
    1536 data blocks. The second data block is the full contents of
    the original data block as sent by the client.
  </para>

  <para>
    The client receives the 1536 byte data block, and if they match,
    the connection is established. After the handshake process is
    done, there are three other messages that the client sends to the
    sever to start the data flowing.
  </para>

  <para>
    The first AMF packet sent to the server contains the
    <emphasis>connect</emphasis> packet. This doesn't appear to do
    much but notify the server the client is happy with the
    handshake, and ready to start reading packets.
  </para>

  <para>
    The second packet is the <emphasis>NetConnection</emphasis> object from
    the client. This ActionScript class is used by the SWF movie to
    create the network connection to the server.
  </para>

  <para>
    The third packet is the <emphasis>NetStream</emphasis> object from the
    client. This is the ActionScript class used to specify the file to
    be streamed by the server.
  </para>

  <para>
    The RTMP packet for our example looks like this:
   
    <programlisting>
      030000190000c91400000000020007connect00?f0000000000000030003app0200#
      software/gnash/tests/1153948634.flv0008flashVer02000cLNX 6,0,82,0 0006
      swfUrl02001dfile:///file|%2Ftmp%2Fout.swfc30005tcUrl\002\0004
      rtmp://localhost/software/gnash/tests/1153948634.flv\000\000\t
      \002\000\005userx
    </programlisting>
    
    We'll take this apart in a bit, but you can see how all three AMF
    packets are in the same message. The message is received in
    several 128 byte blocks, with the last one being less than
    that. The total size of the RTMP message is in the header, so the
    reader can tell if the entire message was read or not.
  </para>
  
  <para>
    The RTMP header is first, followed by the connect message as an
    ASCII string as the message body. The following AMF packet is the
    <emphasis>NetConnection</emphasis> one, which specifies that this is coming
    from a SWF application. This also contains the file path the server
    can use to find the file to stream. This is then followed by the
    version number, which I assume is the version of the SWF player,
    so the server knows what it is talking to.
  </para>

  <para>
    The third packet is the one from <emphasis>NetStream</emphasis>, which
    specifies the URL used for the movie, followed by the user name
    for a semblance of security.
  </para>

  <para>
    For the next level of detail, we'll explain the format of AMF. AMF
    is used by the RTMP protocol to transfer data. Each SWF object
    is encapsulated in an AMF packet, including streaming audio or
    video.
  </para>

  <para>
    The first byte of the RTMP header determines two things about the
    rest of the message. The first 2 bits of this byte signify the
    total size of the RTMP header. The RTMP header is of a variable
    size, so this is important.

    <variablelist>
      <varlistentry>
	<term>00</term>
	<listitem>
	  <para>
 	    This specifies the header contains 12 bytes, including
	    this one.
	  </para>
	</listitem>
      </varlistentry>
      <varlistentry>
	<term>01</term>
	<listitem>
	  <para>
 	    This specifies the header contains 8 bytes, including this
	    one.
	  </para>
	</listitem>
      </varlistentry>
      <varlistentry>
	<term>02</term>
	<listitem>
	  <para>
 	    This specifies the header contains 4 bytes, including this
	    one.
	  </para>
	</listitem>
      </varlistentry>
      <varlistentry>
	<term>03</term>
	<listitem>
	  <para>
 	    This specifies the header contains 1 byte, so this is the
	    complete header.
	  </para>
	</listitem>
      </varlistentry>
    </variablelist>
  </para>

  <para>
    The other 6 bits in this byte represent the AMF index. As a single
    RTMP connection can support multiple data streams, this signifies
    which stream this packet is for. Once an AMF object is fully
    received by the client, the AMF index may be reused.
  </para>

  <para>
    For messages with headers of at least 4 bytes, the next 3 bytes are
    used by audio and video data packets, but at this time the meaning
    of this field is unknown.
  </para>

  <para>
    For messages with a 8 byte or larger header, the next 3 bytes
    determine the size of the RTMP message being transmitted. Messages
    with a 1 byte or 4 byte header use a standard size, 128 bytes for
    video, and 64 bytes for audio.
  </para>

  <para>
    For messages with an 8 byte or larger header, the next byte is the
    type of the AMF object.
    
    <variablelist>
      <varlistentry>
	<term>0x3</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP packet is the
	    number of bytes read. This is used to start the RTMP
	    connection.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x4</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is a
	    <emphasis>ping</emphasis> packet.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x5</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is
	    server response of some type.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x6</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP packet is
	    client request of some type.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x8</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP packet is an
	    audio message.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x9</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is a
	    video packet.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x12</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is
	    notify. 
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x13</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is
	    shared object.
	  </para>
	</listitem>
      </varlistentry> 
      <varlistentry>
	<term>0x14</term>
	<listitem>
	  <para>
	    This specifies the content type of the RTMP message is
	    remote procedure call. This invokes the method of a SWF
	    class remotely.
	  </para>
	</listitem>
      </varlistentry> 
    </variablelist>     
  
  </para>

  <para>
    There are two sets of data types to consider. One set is used by
    the to specify the content type of the AMF object, the other is an
    ActionScript data type tag used to denote which type of object is
    being transferred.
  </para>
  
  <para>
    The values of the initial type byte are:
    <variablelist>

      <varlistentry>
	<term>0x0</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a numeric
	    value. All numeric values in SWF are 64 bit,
	    <emphasis>big-endian</emphasis>.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x1</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a boolean
	    value.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x2</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is an
	    <emphasis>ASCII</emphasis> string. 
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x3</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a SWF
	    object. The SWF object data type field further along in
	    the message specifies which type of ActionScript object it
	    is.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x4</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a SWF
	    movie, ie. another SWF movie.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x5</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a NULL
	    value. NULL is often used as the return code from calling
	    SWF functions.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x6</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a
	    undefined. This is also used as the return code from
	    calling SWF functions.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x7</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a reference.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x8</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a ECMA
	    array.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x9</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is the end of an
	    object definition. As an object is transmitted with
	    multiple AMF packets, this lets the server know when the
	    end of the object is reached.
	  </para>
	</listitem>
      </varlistentry>


      <varlistentry>
	<term>0xa</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a Strict
	    array.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0xb</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a date.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0xc</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a multi-byte
	    string. Multi-byte strings are used for international
	    language support to represent non <emphasis>ASCII</emphasis>
	    fonts.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0xd</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a an
	    unsupported feature.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0xe</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a record
	    set.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0xf</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a AML
	    object. XML objects are then parsed by the
	    <emphasis>XML</emphasis> ActionScript class.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>0x10</term>
	<listitem>
	  <para>
	    This specifies the data in the AMF packet is a typed object.
	  </para>
	</listitem>
      </varlistentry>

    </variablelist>
    
  </para>

  <para>
    For messages with a 12 byte header, the last 4 bytes are the
    routing of the message. If the destination is the server, this
    value is the NetStream object source. If the destination is the
    client, this is the NetStream object for this RTMP message. A
    value of 0x00000000 appears to be reserved for the NetConnection
    object. 
  </para>

  <para>
    Multiple AMF streams can be contained in a single RTMP messages,
    so it's important to check the index of each AMF packet.
  </para>

  <para>
    An example RTMP header might look like this. (spaces added between
    fields for clarity) All the numbers are in hex.

    <screen>
      03 000019 0000c9 14 000000000
    </screen>
    
    <variablelist>
      <varlistentry>
	<term>03</term>
	<listitem>
	  <para>
	    The first two bits of this byte are the size of the
	    header, which in this example is 00, for a 12 byte
	    header. The next 6 bits is the AMF stream index number,
	    which in this example is 0x3.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>000019</term>
	<listitem>
	  <para>
	    These 3 bytes currently have an unknown purpose.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>000c9</term>
	<listitem>
	  <para>
	    Since this example has a 12 byte header, this is the size
	    of the RTMP message, in this case 201 bytes.
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>14</term>
	<listitem>
	  <para>
	    This is the content type of the RTMP message, which in
	    this case is to invoke a remote function call. (which we
	    later see is the connect function).
	  </para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>00000000</term>
	<listitem>
	  <para>
	    The source is the NetConnection object used to start this
	    connection.
	  </para>
	</listitem>
      </varlistentry>
    </variablelist>

  </para>

  &amf;

</chapter>
  
