直播推流兼容性问题追踪

      好久没有写了。说说最近遇到的一个坑吧。
      很多直播产品都用的librtmp来做rtmp流的传输。
      关于RTMP的协议,在官方文档rtmp_specification_1.0.pdf中有具体的说明。
首先建立一个RTMP链接,建立链接的步骤先要handshake,需要客户端向服务器发送C0,C1,C2三个RTMP Chunk。
rtmp_specification_1.0中规定C0是RTMP version,一个字节。目前一般使用的0x03,1和2废弃了,超过4预留。


C1是1536个字节,由4个字节的时间戳、4个字节的0和1528个字节的随机数组成。
一般定义#define RTMP_SIG_SIZE 1536


      通常在客户端中C0和C1是同时发送,这里通常的代码是:

char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
//clientbuf中存有C0和C1数据,clientsig只含有C1数据
char serversig[RTMP_SIG_SIZE];
// RTMP协议版本号为0x03,即C0数据 
clientbuf[0] = 0x03; /* not encrypted,没有加密 */
// 获取系统时间(毫秒为单位),将其写入到C1中,占4个字节
uptime = htonl(RTMP_GetTime());
memcpy(clientsig, &uptime, 4);
// 系统时间之后是4个字节的0,固定的 
memset(&clientsig[4], 0, 4);

#ifdef _DEBUG
// debug版,后面的1528个随机数简单的都设为0xff
for (i = 8; i < RTMP_SIG_SIZE; i++)
clientsig[i] = 0xff;
#else
// release版,使用rand()循环生成1528个伪随机数
for (i = 8; i < RTMP_SIG_SIZE; i++)
clientsig[i] = (char)(rand() % 256);
#endif

      而百度出来结果还一种错误的写法类似,

conn->clientbuf[0] = 0x03;
uptime = htonl( RTMP_GetTime() );
memcpy( conn->clientbuf + 1, &uptime, 4 );
memset( conn->clientbuf + 4, 0, 4 );
for( i = 8; i < RTMP_SIG_SIZE; i++ )
conn->clientbuf[i] = ( char )( rand() % 256 );

      很不幸,我们最初版本的代码就是百度出来的这个版本。
      早期的直播产品使用simple handshake,都没有做握手协议校验。甚至使用了几家CDN也是同样没做校验。
于是,从未怀疑过这段代码。至少从我接手后,从未改过这几句。

      一次做迅雷星域CDN推流兼容的时候,发现我们的直播产品推流失败。
进一步查看,发现握手都未成功。抓包后发现C0C1发送后,并未收到服务器发来的S0S1chunk。
和星域CDN的技术沟通后发现才发现问题所在。
原来CDN做了simple handshake和complex handshake校验,验证handshake中的C0C1。而我的代码
C1错位了一个字节。正确的写法如下:

memcpy( conn->clientbuf + 1, &uptime, 4 );
memset( conn->clientbuf + 5, 0, 4 );

for( i = 9; i < RTMP_SIG_SIZE; i++ )
conn->clientbuf[i] = ( char )( rand() % 256 );

修改后,推流到星域CDN完全没问题了。

这个问题相信不少人都遇到过。轻易的发布博客或者转帖而没有验证具体代码的执行结果,导致看到博客的人信以为真。为了避免类似的问题,还需要每一个开发者谨慎对待自己发出去的技术博客。我的每篇博客都是自己验证了代码的。

原文地址:https://www.cnblogs.com/zzugyl/p/6676549.html