注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表:
RTMPdump 源代码分析 1: main()函数
RTMPDump(libRTMP)源代码分析 2:解析RTMP地址——RTMP_ParseURL()
RTMPdump(libRTMP) 源代码分析 3: AMF编码
RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)
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中建立一个NetConnection的过程:RTMPdump 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)
多余的话不多说,下面先来看看RTMP_ConnectStream(),该函数主要用于在NetConnection基础上建立一个NetStream。
RTMP_ConnectStream()
//创建流
int
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;
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;
}
//处理Packet!
//----------------
r->dlg->AppendCInfo("建立网络流:处理收到的数据。开始处理收到的数据");
//-----------------------------
RTMP_ClientPacket(r, &packet);
//----------------
r->dlg->AppendCInfo("建立网络流:处理收到的数据。处理完毕,清除数据。");
//-----------------------------
RTMPPacket_Free(&packet);
}
}
return r->m_bPlaying;
}
乍一看,这个函数的代码量好像挺少的,实际上不然,其复杂度还是挺高的。我觉得比RTMP_Connect()要复杂不少。
其关键就在于这个While()循环。首先,循环的三个条件都满足,就能进行循环。只有出错或者建立网络流(NetStream)的步骤完成后,才能跳出循环。
在这个函数中有两个函数尤为重要:
RTMP_ReadPacket()
RTMP_ClientPacket()
第一个函数的作用是读取通过Socket接收下来的消息(Message)包,但是不做任何处理。第二个函数则是处理消息(Message),并做出响应。这两个函数结合,就可以完成接收消息然后响应消息的步骤。
下面来开一下RTMP_ReadPacket():
//读取收下来的Chunk
int
RTMP_ReadPacket(RTMP *r, RTMPPacket *packet)
{
//packet 存读取完后的的数据
//Chunk Header最大值18
uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 };
//header 指向的是从Socket中收下来的数据
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);
//收下来的数据存入hbuf
if (ReadN(r, (char *)hbuf, 1) == 0)
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__);
return FALSE;
}
//块类型fmt
packet->m_headerType = (hbuf[0] & 0xc0) >> 6;
//块流ID(2-63)
packet->m_nChannel = (hbuf[0] & 0x3f);
header++;
//块流ID第1字节为0时,块流ID占2个字节
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;
}
//计算块流ID(64-319)
packet->m_nChannel = hbuf[1];
packet->m_nChannel += 64;
header++;
}
//块流ID第1字节为0时,块流ID占3个字节
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];
//计算块流ID(64-65599)
packet->m_nChannel = tmp + 64;
RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel);
header += 2;
}
//ChunkHeader的大小(4种)
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; //11字节的完整ChunkMsgHeader的TimeStamp是绝对值
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));
}
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)
{
//TimeStamp(注意 BigEndian to SmallEndian)(11,7,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); */
//消息长度(11,7字节首部都有)
if (nSize >= 6)
{
packet->m_nBodySize = AMF_DecodeInt24(header + 3);
packet->m_nBytesRead = 0;
RTMPPacket_Free(packet);
//(11,7字节首部都有)
if (nSize > 6)
{
//Msg type ID
packet->m_packetType = header[6];
//Msg Stream ID
if (nSize == 11)
packet->m_nInfoField2 = DecodeInt32LE(header + 7);
}
}
//Extend TimeStamp
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);
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;
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;
}
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] = (RTMPPacket *) malloc(sizeof(RTMPPacket));
memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket));
//读取完毕
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 */
}
return TRUE;
}
在这里要注意的是,接收下来的实际上是块(Chunk)而不是消息(Message),因为消息(Message)在网络上传播的时候,实际上要分割成块(Chunk)。
这里解析的就是块(Chunk)
可参考:RTMP规范简单分析
具体的解析代码我就不多说了,直接参考RTMP协议规范就可以了,一个字节一个字节的解析就OK了。
rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561
rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163
分享到:
相关推荐
首先,rtmpdump是一个开源命令行工具,用于从RTMP服务器上下载或播放流媒体内容。它支持RTMP、RTMPT、RTMPE、RTMPTE和RTMPS等多种协议变种。rtmpdump2.3是该工具的一个版本,可能包含了一些新特性或修复了某些已知...
对于想要深入了解网络流媒体技术或RTMP协议的开发者,rtmpdump源代码提供了一个很好的学习平台。你可以通过阅读和理解源代码,学习如何处理网络请求、解析复杂协议、实现流媒体的抓取和存储。同时,源代码也适用于...
在压缩包中的"rtmpdump-2.3"可能是rtmpdump的源代码或者编译好的二进制文件,用户可以进一步查看源码理解其工作原理,或者直接在Android设备上使用编译后的二进制文件进行流媒体操作。对于开发者来说,这为自定义...
rtmpdump是基于librtmp库构建的,主要功能包括连接RTMP服务器、发送控制命令、接收和保存流媒体数据。其命令行语法简洁,支持多种参数定制,如指定服务器地址、播放路径、输出文件等。 3. librtmp库: librtmp是...
librtmp 2.2是rtmpdump的一个关键组成部分,提供了对RTMP协议的底层支持。 **RTMP协议** RTMP是一种广泛应用于在线视频流传输的协议,由Adobe Systems开发。它允许内容提供商向用户实时传输音频、视频和其他数据。...
rtmpdump-2.4是一个强大的工具,尤其对需要抓取或回放RTMP流的开发者和爱好者来说。尽管需要手动编译,但其灵活性和实用性使其在Linux环境中具有很高的价值。记得在使用前了解RTMP协议和基本的命令行操作,这将有助...
rtmpdump是一个开源工具,主要用于从RTMP(Real-Time Messaging Protocol)流中捕获和下载媒体内容,如视频和音频。这个压缩包“rtmpdump.zip”包含了在Visual Studio环境中编译rtmpdump项目所需的所有关键文件,...
1. **源代码**:这是rtmpdump-2.3的核心部分,由C或C++编写,包含了实现RTMP协议解析和交互的所有函数和结构。开发者可以查看源码,理解其工作原理,甚至进行自定义修改。 2. **编译好的动态库**:预编译的动态库...
总的来说,rtmpdump是一个强大的工具,它简化了与RTMP服务器交互的过程,特别是在需要抓取和分析流媒体内容时。通过使用你提供的压缩包,用户可以避免编译问题,直接在VC2010环境中使用rtmpdump,这对于开发者和网络...
rtmpdump是一个开源工具,由Alexander Zoller创建,用于从RTMP服务器中抓取、录制或者回放流媒体内容。它可以用于下载在线视频,帮助网络管理员进行故障排查,或者对RTMP服务器进行测试。rtmpdump支持多个RTMP变种,...
rtmpdump是一款强大的开源工具,主要用于从RTMP(Real-Time Messaging Protocol)服务器上抓取流媒体内容。RTMP是一种广泛用于在线视频传输的协议,由Adobe Systems开发。rtmpdump的2.4版本是我们讨论的重点。 这个...
1. **连接到服务器**:rtmpdump首先需要与RTMP服务器建立连接,通过指定服务器地址和应用路径(通常是直播频道的名称)。 2. **获取流信息**:连接成功后,rtmpdump会获取流的元数据,包括视频和音频编码信息、流...
rtmpdump-v1.6.tar.gz是rtmpdump的一个特定版本,通过解压这个tar.gz压缩包,我们可以获取到rtmpdump的源代码和其他相关文件。在这个压缩包中,唯一列出的子文件名是"rtmpdump",这通常指的是rtmpdump的可执行程序...
在提供的压缩包文件`rtmp_send`中,可能包含了一个示例程序,演示了如何使用RTMPDump或其他相关工具进行视频推流。这个程序通常会包含设置RTMP连接、读取FLV数据、处理元数据和实时推流的主要逻辑。你可以通过阅读和...
例如,要记录一个RTMP流,命令可能如下: ``` rtmpdump -r rtmp://server/live/stream -o output.flv ``` 这将把`rtmp://server/live/stream`的流保存到`output.flv`文件中。 **总结** 通过VC6.0编译的RTMPDump....
rtmpdump 2.3 是一个开源工具,专为从RTMP(Real Time Messaging Protocol)流媒体服务器上抓取和下载点播视频文件而设计。它由Alexander Zerbe开发,广泛应用于网络视频内容的下载和分析。本文将详细探讨rtmpdump的...
例如,你可以创建一个RTMPClient类,封装RTMP连接、发布流、播放流等功能,调用librtmp的API来实现: 1. 初始化RTMP连接:使用`RTMP_Alloc()`和`RTMP_Init()`初始化RTMP对象,然后通过`RTMP_SetupURL()`设置服务器...
rtmpdump是一个开源工具,主要用于从RTMP(Real Time Messaging Protocol)流中捕获和下载媒体数据,如视频和音频。这个压缩包“rtmpdump.zip”包含了使用C++编程语言编译通过的rtmpdump工程,适用于Visual Studio ...
rtmpdump 2.4 for vs2017是一个针对Visual Studio 2017编译环境优化的开源工具,主要用于处理RTMP(Real-Time Messaging Protocol)流媒体协议。RTMP是Adobe Systems开发的一种用于在互联网上传输音频、视频和数据的...
开发者可以利用rtmpdump来分析RTMP流量,或者在没有原始流源的情况下记录和播放RTMP内容。此外,rtmpdump也可以用来下载RTMP流,例如从直播网站获取视频内容。 4. RTMP客户端:是指能够连接到RTMP服务器并接收流...