RTMP Handshake导致Flash不能播放H264流

详细分析和例子参考:http://blog.csdn.net/win_lin/article/details/13006803

服务器端实现参考:高性能流媒体服务器SRS:https://github.com/winlinvip/simple-rtmp-server


Adobe在2009年公开了rtmp协议,wikipedia说是部分公开(an incomplete version)而且handshake也有变更。

simple handshake是rtmp spec 1.0定义的握手方式。而complex handshake是后来变更的方式,Adobe没有公开。若研发rtmp server,将h264数据给FP播放时,必须为complex handshake,否则数据能传输但无法播放。

读rtmpd(ccrtmpserver)代码发现,handshake确实变更了。BTW:rtmpd的代码可读性要强很多,很快就能知道它在做什么;不过,它是部分C++部分C的混合体;譬如,使用了BaseRtmpProtocol和InboundRtmpProtocol这种C++的解决方式;以及在解析complex handshake时对1536字节的包直接操作,offset=buf[772]+buf[773]+buf[774]+buf[775],这个就很难看明白在做什么了,其实1536是由4字节的time+4字节的version+764字节的key+764字节的digest,key的offset在后面,digest的offset在前面,若定义两个结构再让它们自己去解析,就很明白在做什么了。

sources\thelib\src\protocols\rtmp\inboundrtmpprotocol.cpp: 51

InboundRTMPProtocol::PerformHandshake
    // 没有完成握手。
    case RTMP_STATE_NOT_INITIALIZED:
        // buffer[1537]
        // 第一个字节,即c0,表示握手类型(03是明文,06是加密,其他值非法)
        handshakeType = GETIBPOINTER(buffer)[0];
        // 删除第一个字节,buffer[1536] 即c1
        buffer.Ignore(1);
        // 第5-8共4个字节表示FPVersion,这个必须非0,0表示不支持complex handshake。
        _currentFPVersion = ENTOHLP(GETIBPOINTER(buffer) + 4);
        // 进行明文握手(false表示明文)
        PerformHandshake(buffer, false);
        
InboundRTMPProtocol::PerformHandshake
    // 先验证client,即验证c1
    // 先尝试scheme0
    valid = ValidClientScheme(0);
    // 若失败,再尝试scheme1
    valid = ValidClientScheme(1)
    // 若验证成功
    if(valid)
        // 复杂的handshake:PerformComplexHandshake,主要流程如下:
        S0 = 3或6 
        随机数初始化S1S2
        根据C1的public key生成S1的128byets public key
        生成S1的32bytes digest
        根据C1和S2生成S2的32bytes digest 
    else
        // rtmp spec 1.0定义的握手方式 
        PerformSimpleHandshake();


其实到后面看明白了,scheme1和scheme2这两种方式,是包结构的调换。

complex的包结构如下:C1/S1 1536bytes
    time: 4bytes 开头是4字节的当前时间。(u_int32_t)time(NULL)
    peer_version: 4bytes 为程序版本。C1一般是0x80000702。S1是0x04050001。
    764bytes: 可能是KeyBlock或者DigestBlock
    764bytes: 可能是KeyBlock或者DigestBlock
其中scheme1就是KeyBlock在前面DigestBlock在后面,而scheme0是DigestBlock在前面KeyBlock在后面。
子结构KeyBlock定义:
    760bytes: 包含128bytes的key的数据。
    key_offset: 4bytes 最后4字节定义了key的offset(相对于KeyBlock开头而言)
字结构DigestBlock定义:
    digest_offset: 4bytes 开头4字节定义了digest的offset(相对于第DigestBlock的第5字节而言,offset=3表示digestBlock[7~38]为digest
    760bytes: 包含32bytes的digest的数据。

其中,key和digest的主要算法是:C1的key为128bytes随机数。C1_32bytes_digest = HMACsha256(P1+P2, 1504, FPKey, 30) ,其中P1为digest之前的部分,P2为digest之后的部分,P1+P2是将这两部分拷贝到新的数组,共1536-32长度。S1的key根据C1的key算出来,算法如下:

DHWrapper dhWrapper(1024);
dhWrapper.Initialize()
dhWrapper.CreateSharedKey(c1_key, 128)
dhWrapper.CopyPublicKey(s1_key, 128)
S1的digest算法同C1。注意,必须先计算S1的key,因为key变化后digest也也重新计算。
S2/C2没有key,只有一个digest,是根据C1/S1算出来的:
先用随机数填充S2
s2data=S2[0-1504]; 前1502字节为随机的数据。
s2digest=S2[1505-1526] 后32bytes为digest。
// 计算s2digest方法如下:
ptemphash[512]: HMACsha256(c1digest, 32, FMSKey, 68, ptemhash)
ps2hash[512]: HMACsha256(s2data, 1504, ptemphash, 32, ps2hash)
将ps2hash[0-31]拷贝到s2的后32bytes。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winlinvip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值