=====================================================
RTMPdump(libRTMP) 源代码分析系列文章:
RTMPdump 源代码分析 1: main()函数
RTMPDump (libRTMP) 源代码分析2:解析RTMP地址——RTMP_ParseURL()
RTMPdump (libRTMP) 源代码分析3: AMF编码
RTMPdump (libRTMP) 源代码分析4: 连接第一步——握手 (HandShake)
RTMPdump (libRTMP) 源代码分析5: 建立一个流媒体连接 (NetConnection部分)
RTMPdump (libRTMP) 源代码分析6: 建立一个流媒体连接 (NetStream部分 1)
RTMPdump (libRTMP) 源代码分析7: 建立一个流媒体连接 (NetStream部分 2)
RTMPdump (libRTMP) 源代码分析8: 发送消息 (Message)
RTMPdump (libRTMP) 源代码分析9: 接收消息 (Message) (接收视音频数据)
RTMPdump (libRTMP) 源代码分析10: 处理各种消息 (Message)
=====================================================
函数调用结构图
RTMPDump (libRTMP)的整体的函数调用结构图如下图所示。
单击查看大图
详细分析
之前写了一系列的文章介绍RTMPDump各种函数。比如怎么建立网络连接(NetConnection),怎么建立网络流(NetStream)之类的,唯独没有介绍这些发送或接收的数据,在底层到底是怎么实现的。本文就是要剖析一下其内部的实现。即这些消息(Message)到底是怎么发送和接收的。
先来看看发送消息吧。
- 发送connect命令使用函数SendConnectPacket()
- 发送createstream命令使用RTMP_SendCreateStream()
- 发送realeaseStream命令使用SendReleaseStream()
- 发送publish命令使用SendPublish()
- 发送deleteStream的命令使用SendDeleteStream()
- 发送pause命令使用RTMP_SendPause()
不再一一例举,发现函数命名有两种规律:RTMP_Send***()或者Send***(),其中*号代表命令的名称。
SendConnectPacket()这个命令是每次程序开始运行的时候发送的第一个命令消息,内容比较多,包含了很多AMF编码的内容,在此不多做分析,贴上代码:
-
- static int
- SendConnectPacket(RTMP *r, RTMPPacket *cp)
- {
- RTMPPacket packet;
- char pbuf[4096], *pend = pbuf + sizeof(pbuf);
- char *enc;
-
- if (cp)
- return RTMP_SendPacket(r, cp, TRUE);
-
- packet.m_nChannel = 0x03;
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_packetType = 0x14;
- packet.m_nTimeStamp = 0;
- packet.m_nInfoField2 = 0;
- packet.m_hasAbsTimestamp = 0;
- packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
-
- enc = packet.m_body;
- enc = AMF_EncodeString(enc, pend, &av_connect);
- enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
- *enc++ = AMF_OBJECT;
-
- enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app);
- if (!enc)
- return FALSE;
- if (r->Link.protocol & RTMP_FEATURE_WRITE)
- {
- enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate);
- if (!enc)
- return FALSE;
- }
- if (r->Link.flashVer.av_len)
- {
- enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer);
- if (!enc)
- return FALSE;
- }
- if (r->Link.swfUrl.av_len)
- {
- enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl);
- if (!enc)
- return FALSE;
- }
- if (r->Link.tcUrl.av_len)
- {
- enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl);
- if (!enc)
- return FALSE;
- }
- if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
- {
- enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);
- if (!enc)
- return FALSE;
- enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);
- if (!enc)
- return FALSE;
- enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);
- if (!enc)
- return FALSE;
- enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs);
- if (!enc)
- return FALSE;
- enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0);
- if (!enc)
- return FALSE;
- if (r->Link.pageUrl.av_len)
- {
- enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl);
- if (!enc)
- return FALSE;
- }
- }
- if (r->m_fEncoding != 0.0 || r->m_bSendEncoding)
- {
- enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
- if (!enc)
- return FALSE;
- }
- if (enc + 3 >= pend)
- return FALSE;
- *enc++ = 0;
- *enc++ = 0;
- *enc++ = AMF_OBJECT_END;
-
-
- if (r->Link.auth.av_len)
- {
- enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH);
- if (!enc)
- return FALSE;
- enc = AMF_EncodeString(enc, pend, &r->Link.auth);
- if (!enc)
- return FALSE;
- }
- if (r->Link.extras.o_num)
- {
- int i;
- for (i = 0; i < r->Link.extras.o_num; i++)
- {
- enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend);
- if (!enc)
- return FALSE;
- }
- }
- packet.m_nBodySize = enc - packet.m_body;
-
- r->dlg->AppendMLInfo(20,1,"命令消息","Connect");
-
- return RTMP_SendPacket(r, &packet, TRUE);
- }
RTMP_SendCreateStream()命令相对而言比较简单,代码如下:-
- int
- RTMP_SendCreateStream(RTMP *r)
- {
- RTMPPacket packet;
- char pbuf[256], *pend = pbuf + sizeof(pbuf);
- char *enc;
-
- packet.m_nChannel = 0x03;
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14;
- packet.m_nTimeStamp = 0;
- packet.m_nInfoField2 = 0;
- packet.m_hasAbsTimestamp = 0;
- packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
-
- enc = packet.m_body;
- enc = AMF_EncodeString(enc, pend, &av_createStream);
- enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
- *enc++ = AMF_NULL;
-
- packet.m_nBodySize = enc - packet.m_body;
-
- r->dlg->AppendMLInfo(20,1,"命令消息","CreateStream");
-
- return RTMP_SendPacket(r, &packet, TRUE);
- }
同样,SendReleaseStream()内容也比较简单,我对其中部分内容作了注释:-
- static int
- SendReleaseStream(RTMP *r)
- {
- RTMPPacket packet;
- char pbuf[1024], *pend = pbuf + sizeof(pbuf);
- char *enc;
-
- packet.m_nChannel = 0x03;
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = 0x14;
- packet.m_nTimeStamp = 0;
- packet.m_nInfoField2 = 0;
- packet.m_hasAbsTimestamp = 0;
- packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
-
- enc = packet.m_body;
-
- enc = AMF_EncodeString(enc, pend, &av_releaseStream);
-
- enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
-
- *enc++ = AMF_NULL;
-
- enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
- if (!enc)
- return FALSE;
-
- packet.m_nBodySize = enc - packet.m_body;
-
- r->dlg->AppendMLInfo(20,1,"命令消息","ReleaseStream");
-
- return RTMP_SendPacket(r, &packet, FALSE);
- }
再来看一个SendPublish()函数,用于发送“publish”命令-
- static int
- SendPublish(RTMP *r)
- {
- RTMPPacket packet;
- char pbuf[1024], *pend = pbuf + sizeof(pbuf);
- char *enc;
-
- packet.m_nChannel = 0x04;
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-
- packet.m_packetType = 0x14;
- packet.m_nTimeStamp = 0;
-
- packet.m_nInfoField2 = r->m_stream_id;
- packet.m_hasAbsTimestamp = 0;
- packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
-
- enc = packet.m_body;
-
- enc = AMF_EncodeString(enc, pend, &av_publish);
- enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
-
- *enc++ = AMF_NULL;
- enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
- if (!enc)
- return FALSE;
-
-
- enc = AMF_EncodeString(enc, pend, &av_live);
- if (!enc)
- return FALSE;
-
- packet.m_nBodySize = enc - packet.m_body;
-
- r->dlg->AppendMLInfo(20,1,"命令消息","Pulish");
-
- return RTMP_SendPacket(r, &packet, TRUE);
- }
其他的命令不再一一例举,总体的思路是声明一个RTMPPacket类型的结构体,然后设置各种属性值,最后交给RTMP_SendPacket()进行发送。RTMPPacket类型的结构体定义如下,一个RTMPPacket对应RTMP协议规范里面的一个块(Chunk)。
-
- typedef struct RTMPPacket
- {
- uint8_t m_headerType;
- uint8_t m_packetType;
- uint8_t m_hasAbsTimestamp;
- int m_nChannel;
- uint32_t m_nTimeStamp;
- int32_t m_nInfoField2;
- uint32_t m_nBodySize;
- uint32_t m_nBytesRead;
- RTMPChunk *m_chunk;
- char *m_body;
- } RTMPPacket;
下面我们来看看RTMP_SendPacket()吧,各种的RTMPPacket(即各种Chunk)都需要用这个函数进行发送。-
-
- int
- RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue)
- {
- const RTMPPacket *prevPacket = r->m_vecChannelsOut[packet->m_nChannel];
- uint32_t last = 0;
- int nSize;
- int hSize, cSize;
- char *header, *hptr, *hend, hbuf[RTMP_MAX_HEADER_SIZE], c;
- uint32_t t;
- char *buffer, *tbuf = NULL, *toff = NULL;
- int nChunkSize;
- int tlen;
-
- if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE)
- {
-
-
-
- if (prevPacket->m_nBodySize == packet->m_nBodySize
- && prevPacket->m_packetType == packet->m_packetType
- && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM)
- packet->m_headerType = RTMP_PACKET_SIZE_SMALL;
-
-
- if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp
- && packet->m_headerType == RTMP_PACKET_SIZE_SMALL)
- packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM;
-
- last = prevPacket->m_nTimeStamp;
- }
-
- if (packet->m_headerType > 3)
- {
- RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.",
- (unsigned char)packet->m_headerType);
- return FALSE;
- }
-
- nSize = packetSize[packet->m_headerType];
- hSize = nSize; cSize = 0;
-
- t = packet->m_nTimeStamp - last;
-
- if (packet->m_body)
- {
-
-
- header = packet->m_body - nSize;
-
- hend = packet->m_body;
- }
- else
- {
- header = hbuf + 6;
- hend = hbuf + sizeof(hbuf);
- }
-
- if (packet->m_nChannel > 319)
-
- cSize = 2;
-
- else if (packet->m_nChannel > 63)
-
- cSize = 1;
- if (cSize)
- {
-
- header -= cSize;
-
- hSize += cSize;
- }
-
- if (nSize > 1 && t >= 0xffffff)
- {
- header -= 4;
- hSize += 4;
- }
-
- hptr = header;
-
- c = packet->m_headerType << 6;
- switch (cSize)
- {
-
- case 0:
- c |= packet->m_nChannel;
- break;
-
- case 1:
- break;
-
- case 2:
- c |= 1;
- break;
- }
-
- *hptr++ = c;
-
- if (cSize)
- {
-
- int tmp = packet->m_nChannel - 64;
-
- *hptr++ = tmp & 0xff;
-
- if (cSize == 2)
-
- *hptr++ = tmp >> 8;
- }
-
-
- if (nSize > 1)
- {
-
- hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t);
- }
-
- if (nSize > 4)
- {
-
- hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize);
-
- *hptr++ = packet->m_packetType;
- }
-
- if (nSize > 8)
- hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);
-
-
- if (nSize > 1 && t >= 0xffffff)
- hptr = AMF_EncodeInt32(hptr, hend, t);
-
- nSize = packet->m_nBodySize;
- buffer = packet->m_body;
-
- nChunkSize = r->m_outChunkSize;
-
- RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket,
- nSize);
-
-
- if (r->Link.protocol & RTMP_FEATURE_HTTP)
- {
-
-
-
-
- int chunks = (nSize+nChunkSize-1) / nChunkSize;
-
- if (chunks > 1)
- {
-
-
-
-
- tlen = chunks * (cSize + 1) + nSize + hSize;
-
- tbuf = (char *) malloc(tlen);
- if (!tbuf)
- return FALSE;
- toff = tbuf;
- }
-
- }
- while (nSize + hSize)
- {
- int wrote;
-
- if (nSize < nChunkSize)
-
- nChunkSize = nSize;
-
- RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)header, hSize);
- RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)buffer, nChunkSize);
- if (tbuf)
- {
-
-
- memcpy(toff, header, nChunkSize + hSize);
- toff += nChunkSize + hSize;
- }
- else
- {
- wrote = WriteN(r, header, nChunkSize + hSize);
- if (!wrote)
- return FALSE;
- }
-
- nSize -= nChunkSize;
-
- buffer += nChunkSize;
- hSize = 0;
-
-
- if (nSize > 0)
- {
-
- header = buffer - 1;
- hSize = 1;
- if (cSize)
- {
- header -= cSize;
- hSize += cSize;
- }
-
- *header = (0xc0 | c);
-
- if (cSize)
- {
- int tmp = packet->m_nChannel - 64;
- header[1] = tmp & 0xff;
- if (cSize == 2)
- header[2] = tmp >> 8;
- }
- }
- }
- if (tbuf)
- {
-
- int wrote = WriteN(r, tbuf, toff-tbuf);
- free(tbuf);
- tbuf = NULL;
- if (!wrote)
- return FALSE;
- }
-
-
- if (packet->m_packetType == 0x14)
- {
- AVal method;
- char *ptr;
- ptr = packet->m_body + 1;
- AMF_DecodeString(ptr, &method);
- RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val);
-
- if (queue) {
- int txn;
- ptr += 3 + method.av_len;
- txn = (int)AMF_DecodeNumber(ptr);
- AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn);
- }
- }
-
- if (!r->m_vecChannelsOut[packet->m_nChannel])
- r->m_vecChannelsOut[packet->m_nChannel] = (RTMPPacket *) malloc(sizeof(RTMPPacket));
- memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket));
- return TRUE;
- }
这个函数乍一看好像非常复杂,其实不然,他只是按照RTMP规范将数据编码成符合规范的块(Chunk),规范可以参考相关的文档。具体怎么编码成块(Chunk)就不多分析了,在这里需要注意一个函数:WriteN()。该函数完成了将数据发送出去的功能。
来看一下WriteN()函数:
-
- static int
- WriteN(RTMP *r, const char *buffer, int n)
- {
- const char *ptr = buffer;
- #ifdef CRYPTO
- char *encrypted = 0;
- char buf[RTMP_BUFFER_CACHE_SIZE];
-
- if (r->Link.rc4keyOut)
- {
- if (n > sizeof(buf))
- encrypted = (char *)malloc(n);
- else
- encrypted = (char *)buf;
- ptr = encrypted;
- RC4_encrypt2((RC4_KEY *)r->Link.rc4keyOut, n, buffer, ptr);
- }
- #endif
-
- while (n > 0)
- {
- int nBytes;
-
-
- if (r->Link.protocol & RTMP_FEATURE_HTTP)
- nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n);
- else
- nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n);
-
-
- if (nBytes < 0)
- {
- int sockerr = GetSockError();
- RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
- sockerr, n);
-
- if (sockerr == EINTR && !RTMP_ctrlC)
- continue;
-
- RTMP_Close(r);
- n = 1;
- break;
- }
-
- if (nBytes == 0)
- break;
-
- n -= nBytes;
- ptr += nBytes;
- }
-
- #ifdef CRYPTO
- if (encrypted && encrypted != buf)
- free(encrypted);
- #endif
-
- return n == 0;
- }
该函数中,RTMPSockBuf_Send()完成了数据发送的功能,再来看看这个函数(函数调用真是好多啊。。。。)-
-
- int
- RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len)
- {
- int rc;
-
- #ifdef _DEBUG
- fwrite(buf, 1, len, netstackdump);
- #endif
-
- #if defined(CRYPTO) && !defined(NO_SSL)
- if (sb->sb_ssl)
- {
- rc = TLS_write((SSL *)sb->sb_ssl, buf, len);
- }
- else
- #endif
- {
-
-
-
-
-
-
-
- rc = send(sb->sb_socket, buf, len, 0);
- }
- return rc;
- }
-
- int
- RTMPSockBuf_Close(RTMPSockBuf *sb)
- {
- #if defined(CRYPTO) && !defined(NO_SSL)
- if (sb->sb_ssl)
- {
- TLS_shutdown((SSL *)sb->sb_ssl);
- TLS_close((SSL *)sb->sb_ssl);
- sb->sb_ssl = NULL;
- }
- #endif
- return closesocket(sb->sb_socket);
- }
到这个函数的时候,发现一层层的调用终于完成了,最后调用了系统Socket的send()函数完成了数据的发送功能。之前贴过一张图总结这个过程,可能理解起来要方便一些:RTMPDump源代码分析 0: 主要函数调用分析
rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561
rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163