RTMP转换成TS流的注意事项,

一  需要注意PCR的标记,虽然有人说PCR不是必须的,但是如果你标记PCR的话,必须写对PCR,开始做的时候,因为看见网上的评论说,ipad和苹果的系统并不检查PCR的。所以我就乱打,结果在windows上播放出问题,windows上的媒体播放器会播放得特别快,他是根据PCR的时间播放的。

另外PCR的字段生成,不是简单的=DTS就可以了。PCR的组成是33(PCR_Base)+6(PCR_const)+9(PCR_Ext) ,计算方法参考下边,此方法是参照SRS的计算方式,另外stackoverflow有另外一个方式计算。我觉得SRS的计算方式更加好理解。 PCR_Ext = 0; PCR_Const = 0x3F; int64_t pcrv = PCR_Ext & 0x1ff; pcrv |= (PCR_Const << 9) & 0x7E00; pcrv |= (PCR_Base << 15) & 0xFFFFFFFF8000LL;

pp = (char*)&pcrv; data[ 6] = pp[5]; data[ 7] = pp[4]; data[ 8] = pp[3]; data[ 9] = pp[2]; data[10] = pp[1]; data[11] = pp[0];

另外PCR转换成播放时间的秒的方法,具体方法可以参考stackoverflow的帖子: pcrv = 0 pcrv |= uint64(pcr[0]) << 40 pcrv |= uint64(pcr[1]) << 32 pcrv |= uint64(pcr[2]) << 24 pcrv |= uint64(pcr[3]) << 16 pcrv |= uint64(pcr[4]) << 8 pcrv |= uint64(pcr[5])

programClockReferenceBase := (pcrv & 0xFFFFFFFF8000) >> 15 programClockReferenceExtension := pcrv & 0x1FF constValue := (pcrv & 0x7E00) >> 9 s =float64(programClockReferenceBase * 300 + programClockReferenceExtension) / 27000000

二 关于rtmp接收到的包,有可能会一个rtmp包含多个个slice)的,。需要分离出每一个slice出来,并在每一个PES包 放入slice前添加 0x00,0x00,0x00,0x01,0x09,0xF0。开始没有主意的时候,这个是根据apple 给的文档 第十条中说到:H.264 video access units must use Access Unit Delimiter NALs, and must be in unique PES packets.

,ffmpeg推流的时候,刚刚开始的推会把编码器信息和第一贞连在一起发,需要把他分开,并添加h264的分隔符。需要在关键贞前面添加pps和sps信息。不能把pps和sps放在单独的PES包内发送

这里讲解下rtmp的视频包结构。。

rtmp视频包在h264的包前边再添加了9个Byte.具体内容填写参考srs的代码 SrsRawH264Stream::mux_ipb_frame 和 SrsRawH264Stream::mux_avc2flv 这个是ipb贞的编码. buf[0] = 贞类型和编码. ( (1|2)<<4 | 7 ) 1是关键帧 2是P/B贞 7代表h264 buf[1] = 数据类型 ( 1 = NALU ) buf[2-4] = cts = pts - dts; buf[5-8] = NAL的长度,不包括贞分隔符 buf[9-] = H264的RAW数据. 不包含分隔符,00 00 01 或者 00 00 00 01 buf[9+NAL_length- +4byte] = 下一个NAL长度 这个很重要,一个rtmp的视频包可能包括多个NAL的,切记转换TS的时候把它分割并添加NAL的分隔符,例如{0x00,0x00,0x01}等。 buf[9+NAL_length+4 -] = 下一个H264的RAW数据. ……

这里需要注意的是cts这个值,如果视频流没有B侦的话,这个cts值=0 ,如果,这个cts值是一个动态数字,你需要根据这个值去计算pts,rtmp协议给的timestamp是DTS来的。如果你简单把pts等与dts的话,播放的时候画面非常乖,有前进后退,前进后退的感觉。 另外rtmp的timestamp与ts流的timestamp需要通过计算转换。 TSdts := uint64(timestamp * 90) TSpts := TSdts + uint64(cts * 90) 参考srs,srs_kernel_ts.cpp文件中的SrsTsCache::cache_video函数.

sps和pps编码成rtmp包参考srs的SrsRawH264Stream::mux_sequence_header rtmp sps pps sequence包结构 推送的时候是这样,但是接收的时候有些字段不对.有可能服务器推送过来的字段有不同. 分解的时候按照下边方式分解。 buf[0] = 贞类型和编码. ( (1<<4) | 7) 7代表h264 buf[1] = 数据类型 (0) buf[2-4] = cts = pts - dts; buf[0+5] = 0x01 buf[1+5] = sps[1] buf[2+5] = 0x00 buf[3+5] = sps[3] buf[4+5] = 0x03 buf[5+5] = 0x01 buf[6-7(+5)] = sps.length buf[8-11(+5)] = sps 假设sps长度魏4. buf[12(+5)] = 0x01 buf[13-14(+5)] = pps.length buf[15-(+5)] = pps

三 关于AAC包问题。 本来在做的过程中,AAC包处理是最顺利的,刚开始的时候生产的ts流是有声音,没有图像的,后来才把图像调出来的。不过到最后AAC的包处理还是有很多问题的。主要的问题也是ffmpeg在推流的时候第一个rtmp包也是包含了一些编码器的信息。如果把这个包独立存入PES包中。mac电脑是没有声音的。有两种解决方法,第一种是把第一个包和第二个AAC合在一起存在同一个PES中,第二个方法是丢弃第一个声音包。不清楚原因是什么,查看SRS的代码,声音AAC部分是存储在缓存中,等待声音信息缓存超过一定长度才一次把数个AAC包存储在一个PES中,不是没一个AAC包存储在一个PES中。

rtmp的AAC音频包解包如下。 buf[0] = audio_header //audio_header的组成,参照SrsRawAacStream::mux_aac2flv buf[1] = aac_packet_type //aac音频数据为 1 sequenc_header为 0 buf[2-] = aac_raw_data //aac原始数据. 不包含0xFFF等7byte的头部.

AAC而言加上界定符每一帧的前7字节是帧的描述信息 AAC 的贞分隔符是.0xFFF.比较怪,12个bit全1.不是字节对齐. 另外贞头纪录贞的长度是13bit,真的怪,又是字节不对齐. 具体参照srs_aac_adts_frame_size sequence_heade的数据组成参考SrsRawAacStream::mux_sequence_header (rtmp to aac应该可以忽略这个包) AAC的rtmp结构和 sequence_header的rtmp包结构 参照srs的SrsRawAacStream::mux_aac2flv

另外ffplay的容错性非常好,在测试调试中,几乎写错好多字段的ts文件都可以播放,而且没有问题。但是mac或者windows就不行,有时候调了mac正常了,又轮到windows播放不正常,反过来也有。最后才把两边播放调整正常了。