rgb(255,0,0);">Type3chunksMUSTNOThavethisfield.
Thisfieldistransmittedonlywhenthenormaltimestampinthe
chunkmessageheaderissetto0x00ffffff.Ifnormaltimestampis
settoanyvaluelessthan0x00ffffff,thisfieldMUSTNOTbe
present.ThisfieldMUSTNOTbepresentifthetimestampfieldisnot
present.Type3chunksMUSTNOThavethisfield.
有可能是adobe变更了这个协议,也就是说,FMLE给FMS,FMS给flash-player,都是带这个时间戳的。
收包时,服务器端则需要检测下,若接下来的4字节不是extendedtimestamp,那么就忽略就好了。
发包时,只能采取配置,要么发,要么不发。
默认发就好了。
看nginx-rtmp代码:
[cpp]viewplaincopyprint?
6ngx_rtmp_recv(ngx_event_t*rev)
7{
8for(;;){
9/*parseheaders*/
10if(b->pos==b->start){
11/*chunkbasicheader*/
12fmt=(*p>>6)&0x03;
13csid=*p++&0x3f;
14if(fmt<=2){
15/*timestamp:
big-endian3b->little-endian4b*/
16pp=(u_char*)timestamp;
17ext=(timestamp==0x00ffffff);
18if(fmt<=1){
19/*size:
big-endian3b->little-endian4btype*/
20pp=(u_char*)&h->mlen;
21h->type=*(uint8_t*)p++;
22if(fmt==0){
23/*stream:
little-endian4b->little-endian4b*/
24pp=(u_char*)&h->msid;
25}
26}
27}
28/*extendedheader*/
29if(ext){
30pp=(u_char*)timestamp;
31pp[3]=*p++;
32pp[2]=*p++;
33pp[1]=*p++;
34pp[0]=*p++;
35}
ngx_rtmp_recv(ngx_event_t*rev)
{
for(;;){
/*parseheaders*/
if(b->pos==b->start){
/*chunkbasicheader*/
fmt=(*p>>6)&0x03;
csid=*p++&0x3f;
if(fmt<=2){
/*timestamp:
big-endian3b->little-endian4b*/
pp=(u_char*)timestamp;
ext=(timestamp==0x00ffffff);
if(fmt<=1){
/*size:
big-endian3b->little-endian4btype*/
pp=(u_char*)&h->mlen;
h->type=*(uint8_t*)p++;
if(fmt==0){
/*stream:
little-endian4b->little-endian4b*/
pp=(u_char*)&h->msid;
}
}
}
/*extendedheader*/
if(ext){
pp=(u_char*)timestamp;
pp[3]=*p++;
pp[2]=*p++;
pp[1]=*p++;
pp[0]=*p++;
}
可见,不管是什么chunk,只要有extended-timestamp,nginx-rtmp都会读这个。
所以nginx-rtmp对接FMLE是没有问题的,对接librtmp有问题。
更好的做法是判断下读出来的extendedtimestamp是否是和之前的一样,如果不是一样说明没有发,就忽略。
nginx-rtmp发包的逻辑:
[cpp]viewplaincopyprint?
36ngx_rtmp_prepare_message
37{
38/*createfmt3headerforsuccessivefragments*/
39thsize=p-out->buf->pos;
40ngx_memcpy(th,out->buf->pos,thsize);
41th[0]|=0xc0;
42/*messageheader*/
43if(fmt<=2){
44}
45
46/*extendedheader*/
47if(ext_timestamp){
48pp=(u_char*)&ext_timestamp;
49*p++=pp[3];
50*p++=pp[2];
51*p++=pp[1];
52*p++=pp[0];
53
54/*ThisCONTRADICTSthestandard
55*butthat'sthewayflashclient
56*wantsdatatobeencoded;
57*ffmpegcomplains*/
58if(cscf->play_time_fix){
59ngx_memcpy(&th[thsize],p-4,4);
60thsize+=4;
61}
62}
ngx_rtmp_prepare_message
{
/*createfmt3headerforsuccessivefragments*/
thsize=p-out->buf->pos;
ngx_memcpy(th,out->buf->pos,thsize);
th[0]|=0xc0;
/*messageheader*/
if(fmt<=2){
}
/*extendedheader*/
if(ext_timestamp){
pp=(u_char*)&ext_timestamp;
*p++=pp[3];
*p++=pp[2];
*p++=pp[1];
*p++=pp[0];
/*ThisCONTRADICTSthestandard
*butthat'sthewayflashclient
*wantsdatatobeencoded;
*ffmpegcomplains*/
if(cscf->play_time_fix){
ngx_memcpy(&th[thsize],p-4,4);
thsize+=4;
}
}
nginx-rtmp还特意说明了flash客户端就是和rtmp规范不一样。
out->buf->pos就是指向包的第一个chunk,若有extended-timestamp,那么肯定会在第一个包加上extendedtimestamp;后面还有个th和thsize,就是给后续的type=3的chunk包的,会加上这4字节的extendedtimestamp:
[cpp]viewplaincopyprint?
63/*ThisCONTRADICTSthestandard
64*butthat'sthewayflashclient
65*wantsdatatobeencoded;
66*ffmpegcomplains*/
67if(cscf->play_time_fix){
68ngx_memcpy(&th[thsize],p-4,4);
69thsize+=4;
70}
71
72/*appendheaderstosuccessivefragments*/
73for(out=out->next;out;out=out->next){
74out->buf->pos-=thsize;
75ngx_memcpy(out->buf->pos,th,thsize);
76}
/*ThisCONTRADICTSthestandard
*butthat'sthewayflashclient
*wantsdatatobeencoded;
*ffmpegcomplains*/
if(cscf->play_time_fix){
ngx_memcpy(&th[thsize],p-4,4);
thsize+=4;
}
/*appendheaderstosuccessivefragments*/
for(out=out->next;out;out=out->next){
out->buf->pos-=thsize;
ngx_memcpy(out->buf->pos,th,thsize);
}
所以nginx-rtmp收发包都是需要这个extended-timestamp,对于chunktype为3的包。
另外,把chunksize设置大一点,最大是65536,避免发type=3的chunk包,也是一个很好的方法。
不过视频的I帧一般较大,超过64KB也有可能。
Themaximumchunksizecanbe65536bytesandminimum128bytes.
按照这种规则,即type=3的chunk也发extended-timestamp,是没有问题:
FMLE一直没有断开,客户端也没有断开。
服务器端是srs(simplertmpserver),也没有断开:
时间已经超过了24位,使用extended-timestamp:
[plain]viewplaincopyprint?
77(gdb)p/x21039918
78$1=0x1410b2e
(gdb)p/x21039918
$1=0x1410b2e
而nginx呢,是采用第一个包发完全的extended-timestamp,但是后续都是发type=1的chunk,即时间戳差值。
完毕。