=====================================================

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编码的内容,在此不多做分析,贴上代码:

[cpp] view plaincopy
  1. //发送“connect”命令
  2. static int
  3. SendConnectPacket(RTMP *r, RTMPPacket *cp)
  4. {
  5. RTMPPacket packet;
  6. char pbuf[4096], *pend = pbuf + sizeof(pbuf);
  7. char *enc;
  8. if (cp)
  9. return RTMP_SendPacket(r, cp, TRUE);
  10. packet.m_nChannel = 0x03; /* control channel (invoke) */
  11. packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
  12. packet.m_packetType = 0x14;   /* INVOKE */
  13. packet.m_nTimeStamp = 0;
  14. packet.m_nInfoField2 = 0;
  15. packet.m_hasAbsTimestamp = 0;
  16. packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  17. enc = packet.m_body;
  18. enc = AMF_EncodeString(enc, pend, &av_connect);
  19. enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  20. *enc++ = AMF_OBJECT;
  21. enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app);
  22. if (!enc)
  23. return FALSE;
  24. if (r->Link.protocol & RTMP_FEATURE_WRITE)
  25. {
  26. enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate);
  27. if (!enc)
  28. return FALSE;
  29. }
  30. if (r->Link.flashVer.av_len)
  31. {
  32. enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer);
  33. if (!enc)
  34. return FALSE;
  35. }
  36. if (r->Link.swfUrl.av_len)
  37. {
  38. enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl);
  39. if (!enc)
  40. return FALSE;
  41. }
  42. if (r->Link.tcUrl.av_len)
  43. {
  44. enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl);
  45. if (!enc)
  46. return FALSE;
  47. }
  48. if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
  49. {
  50. enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);
  51. if (!enc)
  52. return FALSE;
  53. enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);
  54. if (!enc)
  55. return FALSE;
  56. enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);
  57. if (!enc)
  58. return FALSE;
  59. enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs);
  60. if (!enc)
  61. return FALSE;
  62. enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0);
  63. if (!enc)
  64. return FALSE;
  65. if (r->Link.pageUrl.av_len)
  66. {
  67. enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl);
  68. if (!enc)
  69. return FALSE;
  70. }
  71. }
  72. if (r->m_fEncoding != 0.0 || r->m_bSendEncoding)
  73. {   /* AMF0, AMF3 not fully supported yet */
  74. enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
  75. if (!enc)
  76. return FALSE;
  77. }
  78. if (enc + 3 >= pend)
  79. return FALSE;
  80. *enc++ = 0;
  81. *enc++ = 0;           /* end of object - 0x00 0x00 0x09 */
  82. *enc++ = AMF_OBJECT_END;
  83. /* add auth string */
  84. if (r->Link.auth.av_len)
  85. {
  86. enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH);
  87. if (!enc)
  88. return FALSE;
  89. enc = AMF_EncodeString(enc, pend, &r->Link.auth);
  90. if (!enc)
  91. return FALSE;
  92. }
  93. if (r->Link.extras.o_num)
  94. {
  95. int i;
  96. for (i = 0; i < r->Link.extras.o_num; i++)
  97. {
  98. enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend);
  99. if (!enc)
  100. return FALSE;
  101. }
  102. }
  103. packet.m_nBodySize = enc - packet.m_body;
  104. //----------------
  105. r->dlg->AppendMLInfo(20,1,"命令消息","Connect");
  106. //-----------------------------
  107. return RTMP_SendPacket(r, &packet, TRUE);
  108. }

RTMP_SendCreateStream()命令相对而言比较简单,代码如下:

[cpp] view plaincopy
  1. //发送“createstream”命令
  2. int
  3. RTMP_SendCreateStream(RTMP *r)
  4. {
  5. RTMPPacket packet;
  6. char pbuf[256], *pend = pbuf + sizeof(pbuf);
  7. char *enc;
  8. packet.m_nChannel = 0x03; /* control channel (invoke) */
  9. packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
  10. packet.m_packetType = 0x14;   /* INVOKE */
  11. packet.m_nTimeStamp = 0;
  12. packet.m_nInfoField2 = 0;
  13. packet.m_hasAbsTimestamp = 0;
  14. packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  15. enc = packet.m_body;
  16. enc = AMF_EncodeString(enc, pend, &av_createStream);
  17. enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  18. *enc++ = AMF_NULL;        /* NULL */
  19. packet.m_nBodySize = enc - packet.m_body;
  20. //----------------
  21. r->dlg->AppendMLInfo(20,1,"命令消息","CreateStream");
  22. //-----------------------------
  23. return RTMP_SendPacket(r, &packet, TRUE);
  24. }

同样,SendReleaseStream()内容也比较简单,我对其中部分内容作了注释:

[cpp] view plaincopy
  1. //发送RealeaseStream命令
  2. static int
  3. SendReleaseStream(RTMP *r)
  4. {
  5. RTMPPacket packet;
  6. char pbuf[1024], *pend = pbuf + sizeof(pbuf);
  7. char *enc;
  8. packet.m_nChannel = 0x03; /* control channel (invoke) */
  9. packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
  10. packet.m_packetType = 0x14;   /* INVOKE */
  11. packet.m_nTimeStamp = 0;
  12. packet.m_nInfoField2 = 0;
  13. packet.m_hasAbsTimestamp = 0;
  14. packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  15. enc = packet.m_body;
  16. //对“releaseStream”字符串进行AMF编码
  17. enc = AMF_EncodeString(enc, pend, &av_releaseStream);
  18. //对传输ID(0)进行AMF编码?
  19. enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  20. //命令对象
  21. *enc++ = AMF_NULL;
  22. //对播放路径字符串进行AMF编码
  23. enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
  24. if (!enc)
  25. return FALSE;
  26. packet.m_nBodySize = enc - packet.m_body;
  27. //----------------
  28. r->dlg->AppendMLInfo(20,1,"命令消息","ReleaseStream");
  29. //-----------------------------
  30. return RTMP_SendPacket(r, &packet, FALSE);
  31. }

再来看一个SendPublish()函数,用于发送“publish”命令

[cpp] view plaincopy
  1. //发送Publish命令
  2. static int
  3. SendPublish(RTMP *r)
  4. {
  5. RTMPPacket packet;
  6. char pbuf[1024], *pend = pbuf + sizeof(pbuf);
  7. char *enc;
  8. //块流ID为4
  9. packet.m_nChannel = 0x04; /* source channel (invoke) */
  10. packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
  11. //命令消息,类型20
  12. packet.m_packetType = 0x14;   /* INVOKE */
  13. packet.m_nTimeStamp = 0;
  14. //流ID
  15. packet.m_nInfoField2 = r->m_stream_id;
  16. packet.m_hasAbsTimestamp = 0;
  17. packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  18. //指向Chunk的负载
  19. enc = packet.m_body;
  20. //对“publish”字符串进行AMF编码
  21. enc = AMF_EncodeString(enc, pend, &av_publish);
  22. enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  23. //命令对象为空
  24. *enc++ = AMF_NULL;
  25. enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
  26. if (!enc)
  27. return FALSE;
  28. /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
  29. enc = AMF_EncodeString(enc, pend, &av_live);
  30. if (!enc)
  31. return FALSE;
  32. packet.m_nBodySize = enc - packet.m_body;
  33. //----------------
  34. r->dlg->AppendMLInfo(20,1,"命令消息","Pulish");
  35. //-----------------------------
  36. return RTMP_SendPacket(r, &packet, TRUE);
  37. }

其他的命令不再一一例举,总体的思路是声明一个RTMPPacket类型的结构体,然后设置各种属性值,最后交给RTMP_SendPacket()进行发送。

RTMPPacket类型的结构体定义如下,一个RTMPPacket对应RTMP协议规范里面的一个块(Chunk)。

[cpp] view plaincopy
  1. //Chunk信息
  2. typedef struct RTMPPacket
  3. {
  4. uint8_t m_headerType;//ChunkMsgHeader的类型(4种)
  5. uint8_t m_packetType;//Message type ID(1-7协议控制;8,9音视频;10以后为AMF编码消息)
  6. uint8_t m_hasAbsTimestamp;  /* Timestamp 是绝对值还是相对值? */
  7. int m_nChannel;         //块流ID
  8. uint32_t m_nTimeStamp;  // Timestamp
  9. int32_t m_nInfoField2;  /* last 4 bytes in a long header,消息流ID */
  10. uint32_t m_nBodySize;   //消息长度
  11. uint32_t m_nBytesRead;
  12. RTMPChunk *m_chunk;
  13. char *m_body;
  14. } RTMPPacket;

下面我们来看看RTMP_SendPacket()吧,各种的RTMPPacket(即各种Chunk)都需要用这个函数进行发送。

[cpp] view plaincopy
  1. //自己编一个数据报发送出去!
  2. //非常常用
  3. int
  4. RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue)
  5. {
  6. const RTMPPacket *prevPacket = r->m_vecChannelsOut[packet->m_nChannel];
  7. uint32_t last = 0;
  8. int nSize;
  9. int hSize, cSize;
  10. char *header, *hptr, *hend, hbuf[RTMP_MAX_HEADER_SIZE], c;
  11. uint32_t t;
  12. char *buffer, *tbuf = NULL, *toff = NULL;
  13. int nChunkSize;
  14. int tlen;
  15. //不是完整ChunkMsgHeader
  16. if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE)
  17. {
  18. /* compress a bit by using the prev packet's attributes */
  19. //获取ChunkMsgHeader的类型
  20. //前一个Chunk和这个Chunk对比
  21. if (prevPacket->m_nBodySize == packet->m_nBodySize
  22. && prevPacket->m_packetType == packet->m_packetType
  23. && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM)
  24. packet->m_headerType = RTMP_PACKET_SIZE_SMALL;
  25. if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp
  26. && packet->m_headerType == RTMP_PACKET_SIZE_SMALL)
  27. packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM;
  28. //上一个packet的TimeStamp
  29. last = prevPacket->m_nTimeStamp;
  30. }
  31. if (packet->m_headerType > 3)   /* sanity */
  32. {
  33. RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.",
  34. (unsigned char)packet->m_headerType);
  35. return FALSE;
  36. }
  37. //chunk包头大小;packetSize[] = { 12, 8, 4, 1 }
  38. nSize = packetSize[packet->m_headerType];
  39. hSize = nSize; cSize = 0;
  40. //相对的TimeStamp
  41. t = packet->m_nTimeStamp - last;
  42. if (packet->m_body)
  43. {
  44. //Header的Start
  45. //m_body是指向负载数据首地址的指针;“-”号用于指针前移
  46. header = packet->m_body - nSize;
  47. //Header的End
  48. hend = packet->m_body;
  49. }
  50. else
  51. {
  52. header = hbuf + 6;
  53. hend = hbuf + sizeof(hbuf);
  54. }
  55. //当ChunkStreamID大于319时
  56. if (packet->m_nChannel > 319)
  57. //ChunkBasicHeader是3个字节
  58. cSize = 2;
  59. //当ChunkStreamID大于63时
  60. else if (packet->m_nChannel > 63)
  61. //ChunkBasicHeader是2个字节
  62. cSize = 1;
  63. if (cSize)
  64. {
  65. //header指针指向ChunkMsgHeader
  66. header -= cSize;
  67. //hsize加上ChunkBasicHeader的长度
  68. hSize += cSize;
  69. }
  70. //相对TimeStamp大于0xffffff,此时需要使用ExtendTimeStamp
  71. if (nSize > 1 && t >= 0xffffff)
  72. {
  73. header -= 4;
  74. hSize += 4;
  75. }
  76. hptr = header;
  77. //把ChunkBasicHeader的Fmt类型左移6位
  78. c = packet->m_headerType << 6;
  79. switch (cSize)
  80. {
  81. //把ChunkBasicHeader的低6位设置成ChunkStreamID
  82. case 0:
  83. c |= packet->m_nChannel;
  84. break;
  85. //同理,但低6位设置成000000
  86. case 1:
  87. break;
  88. //同理,但低6位设置成000001
  89. case 2:
  90. c |= 1;
  91. break;
  92. }
  93. //可以拆分成两句*hptr=c;hptr++,此时hptr指向第2个字节
  94. *hptr++ = c;
  95. //CSize>0,即ChunkBasicHeader大于1字节
  96. if (cSize)
  97. {
  98. //将要放到第2字节的内容tmp
  99. int tmp = packet->m_nChannel - 64;
  100. //获取低位存储与第2字节
  101. *hptr++ = tmp & 0xff;
  102. //ChunkBasicHeader是最大的3字节时
  103. if (cSize == 2)
  104. //获取高位存储于最后1个字节(注意:排序使用大端序列,和主机相反)
  105. *hptr++ = tmp >> 8;
  106. }
  107. //ChunkMsgHeader。注意一共有4种,包含的字段数不同。
  108. //TimeStamp(3B)
  109. if (nSize > 1)
  110. {
  111. //相对TimeStamp和绝对TimeStamp?
  112. hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t);
  113. }
  114. //MessageLength+MessageTypeID(4B)
  115. if (nSize > 4)
  116. {
  117. //MessageLength
  118. hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize);
  119. //MessageTypeID
  120. *hptr++ = packet->m_packetType;
  121. }
  122. //MessageStreamID(4B)
  123. if (nSize > 8)
  124. hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);
  125. //ExtendedTimeStamp
  126. if (nSize > 1 && t >= 0xffffff)
  127. hptr = AMF_EncodeInt32(hptr, hend, t);
  128. //负载长度,指向负载的指针
  129. nSize = packet->m_nBodySize;
  130. buffer = packet->m_body;
  131. //Chunk大小,默认128字节
  132. nChunkSize = r->m_outChunkSize;
  133. RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket,
  134. nSize);
  135. /* send all chunks in one HTTP request */
  136. //使用HTTP
  137. if (r->Link.protocol & RTMP_FEATURE_HTTP)
  138. {
  139. //nSize:Message负载长度;nChunkSize:Chunk长度;
  140. //例nSize:307,nChunkSize:128;
  141. //可分为(307+128-1)/128=3个
  142. //为什么+nChunkSize-1?因为除法会只取整数部分!
  143. int chunks = (nSize+nChunkSize-1) / nChunkSize;
  144. //Chunk个数超过一个
  145. if (chunks > 1)
  146. {
  147. //注意:CSize=1表示ChunkBasicHeader是2字节
  148. //消息分n块后总的开销:
  149. //n个ChunkBasicHeader,1个ChunkMsgHeader,1个Message负载
  150. //实际中只有第一个Chunk是完整的,剩下的只有ChunkBasicHeader
  151. tlen = chunks * (cSize + 1) + nSize + hSize;
  152. //分配内存
  153. tbuf = (char *) malloc(tlen);
  154. if (!tbuf)
  155. return FALSE;
  156. toff = tbuf;
  157. }
  158. //消息的负载+头
  159. }
  160. while (nSize + hSize)
  161. {
  162. int wrote;
  163. //消息负载<Chunk大小(不用分块)
  164. if (nSize < nChunkSize)
  165. //Chunk可能小于设定值
  166. nChunkSize = nSize;
  167. RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)header, hSize);
  168. RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)buffer, nChunkSize);
  169. if (tbuf)
  170. {
  171. //void *memcpy(void *dest, const void *src, int n);
  172. //由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内
  173. memcpy(toff, header, nChunkSize + hSize);
  174. toff += nChunkSize + hSize;
  175. }
  176. else
  177. {
  178. wrote = WriteN(r, header, nChunkSize + hSize);
  179. if (!wrote)
  180. return FALSE;
  181. }
  182. //消息负载长度-Chunk负载长度
  183. nSize -= nChunkSize;
  184. //Buffer指针前移1个Chunk负载长度
  185. buffer += nChunkSize;
  186. hSize = 0;
  187. //如果消息没有发完
  188. if (nSize > 0)
  189. {
  190. //ChunkBasicHeader
  191. header = buffer - 1;
  192. hSize = 1;
  193. if (cSize)
  194. {
  195. header -= cSize;
  196. hSize += cSize;
  197. }
  198. //ChunkBasicHeader第1个字节
  199. *header = (0xc0 | c);
  200. //ChunkBasicHeader大于1字节
  201. if (cSize)
  202. {
  203. int tmp = packet->m_nChannel - 64;
  204. header[1] = tmp & 0xff;
  205. if (cSize == 2)
  206. header[2] = tmp >> 8;
  207. }
  208. }
  209. }
  210. if (tbuf)
  211. {
  212. //
  213. int wrote = WriteN(r, tbuf, toff-tbuf);
  214. free(tbuf);
  215. tbuf = NULL;
  216. if (!wrote)
  217. return FALSE;
  218. }
  219. /* we invoked a remote method */
  220. if (packet->m_packetType == 0x14)
  221. {
  222. AVal method;
  223. char *ptr;
  224. ptr = packet->m_body + 1;
  225. AMF_DecodeString(ptr, &method);
  226. RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val);
  227. /* keep it in call queue till result arrives */
  228. if (queue) {
  229. int txn;
  230. ptr += 3 + method.av_len;
  231. txn = (int)AMF_DecodeNumber(ptr);
  232. AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn);
  233. }
  234. }
  235. if (!r->m_vecChannelsOut[packet->m_nChannel])
  236. r->m_vecChannelsOut[packet->m_nChannel] = (RTMPPacket *) malloc(sizeof(RTMPPacket));
  237. memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket));
  238. return TRUE;
  239. }

这个函数乍一看好像非常复杂,其实不然,他只是按照RTMP规范将数据编码成符合规范的块(Chunk),规范可以参考相关的文档。

具体怎么编码成块(Chunk)就不多分析了,在这里需要注意一个函数:WriteN()。该函数完成了将数据发送出去的功能。

来看一下WriteN()函数:

[cpp] view plaincopy
  1. //发送数据报的时候调用(连接,buffer,长度)
  2. static int
  3. WriteN(RTMP *r, const char *buffer, int n)
  4. {
  5. const char *ptr = buffer;
  6. #ifdef CRYPTO
  7. char *encrypted = 0;
  8. char buf[RTMP_BUFFER_CACHE_SIZE];
  9. if (r->Link.rc4keyOut)
  10. {
  11. if (n > sizeof(buf))
  12. encrypted = (char *)malloc(n);
  13. else
  14. encrypted = (char *)buf;
  15. ptr = encrypted;
  16. RC4_encrypt2((RC4_KEY *)r->Link.rc4keyOut, n, buffer, ptr);
  17. }
  18. #endif
  19. while (n > 0)
  20. {
  21. int nBytes;
  22. //因方式的不同而调用不同函数
  23. //如果使用的是HTTP协议进行连接
  24. if (r->Link.protocol & RTMP_FEATURE_HTTP)
  25. nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n);
  26. else
  27. nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n);
  28. /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
  29. //成功发送字节数<0
  30. if (nBytes < 0)
  31. {
  32. int sockerr = GetSockError();
  33. RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
  34. sockerr, n);
  35. if (sockerr == EINTR && !RTMP_ctrlC)
  36. continue;
  37. RTMP_Close(r);
  38. n = 1;
  39. break;
  40. }
  41. if (nBytes == 0)
  42. break;
  43. n -= nBytes;
  44. ptr += nBytes;
  45. }
  46. #ifdef CRYPTO
  47. if (encrypted && encrypted != buf)
  48. free(encrypted);
  49. #endif
  50. return n == 0;
  51. }

该函数中,RTMPSockBuf_Send()完成了数据发送的功能,再来看看这个函数(函数调用真是好多啊。。。。)

[cpp] view plaincopy
  1. //Socket发送(指明套接字,buffer缓冲区,数据长度)
  2. //返回所发数据量
  3. int
  4. RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len)
  5. {
  6. int rc;
  7. #ifdef _DEBUG
  8. fwrite(buf, 1, len, netstackdump);
  9. #endif
  10. #if defined(CRYPTO) && !defined(NO_SSL)
  11. if (sb->sb_ssl)
  12. {
  13. rc = TLS_write((SSL *)sb->sb_ssl, buf, len);
  14. }
  15. else
  16. #endif
  17. {
  18. //向一个已连接的套接口发送数据。
  19. //int send( SOCKET s, const char * buf, int len, int flags);
  20. //s:一个用于标识已连接套接口的描述字。
  21. //buf:包含待发送数据的缓冲区。   
  22. //len:缓冲区中数据的长度。
  23. //flags:调用执行方式。
  24. //rc:所发数据量。
  25. rc = send(sb->sb_socket, buf, len, 0);
  26. }
  27. return rc;
  28. }
  29. int
  30. RTMPSockBuf_Close(RTMPSockBuf *sb)
  31. {
  32. #if defined(CRYPTO) && !defined(NO_SSL)
  33. if (sb->sb_ssl)
  34. {
  35. TLS_shutdown((SSL *)sb->sb_ssl);
  36. TLS_close((SSL *)sb->sb_ssl);
  37. sb->sb_ssl = NULL;
  38. }
  39. #endif
  40. return closesocket(sb->sb_socket);
  41. }

到这个函数的时候,发现一层层的调用终于完成了,最后调用了系统Socket的send()函数完成了数据的发送功能。

之前贴过一张图总结这个过程,可能理解起来要方便一些:RTMPDump源代码分析 0: 主要函数调用分析

rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561

rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163

RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)相关推荐

  1. LibRTMP源代码分析2:解释RTMP地址

    转载自:http://nkwavelet.blog.163.com/blog/static/227756038201412022720924/ 获取RTMP流媒体数据很重要的前提是RTMP的URL的解 ...

  2. RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  3. RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  4. RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  5. RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  6. RTMPDump(libRTMP)源代码分析 2 解析RTMP地址——RTMP ParseURL

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  7. Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...

  8. java给qq发消息_QQ发送消息

    我们做一个小程序,简单实现QQ发送消息的功能. 先定义一个消息类,里面有消息的属性(来自哪里,发送到哪,消息内容).然后定义一个测试类测试我们的运行结果,输出发送消息的内容.测试类里面需要一个Map来 ...

  9. RTMPdump(libRTMP) 源代码分析 9: 接收消息(Message)(接收视音频数据)

    2019独角兽企业重金招聘Python工程师标准>>> 注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表: RTMPdump 源代码分析 1: ...

最新文章

  1. thread.sleep是让哪个线程休眠_java开发两年,这些线程知识你都不知道,你怎么涨薪?...
  2. 企业托管云模式 浪潮ERP签约山东医药
  3. Express中app.use中间件的用法-匹配所有的请求方式
  4. pycharm-设快捷代码热键
  5. B站发布51部国创作品新内容 公布《三体》动画新PV
  6. 极品五笔管理员能用,普通用户无法使用
  7. 在eclispe的类中快速打出main方法
  8. Educoder Basemap和Seaborn 第2关:Seaborn图形介绍
  9. 拓端tecdat|R语言相关分析和稳健线性回归分析
  10. 钉钉日志范文100篇_钉钉工作日记(模板一)
  11. Photo Album: MSN中国版头像
  12. 如何制做计算机病毒,电脑病毒制作-怎么制作电脑病毒请教高手,怎么做病毒? – 手机爱问...
  13. 智能卡卡发卡流程(收藏1)
  14. 四个步骤写一份策划方案(下)
  15. 计算机远程控制相关考题,北邮远程计算机试题和答案.docx
  16. opengl 画椭圆_如何用彩铅画一朵牡丹?彩铅牡丹花的画法步骤,彩铅花卉画入门教程...
  17. 小猪的Python学习之旅 —— 14.项目实战:抓取豆瓣音乐Top 250数据存到Excel中
  18. 【媒体报道】2013eoe移动开发者大会圆满落幕
  19. meanshift 与 camshift 跟踪算法比较
  20. 腾讯C++后台开发实习面经(已拿offer)

热门文章

  1. mongodb清洗数据
  2. Linux终端乱码的解决办法
  3. Web游戏开发编程:最神奇的“触觉振动”
  4. CodeForces - 1455E Four Points(数学+几何)
  5. SPOJ - PHRASES Relevant Phrases of Annihilation(后缀数组+二分)
  6. CH - 0701 国王游戏(贪心+高精度运算)
  7. 微信小程序-地图组件(map)的使用
  8. 利用JSP交互式打印表格
  9. Lua 文件 I/O
  10. delphi中的ParamStr