索性一口气把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);
}