librtmpソース分析ノートRTMP_ReadPacket
いっそ一気にRTMP_をReadPacketも記録して、できるだけ早く具体的な上層プロトコルの実現の詳細を詳しく読むようにします.
とRTMP_SendPacketが異なり、RTMP_ReadPacketは複数回の呼び出し(chunkを1回読み出すたびに)を必要とし、RTMPPacket_と協力します.IsReadyというマクロは、完全なRTMPPacketを読み込むために使用されます.
大体以下の通りです.
ここで、
次はRTMP_を見てみましょうReadPacket
ReadNも古い友人で、最初のバイトを読み出してpacketストリームの解析を開始しました
headerTypeによってmessage headerの長さを取得(ここでは長さ+1、すぐにまた-1、なぜ何度も一挙にやるのか分からない)、この中が実はRTMP_SendPacketの対応部分の逆復号過程は,あまり話されていない.
RTMPでなければLARGE_HEADER_SIZEは、現在のチャンネルに保存されているpacketのメタデータを直接コピーします.この中には2つの状況があります.
1)保存したpacketのchunkは今回読み取ったchunkと同じpacketに属する
このときpacket->m_bodyは、まだ読み取りが完了していないパケットの開始アドレスを指す.
2)保存したpacketは取得済みの前のpacketである.
このときpacket->m_bodyはNULLで、新しいpacketとして、自分で自分のパッケージストレージ空間を開かなければならないことを示しています.
実際のメッセージヘッダを読み出します.
メッセージヘッダの内容を読み込んでpacketフィールドに入力します.タイムスタンプが0 xffffの場合、さらに4バイトの絶対時間を読む必要があります.これも規範に規定されています.実際、現在のpacketの2番目のchunkを受け入れると、nSizeは0であり、すなわち、最初のchunkのときに解析されたため、このifは実行されない.
いよいよパッケージ読み始めます.ここで
packet->m_body == NULL
の場合、コンテンツにメモリ領域が要求されていないため、要求されています.
どのバイトのパッケージを読み込むかを決定します.
このセクションは、ユーザーにデバッグするために使用される可能性があります.無視します.
実際の読み取り.m_経由nBytesReadこのフィールドは、m_を読み終わるまで、読み取りの進捗状況を記録します.nBodySizeバイト.
現在のpacketのメタデータを保存し、次のpacket多重化の準備をします.
よく考えてみると、この主な目的は次のpacketを多重化することではなく、現在のpakcetがまだ受信が完了していない場合は、現在のpacketの内容を現在のchannelに保存し、次にこのpacketのchunkを受信したときにpacketを埋め込むことです.現在のpacketのみが読み取り済みで、次のpakcetのパケットヘッダ多重化に数バイトを少なくする準備が目的です.
もう少し考えてみると、packetの情報はパラメータのpacketに保存されているような気がするので、ここの情報保存の目的はやはり次のpacketの多重化のためです.
この英語の注釈ははっきりしていて、主に相対時間を絶対時間に変換します.また、channelを保存した読み取りが完了したpacketのm_bodyはnullに設定され、次の関数呼び出しは新しいpacketを受信していることを示します.
とRTMP_SendPacketが異なり、RTMP_ReadPacketは複数回の呼び出し(chunkを1回読み出すたびに)を必要とし、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は、現在のチャンネルに保存されているpacketのメタデータを直接コピーします.この中には2つの状況があります.
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);
実際のメッセージヘッダを読み出します.
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;
}
}
メッセージヘッダの内容を読み込んでpacketフィールドに入力します.タイムスタンプが0 xffffの場合、さらに4バイトの絶対時間を読む必要があります.これも規範に規定されています.実際、現在のpacketの2番目のchunkを受け入れると、nSizeは0であり、すなわち、最初のchunkのときに解析されたため、このifは実行されない.
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);
}