RTMP转换成TS流的注意事项

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播放不正常,反过来也有。最后才把两边播放调整正常了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注