H.264数据的FU切分与恢复
Charles Chan @ 2021-07-31 #H.264 @Ethernet
在传输负载H.264的流媒体时,无论是使用AVTP协议还是RTP协议,都会面临一个数据分包的问题。 这是由于以太网的PDU的大小存在上限,也就是MTU。这个数值通常是1500个字节。
为了解决这个问题,H.264引入了Fragmented Unit的概念。
如果H.264的媒体文件是以“字节流”的形式保存的, 我们可以看到,大概率,其每一个视频帧都是以标准“NALU”的类型存储的。
这时候,我们就需要将NALU的类型格式转换为FU-A的类型格式。
FU-A的数据格式与常用的NALU类型的数据格式非常类似。差别仅仅在类型字段。
NALU类型数据的类型字段是一个字节。FU-A类型数据的的类型字段是两个字节。 分别如下图所示:
+---------------+ +---------------++---------------+
|0|1|2|3|4|5|6|7| |0|1|2|3|4|5|6|7||0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
|F|NRI| Type | |F|NRI| Type1 ||S|E|R| Type2 |
+---------------+ +---------------++---------------+
上图的左侧部分为NALU的类型字段, 右侧部分为FU-A的类型字段(前8个Bites为FU indicator、后8个Bits为FU header)。
FU-A的F和NRI字段与NALU的意义相同。 Type1固定为28。 Type2为这包数据在使用非FU模式的时候的Type值(可以简单地认为就是NALU的Type值)。 S表示这是一个NAL的起始帧,E表示这是一个NAL的终止帧。R固定为0,是保留位。
因此,将一个标准的NALU的NAL转换为FU类型的方式非常简单。
对于发送端来说, 只要将一个字节的类型字段拆分成两个字节,并按着MTU的限制将NAL Data拆分成多段。
digraph G {
node [shape=record];
graph [ splines = line ];
SA [label="{NALU Type | {<f> F |<n> NRI |<t> Type } }| {<d1> NALU Data 1}| {<dx> NALU Data ...}| {<dn> NALU Data N}"];
FU [label="{<i> N}| {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {<s> S(1) | E(0) | R(0) | <t> Type } }| {<p> FU-A Payload}"];
FUx [label="{<i> N + ...} | {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {S(0) | E(0) | R(0) | <t> Type } } | {<p> FU-A Payload}"];
FUn [label="{<i> N + n} | {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {S(0) |<e> E(1) | R(0) | <t> Type } } | {<p> FU-A Payload}"];
FU:i -> FUx:i;
FUx:i -> FUn:i;
SA:f -> FU:f [color="DodgerBlue" ];
SA:f -> FUx:f [color="DodgerBlue" ];
SA:f -> FUn:f [color="DodgerBlue" ];
SA:n -> FU:n [color="Blue" ];
SA:n -> FUx:n [color="Blue" ];
SA:n -> FUn:n [color="Blue" ];
SA:t -> FU:t [color="Green" ];
SA:t -> FUx:t [color="Green" ];
SA:t -> FUn:t [color="Green" ];
SA:d1 -> FU:p [label="第一段" color="Red" ];
SA:dx -> FUx:p [label="第...段" color="Red" ];
SA:dn -> FUn:p [label="最后一段" color="Red" ];
}
注意图中S和E的数值变化。它们标识了这一组FUs的起始。
在接收端,数据恢复的过程与之相反:
digraph G {
node [shape=record];
graph [splines=line];
SA [label="{NALU Type | {<f> F |<n> NRI |<t> Type } }| {<d1> NALU Data 1} | {<dx> NALU Data ...} | {<dn> NALU Data N}"];
FU [label="{<i> N} | {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {S(1) | E(0) | R(0) | <t> Type } } | {<p> FU-A Payload}"];
FUx [label="{<i> N + ...} | {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {S(0) | E(0) | R(0) | <t> Type } } | {<p> FU-A Payload}"];
FUn [label="{<i> N + n} | {FU-A Indicator | {<f> F |<n> NRI | Type(28) } } | {FU-A Header | {S(0) | E(1) | R(0) | <t> Type } } | {<p> FU-A Payload}"];
FU:i -> FUx:i [label="下一条数据" color="DodgerBlue" ];
FUx:i -> FUn:i [label="下一条数据" color="DodgerBlue" ];
FU:f -> SA:f [color="Blue" ];
FU:n -> SA:n [color="Blue" ];
FU:t -> SA:t [color="Blue" ];
FU:p -> SA:d1 [color="Red" ];
FUx:p -> SA:dx [color="Red" ];
FUn:p -> SA:dn [color="Red" ];
}
注1:NAL是Network Abstraction Layer,即网络抽象层的缩写。NALU 即 NAL Unit。 在H.264中,一个NAL基本上等同于一个数据帧。
注2:MTU是Maximum Transmission Unit,即最大传输单元的缩写。 以太网的MTU通常为1500字节。
注3:FU是Fragmentation Unit的缩写。 在H.264中,一个FU是某个NAL的一部分,且不能是完整的一个NAL (即一个NAL至少需要切成两个FUs,否则就不能使用FU的概念)。