【问题标题】:H.264 over RTP - Identify SPS and PPS FramesH.264 over RTP - 识别 SPS 和 PPS 帧
【发布时间】:2012-03-25 23:28:10
【问题描述】:

我有一个来自 IP 摄像机的原始 H.264 流,打包在 RTP 帧中。我想将原始 H.264 数据放入文件中,以便可以使用 ffmpeg 进行转换。

因此,当我想将数据写入原始 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]
....

所以我从之前的RTSP 通信中得到了Session Description Protocol 中的SPSPPS。此外,在开始视频流本身之前,相机会在两条单独的消息中发送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

然后出现了一些带有有效负载的帧,在某些时候出现了带有Marker Bit = 1 的 RTP 帧。这意味着(如果我没记错的话)我有一个完整的视频帧。在此之后,我再次从有效负载中编写前缀序列(00 00 01)和NAL,并继续执行相同的过程。

现在,我的相机在每 8 个完整的视频帧之后再次向我发送 SPSPPS。 (同样在两个 RTP 帧中,如上例所示)。我知道,尤其是 PPS 可以在流式传输之间发生变化,但这不是问题。

我现在的问题是:

1.我需要每 8 个视频帧编写一次 SPS/PPS 吗?

如果我的SPS 和我的PPS 不更改,将它们写在我的文件的开头就足够了,仅此而已?

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

在解析传输数据的 C++ 代码中,我需要区分具有正常有效负载的 RTP 帧和带有 SPS/PPS 的帧。我怎样才能区分它们?好的,SPS/PPS 帧通常要小得多,但这不是可以依赖的保存调用。因为如果我忽略它们,我需要知道我可以丢弃哪些数据,或者如果我需要编写它们,我需要将00 00 01 前缀放在它们前面。 ?还是每 8 个视频帧出现一次的固定规则?

【问题讨论】:

  • 感谢您提出这个问题。我和你有同样的问题。我通读了 live555 源代码,不知道他们为什么要这样保存每个数据包/帧。读完这篇文章后,我明白了。作为基于live555实现的建议,marker位只用于其他编解码器,H264有自己的start_bit和end_bit来表示帧的开始/结束,H264不使用marker位。

标签: c++ h.264 rtsp rtp


【解决方案1】:
  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;

【讨论】:

  • 这意味着SPSPPS 帧的前两个字节也是某种“NAL 状态”,就像每隔一个RTP 帧的前两个字节一样?或者换句话说 - 我预计 SPS 和 PPS 为 7 或 8 的 nal_unit_type 字段与我预计 28 表示其视频数据的字段相同?
  • nal_unit_type的详细定义可以参考H.264文档。顺便说一句,(payload[0] & 0x1f) == 28 表示这是一个分段的视频帧,在这种情况下,实际的 nal_unit_type 应该是 (payload[1] & 0x1f)。这是在 RFC3984 中定义的。
  • 是的,我刚读过它就知道了……但是您怎么知道 nal_unit_type = 28 是分段视频帧? RFC 3984 引用了www-ee.uta.edu/dip/courses/ee5356/H264systems.pdf 表 7.1(第 63 页) - 代码 28 将属于“未指定”.. ?
  • 在 RFC 3984 的表 1 中:28 FU-A Fragmentation unit 5.8
  • 啊,当然——我有点困惑,因为我需要查看该外部文件(上面的链接)中前 23 个值的代码,而其他代码在 RFC 的表格中。好吧,谢谢你为我做的!
【解决方案2】:
  1. 您应该在流的开头写入 SPS 和 PPS,并且仅当它们在流的中间发生变化时。

  2. SPS 和 PPS 帧被打包在一个 STAP NAL 单元(通常是 STAP-A)中,NAL 类型为 24 (STAP-A) 或 25 (STAP-B) STAP 格式在 RFC-3984 section 5.7.1 中描述

  3. 不要依赖标记位,在 NAL 标头中使用起始位和结束位。

  4. 对于分段视频帧,您应该使用第一个片段(F,NRI)的 3 个 NAL 单元位与有效负载中第一个字节的 5 个 NAL 类型位(仅适用于起始位设置为 1 的数据包)重新生成 NAL 单元检查@987654322 @:

    分片的 NAL 单元类型八位字节 NAL 单元不包含在分段单元有效负载中, 而是 NAL 单元类型八位字节的信息 分段的 NAL 单元在 FU 的 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”中获取“Type”,从“FU indicator”中获取“F”和“NRI”

here 是一个简单的实现

【讨论】:

  • 你能解释一下第4条吗?我已经阅读了该规范的那一部分以及您引用的内容多次,但我不明白它在描述什么。是否意味着应该根据这些规则重构FU指示符,丢弃FU头,丢弃有效载荷的前5位,并将现在重构的FU指示符与有效载荷连接(减去前5位)?谢谢
  • @Joshua:我补充了一些解释
  • 谢谢,代码示例非常优雅,感谢您的简化解释。事后看来,您引用的简介很简单,但您在第 4 项中的解释建议使用有效负载中的位,因此我感到困惑。不过我现在明白你的意思了。
猜你喜欢
  • 1970-01-01
  • 2011-10-05
  • 2012-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 2014-12-29
相关资源
最近更新 更多