2012-03-08 135 views
10

我有一个来自IP摄像机的原始H.264流,打包在RTP帧中。我想将原始H.264数据转换为文件,以便我可以将它转换为ffmpegH.264 over RTP - 识别SPS和PPS帧

所以,当我想将数据写入到我生的H.264文件,我发现它看起来像这样:

00 00 01 [SPS] 
00 00 01 [PPS] 
00 00 01 [NALByte] 
[PAYLOAD RTP Frame 1]  // Payload always without the first 2 Bytes -> NAL 
[PAYLOAD RTP Frame 2] 
[... until PAYLOAD Frame with Mark Bit received] // From here its a new Video Frame 
00 00 01 [NAL BYTE] 
[PAYLOAD RTP Frame 1] 
.... 

所以我得到的SPSPPSSession Description Protocol出来的我前面的RTSP通信。此外,在开始使用视频流本身之前,相机会在两条单独消息中发送SPSPPS

所以我捕捉到的消息顺序:

1. Preceding RTSP Communication here (including SDP with SPS and PPS) 
2. RTP Frame with Payload: 67 42 80 28 DA 01 40 16 C4 // This is the SPS 
3. RTP Frame with Payload: 68 CE 3C 80     // This is the PPS 
4. RTP Frame with Payload: ... // Video Data 

再就是出现一些框架的有效载荷,并在某些时候RTP框架与Marker Bit = 1。这意味着(如果我说得对)我有一个完整的视频框架。因此,我再次从有效载荷中写入前缀序列(00 00 01)和NAL,并继续执行相同的过程。

现在我的相机每隔8个完整的视频帧发送给我SPSPPS。 (同样在两个RTP帧中,如上例所示)。我知道特别是PPS可以在流媒体之间改变,但这不是问题。

我的问题,现在:

1.我是否需要写的SPS/PPS每8个视频帧?

如果我的SPS和我的PPS不改变它应该足以让他们写在我的文件的开头,没有更多?

2.如何区分SPS/PPS和普通RTP帧?

在我解析传输数据的C++代码中,我需要区分具有正常有效负载的RTP帧和携带SPS/PPS的RTP帧。我怎样才能区分它们?好的SPS/PPS帧通常是小的,但这不是一个救人的呼吁依靠。因为如果我忽略它们,我需要知道可以扔掉哪些数据,或者如果我需要写出它们,我需要将00 00 01前缀放在它们前面。 ?或者它是一个固定的规则,他们每8个视频帧发生?

+0

感谢您的这个问题。我和你有同样的问题。我通过live555源代码阅读,不知道为什么他们保存每个数据包/框架。阅读完这篇文章后,事情就变得很清楚了。作为基于live555实现的建议,标记位仅用于其他编解码器,H264具有自己的start_bit和end_bit来表示帧的开始/结束,标记位不用于H264。 – user534498 2015-03-31 01:50:24

回答

10
  1. 如果SPS和PPS没有变化,您可以省略它们,但第一个除外。
  2. 您需要解析每个NAL的nal_unit_type字段,对于SPS,nal_unit_type == 7;对于PPS,nal_unit_type == 8。

我记得,nal_unit_type是帧的第一个字节的低5位。

nal_unit_type = frame[0] & 0x1f; 
+0

这意味着'SPS'和'PPS'帧的前2个字节也是某种“NAL状态”,就像每个其他RTP帧的前两个字节一样?或换句话说 - 我期望7或8的SPS和PPS的nal_unit_type字段与我期望的28相同的字段意味着它的视频数据? – Toby 2012-03-08 13:43:04

+2

有关nal_unit_type的详细定义,您可以参考H.264文档。 btw,(payload [0]&0x1f)== 28表示这是一个分段的视频帧,在这种情况下,实际的nal_unit_type应该是(payload [1]&0x1f)。这在RFC3984中定义。 – ciphor 2012-03-08 13:55:04

+0

是的,我刚刚阅读并得到它......但你怎么知道nal_unit_type = 28是零碎的视频帧? RFC 3984引用http://www-ee.uta.edu/dip/courses/ee5356/H264systems.pdf表7.1(页63) - 那里代码28将归入“未指定”。? – Toby 2012-03-08 14:04:53

10
  1. 你应该写SPS和PPS在流的开始,只有当他们在流的中间改变。

  2. SPS和PPS帧用NAL类型24(STAP-A)或25(STAP-B)STAP格式RFC-3984 section 5.7.1

  3. 唐描述装在一个STAP NAL单元(通常STAP-A)不依赖于标记位,使用NAL头中的起始位和结束位。

  4. 对于分段视频帧,您应该使用第一个片段(F,NRI)的3个NAL单元位与有效负载中第一个字节的5个NAL类型位(仅用于启动位设置为1的数据包)重新生成NAL单元RFC-3984 section 5.8

    零散 NAL单元的NAL单元类型字节未在分片单元的有效负载, 而是 片NAL单元的NAL单元类型字节的信息在被传送包括作为这样在分片单元的指示符字段中以及在的类型字段中的F和NRI字段FU头。

编辑:约NAL单元构造用于分片单元更多的解释:

这是前两个字节的FU-A的有效载荷的(RTP报头之后):

| FU indicator | FU header | 
+---------------+---------------+ 
|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
|F|NRI| Type |S|E|R| Type | 
+---------------+---------------+ 

要构建NAL单元,您应该从“FU Header”和“F”和“NRI”中从“FU indicator”中取“Type”

here是一个简单的实现

+0

你能解释第4项吗?我已经阅读过规格说明部分以及您多次引用的简介,我不明白它描述的是什么。这是否意味着应根据这些规则重构FU指示符,并丢弃FU报头,并丢弃有效载荷的前5位,并将现在重建的FU指示符与有效负载(少于前5位)串联起来?谢谢 – Joshua 2014-01-27 04:33:34

+1

@Joshua:我加了一些解释 – 2014-01-27 08:14:05

+0

谢谢,代码示例很优雅,我很感谢你的简单解释。事后看来,你引用的blurb是直接的,但是你在第4项中的解释建议使用有效载荷中的位,因此我的困惑。我现在明白你的意思了。 – Joshua 2014-01-27 16:11:47