I've been implementing an RTMP client and server library for the past few months and assumed that if I stuck to the official specifications I couldn't go wrong. Unfortunately though the specs are a little lacking in parts. To save anyone else the mistakes I have made, heres a few pointers.
This is a little hard to grasp without re-reading the spec a few times but the basic structure of RTMP is as follows:
RTMP Chunks (small blocks of bytes)
RTMP Messages (bigger blocks of bytes with timestamp, type and "Stream ID")
NetConnection, NetStream (logical objects that accept network commands encoded in AMF0/3 encapsulated in RTMP Messages.)
RTMPT is just RTMP encapsulated in a further layer of HTTP compliant headers that allow messages to get past basic proxy servers.
RTMPS is just RTMPT with SSL slapped on the outside.
The initial RTMP chunk size must be 128 bytes
Get this wrong and you won't get past go.
The initial connect message is in AMF0
They don't really specify this anywhere. If you stick to their specs and pass version 3 in your handshake, you should issue your connect command in AMF0.
You can (probably) ignore the sections on aggregate messages and extended time stamps.
I've never seen either of these come up in a traffic stream. I welcome someone to prove me wrong.
Message Stream ID is NOT in network byte order.
For some reason (maybe someone forget to call ntohl?) the RTMP chunk header fields are all in Big Endian (network byte order) format except for the Message Stream ID field. This is only present in Chunk type 0 packets.
The term "Stream ID" is used to mean different things in different parts of the documentation
In the first part of the spec, a "Chunk Stream ID" refers to the value encoded in the first 1, 2, or 3 bytes of a chunk header. The values 0 and 1 are reserved and the Chunk Stream ID 2 is used for control traffic. In the rest of the spec, Stream ID refers to what I call a "Message Stream ID". This starts from ID 0 for the NetConnection object and higher values are free to be used for NetStream objects as needed.
The "User Control" messages that refer to the Stream ID field are referring to the Message Stream ID field. You can consider the Chunk Stream ID field little more than a way to prioritize your traffic and interleave big fat video blocks with short audio blocks. In general, lower chunk stream ID's get higher priority.
The initial set chunk size, set window size and set peer bandwidth functions should be sent WITHOUT header compression.
This seem to be another silent requirement. I have tried with compression and it seems to fail for some reason. I had to hack a "compressHeader" argument onto my sendChunk function to handle this once-off case.
NetStream commands are sent with MessageStreamID of their NetStream object, not the NetConnection object.
This was said but not very clearly.
Internally, Flash player seems to use the MessageStreamID to route received packets to their appropriate handlers. I mistakenly assumed the ChunkStreamID was used for this purpose but it seems any ChunkStreamID will do whereas the MessageStreamID MUST match the one returned by a "createStream" command on the NetConnection object.
After a createStream command, make sure you send a SetBufferLength User Control command
If you fail to do this, again you're streams won't be streaming at all.
I'm sure theres a whole lot more I have forgotten to write down as I've been working through this. I'll happily add to this if anyone out there can think of something I've forgotten.