RTMPdump source code analysis: RTMP_ConnectStream establishes a network stream connection

------------------------The main role of this article is to record that I read the RTMP source code against the agreement and make a detailed approval ----------- -----------
Note: Just browse the agreement roughly, don’t read it in depth, it’s easier to understand it with the code to read the agreement

protocol

Block

After the handshake, the connection begins to multiplex one or more block streams. Each block stream carries a type of message from a message stream. Each created block is associated with a unique block stream ID. All blocks are transmitted over the network. During transmission, one block must be sent before the next block is sent. At the receiving end, each block is collected into a message based on the block ID.
  Blocking divides the large messages of the high-level protocol into small messages, ensuring that large low-priority messages do not block small high-priority messages.
  Blocking compresses the information originally contained in the message in the block header, reducing the overhead of sending small messages.
  The block size is configurable. This can be done in the block message described in section 7.1. The largest block is 65535 bytes, and the smallest block is 128 bytes. The larger the block, the lower the CPU usage, but it also leads to large writes and other content delays under low bandwidth. The block size remains independent for each direction.
1. Block format
  consists of header and data. The block header consists of three parts:
Insert picture description here
  Basic block header: 1 to 3 bytes
    This field contains the block stream ID and the block type. The block type determines the format of the encoded message header. The length depends on the block stream ID. The block stream ID is a variable length field.
  Block message header: 0, 3, 7 or 11 bytes.
    This field encodes the information of the message to be sent. The length of this field depends on the block type specified in the block header.
  Extended timestamp: 0 or 4 bytes.
    This field must be sent when the normal timestamp (normal timestamp refers to the timestamp in the block message header) is set to 0xffffff, and this field should not be sent when the normal timestamp has other values. value. When the value of the ordinary timestamp is less than 0xffffff, this field does not appear, but the normal timestamp field should be used.
1.1 Block basic header The
  block basic header encodes the block stream ID and block type (indicated by fmt in the figure below). The block type determines the
format of the encoded message header . The basic block header field may be 1, 2 or 3 bytes. It depends on the block stream ID.
  Should an implementation use the least amount of data to represent the ID? .
  This protocol supports 65597 streams with IDs ranging from 3-65599. ID 0, 1, 2 are reserved. 0, means that the ID range is 64-319 (second byte + 64); 1, means that the ID range is 64-65599 (third byte 256 + second


  Insert picture description here

  Insert picture description here
byte + 64); 2 means low-level protocol news. There are no other bytes to represent the stream ID. 3-63 represents the complete stream ID. A value between 3-63 represents the complete stream ID. No other bytes represent the stream ID.   Bits 0-5 (insignificant) represent the block stream ID.   The block stream ID 2-63 can be represented by a 1-byte field. The   block stream ID 64-319 can be represented by 2-byte. ID calculation is (the second byte +64)   block stream ID64-65599 can be expressed as 3 bytes. The ID is calculated as the third byte 255 + the second byte + 64
Insert picture description here
  Cs id: 6 bits
    This field represents the block stream ID in the range of 2-63. Values ​​0 and 1 indicate the 2 or 3 byte version
   Fmt of
    this field : 2 bits     This field identifies the 4 formats of the block message header. The block header for each stream type is shown in the next section.
  Cs id-64: 8-16 bits
    This field contains the value of the block stream ID minus 64. For example, 365 should represent 1 in cs id, and 16 bits here represent 301.
  The block stream ID is in the range of 64-319 and can be represented by a 2-byte version or a 3-byte version.
1.2 Block message header
  There are four formats of block message ID for the selection of the fmt field in the basic header of the block stream.
  An implementation should use the most compact way to represent the block header.
1.2.1 Type 0
  The block length of type 0 is 11 bytes. There must be such a block at the beginning of a block stream and when the time stamp is returned.
Insert picture description here
  Timestamp: 3 bytes
  for a block of type 0. The absolute timestamp of the message is sent here. If the timestamp is greater than or equal to 16777215 (hexadecimal 0x00ffffff), the value must be 16777215, and the extended timestamp must appear. Otherwise, the value is the entire timestamp.
1.2.2. Type 1
   Type 1 blocks are 7 bytes long. The message flow ID is not included in this block. The message flow ID of the block is the same as the previous block. For streams with variable size messages, the first block of each message after the first message should use this format.
Insert picture description here
1.2.3. Type 2
  Type 2 block occupies 3 bytes. Neither the stream ID nor the message length is included. The stream ID and message length used in this block are the same as the previous block. For streams with fixed-size messages, the first block of each message after the first message should use this format.
Insert picture description here
1.2.4 Type 3
  Type 3 blocks have no header. The stream ID, message length, and timestamp do not appear. This type of block uses the same data as the previous block. When a message is divided into multiple blocks, all blocks except the first block should use this type. For examples, please refer to Example 2 in Section 6.2.2. Streams with the same size, stream ID, and time interval should use this block after the type 2 block. For examples, please refer to Example 1 in Section 6.2.1.
  If the time increments of the first message and the second message are the same as the time stamp of the first message, then the type 0 block must be followed by the type 3 block and the type 2 block is not required to register the time increment. If the type 3 block is after the type 0 block, the time stamp increment of type 3 is the same as the time stamp of the type 0 block.
   Timestamp increment: 3 bytes
   For type 1 blocks and type 2 blocks, this field indicates the difference between the timestamp of the previous block and the timestamp of the current block. If the increment is greater than or equal to 1677215 (hexadecimal 0x00ffffff), this value must be 16777215, and the extended timestamp must appear. Otherwise, this value is the entire increment.
  Message length: 3 bytes
  For type 0 or type 1 blocks, this field indicates the length of the message.
Note that this value is usually not the same as the load length. The chunk payload length is the maximum chunk size for all but the last chunk, and the remainder (which may be the entire length, for small messages) for the last chunk.
Message type ID: 1 byte
     for type 0 and type 1 Block, this field sends the message type.
Message flow ID: 4 bytes
    For a block of type 0, this field stores the message flow ID. Usually, the messages in a block flow come from the same message flow. Although, because different messages may be multiplexed into a block stream, header compression cannot be effectively implemented. However, if one message flow is closed and another message flow is opened, it is not impossible to reuse an existing block flow by sending a new block of type 0.
1.3. Extended timestamp
  This field is only transmitted when the normal timestamp in the block message header is set to 0x00ffffff. If the value of the ordinary timestamp is less than 0x00ffffff, then this field must not appear. If the timestamp field does not appear, this field must not appear. Type 3 blocks must not contain this field. This field is after the block message header and before the block time.

Involving structure analysis

  typedef struct RTMPPacket
  {
    uint8_t m_headerType;	/* zx块类型,块类型决定了消息头大小 */
    uint8_t m_packetType;	/* zx消息类型 */
    uint8_t m_hasAbsTimestamp;	/* zx True表示绝对时间 */	
    int m_nChannel;			/* zx块流id */
    uint32_t m_nTimeStamp;	/* timestamp */
    int32_t m_nInfoField2;	/* last 4 bytes in a long header消息流id */
    uint32_t m_nBodySize;	/* zx 消息长度 */
    uint32_t m_nBytesRead;	/* zx 表示已读字节数 */
    RTMPChunk *m_chunk;		
    char *m_body;			/* zx 申请内存存放消息内容,头数据18字节之后的地址 */
  } RTMPPacket;

Source code

Realization of RTMP_ConnectStream: RTMP_ReadPacket reads all the block data in a message cyclically. After RTMPPacket_IsReady judges that the reading is complete, it calls RTMP_ClientPacket to parse the data.

bool RTMP_ConnectStream(RTMP *r, int seekTime)
{
	RTMPPacket packet = { 0 };

	/* seekTime was already set by SetupStream / SetupURL.
	* This is only needed by ReconnectStream.
	*/
	if (seekTime > 0)
		r->Link.seekTime = seekTime;

	r->m_mediaChannel = 0;

	/* zx RTMP_ReadPacket读取socket传过来的数据但是不做任何处理 */
	while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet))
	{
		if (RTMPPacket_IsReady(&packet))
		{
			if (!packet.m_nBodySize)
				continue;
			if ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) ||
					(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) ||
						(packet.m_packetType == RTMP_PACKET_TYPE_INFO))
			{
				RTMP_Log(RTMP_LOGWARNING, "Received FLV packet before play()! Ignoring.");
				RTMPPacket_Free(&packet);
				continue;
			}

			/* zx开始处理接收到的数据,并作出响应 */
			RTMP_ClientPacket(r, &packet);
			RTMPPacket_Free(&packet);		/* zx处理完之后释放数据 */
		}
	}

	return r->m_bPlaying;
}

RTMP_ReadPacket:


/* zx 函数作用:读取接收下来的chunk
 * 参数packet:用来保存读到的数据
 */
bool RTMP_ReadPacket(RTMP *r, RTMPPacket *packet)
{
	char hbuf[RTMP_MAX_HEADER_SIZE] = { 0 }, *header = hbuf;
	int nSize, hSize, nToRead, nChunk;
	bool didAlloc = false;

	RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket);

	if (ReadN(r, hbuf, 1) == 0)
	{
		RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__);
		return false;
	}

	packet->m_headerType = (hbuf[0] & 0xc0) >> 6;	/* zx 块类型 */
	packet->m_nChannel = (hbuf[0] & 0x3f);			/* zx 块流id */
	header++;

	/* zx 下面根据协议计算块流id */
	if (packet->m_nChannel == 0)	/* zx 等于0是一个字节 */
	{
		if (ReadN(r, &hbuf[1], 1) != 1)
		{
			RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte", __FUNCTION__);
			return false;
		}
		packet->m_nChannel = (unsigned)hbuf[1];
		packet->m_nChannel += 64;
		header++;
	}
	else if (packet->m_nChannel == 1)
	{
		int tmp;
		if (ReadN(r, &hbuf[1], 2) != 2)
		{
			RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte", __FUNCTION__);
			return false;
		}
		tmp = (((unsigned)hbuf[2]) << 8) + (unsigned)hbuf[1];
		packet->m_nChannel = tmp + 64;
		RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel);
		header += 2;
	}

	/* zx 根据块类型得到后面块消息长度 */
	nSize = packetSize[packet->m_headerType];

	if (nSize == RTMP_LARGE_HEADER_SIZE)	/* if we get a full header the timestamp is absolute */
	{
		packet->m_hasAbsTimestamp = true;	/* zx 11字节的完整块,时间戳是绝对时间,3和7都是相对于之前的时间的 */
	}		
	else if (nSize < RTMP_LARGE_HEADER_SIZE) /* zx 小于12表示不是消息的第一个块,使用上一个块的消息头,只修改差异部分 */
	{				/* using values from the last message of this channel */
		if (r->m_vecChannelsIn[packet->m_nChannel])
			memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], sizeof(RTMPPacket));
	}

	nSize--;

	if (nSize > 0 && ReadN(r, header, nSize) != nSize)
	{
		RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header. type: %x", __FUNCTION__, (unsigned int)hbuf[0]);
		return false;
	}

	hSize = nSize + (header - hbuf);

	if (nSize >= 3)
	{
		/* zx 无论消息头占几个字节,前三个字节都是时间戳,类型0是绝对时间戳,类型1和2是相对于上一次的时间戳 */
		packet->m_nTimeStamp = AMF_DecodeInt24(header);

		/*RTMP_Log(RTMP_LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); */

		if (nSize >= 6)
		{
			packet->m_nBodySize = AMF_DecodeInt24(header + 3);	/* zx 消息长度 */
			packet->m_nBytesRead = 0;
			RTMPPacket_Free(packet);

			if (nSize > 6)
			{
				packet->m_packetType = header[6];				/* zx 此消息的消息类型 */

				if (nSize == 11)
					packet->m_nInfoField2 = DecodeInt32LE(header + 7);	/* zx 消息流id */
			}
		}
		/* zx 扩展时间戳,只要当块消息头中的普通时间戳为0xffffff时,才会有扩展时间戳 */
		if (packet->m_nTimeStamp == 0xffffff)
		{
			if (ReadN(r, header + nSize, 4) != 4)
			{
				RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", __FUNCTION__);
				return false;
			}
			packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize);
			hSize += 4;
		}
	}

	RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize);

	/* zx 消息长度大于0时,申请内存存放消息内容+18字节最大头数据 */
	if (packet->m_nBodySize > 0 && packet->m_body == NULL)
	{
		if (!RTMPPacket_Alloc(packet, packet->m_nBodySize))
		{
			RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
			return false;
		}
		didAlloc = true;
		packet->m_headerType = (hbuf[0] & 0xc0) >> 6;
	}

	nToRead = packet->m_nBodySize - packet->m_nBytesRead;
	nChunk = r->m_inChunkSize;		/* zx 这个是默认值128 */
	if (nToRead < nChunk)
		nChunk = nToRead;

	/* Does the caller want the raw chunk? */
	if (packet->m_chunk)
	{
		packet->m_chunk->c_headerSize = hSize;
		memcpy(packet->m_chunk->c_header, hbuf, hSize);
		packet->m_chunk->c_chunk = packet->m_body + packet->m_nBytesRead;
		packet->m_chunk->c_chunkSize = nChunk;
	}

	/* zx 累加的往后读取数据块内容,packet->m_nBytesRead第一次为0 */
	if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk)
	{
		RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %lu", __FUNCTION__, packet->m_nBodySize);
		return false;
	}

	RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)packet->m_body + packet->m_nBytesRead, nChunk);

	packet->m_nBytesRead += nChunk;

	/* zx 保存packet,供其他作用在这个通道的packet使用 */
	if (!r->m_vecChannelsIn[packet->m_nChannel])
		r->m_vecChannelsIn[packet->m_nChannel] = malloc(sizeof(RTMPPacket));
	memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket));

	/* zx 判断是否读取完成,读取完成之后,读取完的字节数packet->m_nBytesR等于块消息头中读取到的字节数 */
	if (RTMPPacket_IsReady(packet))
	{
		/* make packet's timestamp absolute */
		if (!packet->m_hasAbsTimestamp)
			packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel];	/* timestamps seem to be always relative!! */

		r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp;

		/* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
		/* arrives and requests to re-use some info (small packet header) */
		r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL;
		r->m_vecChannelsIn[packet->m_nChannel]->m_nBytesRead = 0;
		r->m_vecChannelsIn[packet->m_nChannel]->m_hasAbsTimestamp = false;	/* can only be false if we reuse header */
	}
	else
	{
		/* zx 没读完不进行拷贝,下一次进来之后会memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], sizeof(RTMPPacket)); */
		packet->m_body = NULL;	/* so it won't be erased on free */
	}

	return true;
	}

Guess you like

Origin blog.csdn.net/weixin_37921201/article/details/90679009