librtmp 源码分析笔记 RTMP_ReadPacket

索性一口气把RTMP_ReadPacket也记录下来,好尽快开始对具体上层协议实现细节进行细读。

与RTMP_SendPacket不同,RTMP_ReadPacket需要多次调用(每次读取一个chunk),并配合RTMPPacket_IsReady这个宏使用才能读取一个完整的RTMPPacket。

大致如下:

while(isPlaying && RTMP_ReadPacket(r,&packet)){
  if(RTMPPacket_IsReady(&packet)){
    //已成功接收到一个完整包,在此进行处理
  }
} 

其中,

#define RTMPPacket_IsReady(a)	((a)->m_nBytesRead == (a)->m_nBodySize)


下面开始看一下 RTMP_ReadPacket

int
RTMP_ReadPacket( RTMP *r, RTMPPacket *packet )
{
	uint8_t hbuf[RTMP_MAX_HEADER_SIZE]	= { 0 };
	char	*header				= (char *) hbuf;
	int	nSize, hSize, nToRead, nChunk;
	int	didAlloc = FALSE;

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

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

ReadN也是老朋友了,读取第一个字节,开始对packet流进行解析

	packet->m_headerType	= (hbuf[0] & 0xc0) >> 6;
	packet->m_nChannel	= (hbuf[0] & 0x3f);
	header++;
	if ( packet->m_nChannel == 0 )
	{
		if ( ReadN( r, (char *) &hbuf[1], 1 ) != 1 )
		{
			RTMP_Log( RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte",
				  __FUNCTION__ );
			return(FALSE);
		}
		packet->m_nChannel	= hbuf[1];
		packet->m_nChannel	+= 64;
		header++;
	}else if ( packet->m_nChannel == 1 )
	{
		int tmp;
		if ( ReadN( r, (char *) &hbuf[1], 2 ) != 2 )
		{
			RTMP_Log( RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte",
				  __FUNCTION__ );
			return(FALSE);
		}
		tmp			= (hbuf[2] << 8) + hbuf[1];
		packet->m_nChannel	= tmp + 64;
		RTMP_Log( RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel );
		header += 2;
	}

	nSize = packetSize[packet->m_headerType];

根据headerType获取message header的长度(这里是长度+1,马上又-1了,不知道为何要多次一举),这里面其实就是RTMP_SendPacket中对应部分的相反解码过程,没啥好多说的。

	if ( nSize == RTMP_LARGE_HEADER_SIZE )  /* if we get a full header the timestamp is absolute */
		packet->m_hasAbsTimestamp = TRUE;

	else if ( nSize < RTMP_LARGE_HEADER_SIZE )
	{                                       /* 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) );
	}

如果不是RTMP_LARGE_HEADER_SIZE,那么直接拷贝当前channel上保存的packet的元数据。这里面分两种情况:

1)保存的packet的chunk与此次读取的chunk同属于一个packet

此时packet->m_body指向尚未读取完成的包体的开始地址。

2)保存的packet是已经获取完成的上一个packet。

此时packet->m_body为NULL,表示作为一个新packet,你得自己开辟自己的包体存储空间。

	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 - (char *) hbuf);

将实际的message header读取出来。

	if ( nSize >= 3 )
	{
		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 );
			packet->m_nBytesRead	= 0;
			RTMPPacket_Free( packet );

			if ( nSize > 6 )
			{
				packet->m_packetType = header[6];

				if ( nSize == 11 )
					packet->m_nInfoField2 = DecodeInt32LE( header + 7 );
			}
		}
		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;
		}
	}

读取message header的内容并填充至packet字段中,注意如果时间戳是0xffffff的话,还需要再读四字节绝对时间。这也是规范中规定的。事实上,当接受当前packet的第二个chunk的时候,nSize为0,即不执行这段if,因为在第一个chunk的时候已经解析过了。

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

	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;
	}

终于要开始读包体了。此处如果

packet->m_body == NULL

的话,说明还没有真正的为内容请求内存空间,所以请求之。

	nToRead = packet->m_nBodySize - packet->m_nBytesRead;
	nChunk	= r->m_inChunkSize;
	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;
	}

这段可能是给使用者调试用的,忽略。

	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);
	}

实际的读取。通过m_nBytesRead这个字段来记录每次读取的进度,直至读完m_nBodySize个字节。

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

	packet->m_nBytesRead += nChunk;

	/* keep the packet as ref for other packets on this channel */
	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) );

保存当前packet的元数据,为下一个packet复用做准备。

细想一下,这里面主要的目的不是为下一个packet作复用,而是如果当前pakcet还没有接收完成,那么把当前packet的内容保存在当前channel中,等下次接收到此packet的chunk时,再填充packet。而只有当前packet已经读取完成,目的才是为下一个pakcet做包头复用能少发几个字节做准备。

再细想一下,又觉得packet的信息已经保存在参数中的packet里了,所以此处的信息保存目的还是为了下个packet的复用。

	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    {
		packet->m_body = NULL;                                                          /* so it won't be erased on free */
	}

这段英文注释说的很清楚了,主要是将相对时间转换为绝对时间。另外,将channel保存的读取完成的packet的m_body置为null,也告诉下次函数调用是在接收一个新的packet。

	return(TRUE);
}




你可能感兴趣的:(media,stream)