我从RTSP流中获得了H264 RTP数据包。所以我想检测帧是否是I帧。 以下是我第一次打开流时获得的第一个数据包。所以我相信它是一个I帧(我先显示160个字节)。
packet:
00 00 00 01 67 4D 00 1F 95 A8 14 01 6E 40 00 00 00 01 68 EE 3C 80 00 00 00 01 06 E5 01 33 80 00 00 00 01 65 B8 00 00 08 52 90 9F F6 BE D6 C6 9C 3D F6 D4 2F 49 FB F7 13 F2 A9 C7 27 2D A4 75 59 6C DB FF 35 27 A4 C7 B6 E7 69 A2 E0 FB 0E FF 2D 0E E0 6F 25 43 78 BF B9 69 22 1B 24 E3 CA 60 56 44 16 6C 15 44 DA 55 29 C2 39 24 86 CE D6 75 BB E0 0C F4 F4 EC C5 76 E4 7B 59 B9 40 2D B3 ED 19 E4 1D 94 B7 54 9B B3 D0 8F 24 58 CD 3C F3 FA E0 D4 7D 88 70 0E 49 79 12 B2 14 92 BA B6 9C 3A F7 8D 13 78 6B 4C CD C0 CC C8 39 6A AC BE 3D AA 00 9A DB D2 68 70 5F C4 20 B7 5C FC 45 93 DB 00 12 9F 87 5A 66 2C B2 B8 E7 63 C4 87 0B A4 AA 2E 6D AB 42 3F 02 C2 A6 F9 41 E5 FE 80 64 49 14 38 3D 52 4B F6 B2 E7 53 DD 3E F6 BB A8 EB 13 23 BB 71 B1 C9 90 06 92 3E 5F 15 F2 C0 39 43 EA 24 5A 86 AE 11 27 D4 C5 4B 5C CD 6C 90 2B 44 80 18 76 95 6E 16 DF 5D 86 49 25 5A B6 66 23 E6 40 D4 25 6B CE A2 4C EE 13 DD 7B 88 FF A0 64 EC 33 44 B1 DC B7 0B 89 5B 8F 85 68 3C 65 3E 55 0F 41 4B 32 C9 C8 56 78 1A 15 14 8C C7 F5 17 40 D4 EC BC 5B 62 8A 24 66 6A C3 7E 3B DB 44 A8 EC D8 EE 37 E0 DE ...........
然后我使用下面的代码来确定框架
public static bool isH264iFrame(byte[] paket)
{
int RTPHeaderBytes = 0;
int fragment_type = paket[RTPHeaderBytes + 0] & 0x1F;
int nal_type = paket[RTPHeaderBytes + 1] & 0x1F;
int start_bit = paket[RTPHeaderBytes + 1] & 0x80;
if (((fragment_type == 28 || fragment_type == 29) && nal_type == 5 && start_bit == 128) || fragment_type == 5)
{
return true;
}
return false;
}
我的问题是我无法完全知道RTPHeaderByte
的价值。在这种情况下,我的数据包始终以“00 00 00 01”开头。
由于 Tien Vo
答案 0 :(得分:3)
您必须解析有效负载。看到SO回答Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream。对于IDR,所有VCL NALU都是类型5.对于B / P,您需要解析exp-golmb编码数据以找到切片类型。
答案 1 :(得分:0)
实际上,这看起来不对:
int fragment_type = paket[RTPHeaderBytes + 0] & 0x1F;
int nal_type = paket[RTPHeaderBytes + 1] & 0x1F;
int start_bit = paket[RTPHeaderBytes + 1] & 0x80;
首先是 NAL 类型,然后是其他内容,NAL 类型字节的第 7 位始终为 0。
事实上,您可以简单地搜索两个或三个零后跟一个 1,这就是 NAL 的标记。 NAL紧随其后。我目前不清楚 2 个零和 3 个零之间有什么区别。
因此,在您的示例中,您有以下 NAL:
00 00 00 01 67 4D 00 1F : 95 A8 14 01 6E 40 00 00
^^ ^^ ^^ ^^ ^^ ^^ ^^
00 01 68 EE 3C 80 00 00 : 00 01 06 E5 01 33 80 00
^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^
00 00 01 65 B8 00 00 08 : 52 90 9F F6 BE D6 C6 9C
^^ ^^ ^^ ^^
3D F6 D4 2F 49 FB F7 13 : F2 A9 C7 27 2D A4 75 59
.. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..
所以这意味着你有 0x67、0x68、0x06、0x65,作为 per the link given by szatmary,你有(即我们做 type = (byte & 0x1F)
):
7 Sequence parameter set non-VCL
8 Picture parameter set non-VCL
6 Supplemental enhancement information (SEI) non-VCL
5 Coded slice of an IDR picture VCL
5 表示您有一个 I 帧。
查看我的一个文件,下一组 NAL 使用 0x41 或 0x01,这是非 IDR 图片的编码切片(即 B 帧)。偶尔,我会看到 5 而不是 1(即 I 帧)。默认情况下,x264 每 250 帧左右生成一个新的 I-Frame。您可以更改该参数。
因此,您的代码检测这组 NAL 是代表 I-Frame 还是另一个帧,需要搜索该帧内的所有 NAL,并找到 1(B-Frame)或 5(I-Frame)。
in.open("source-file.h264");
while(in)
{
char marker[4];
in.read(marker, 3);
for(;;)
{
in.read(marker + 3, 1);
if(marker[0] == 0
&& marker[1] == 0
&& marker[2] == 1)
{
// found one! (short one)
break;
}
if(marker[0] == 0
&& marker[1] == 0
&& marker[2] == 0
&& marker[3] == 1)
{
// found one! (long one)
break;
}
}
in.read(marker, 1);
type = marker[0] & 0x1F;
if(type == 1)
{
return B_FRAME;
}
if(type == 5)
{
return I_FRAME;
}
}
return NOT_FOUND;
警告:除非您的 in
文件中有一个好的辅助缓冲区,否则这段代码会很慢。这是 C++ 代码。如果缓冲区中已有数据,则应使用缓冲区中的指针或索引替换 in
文件,这肯定会非常快。
注意:如果 H.264 格式碰巧有 0x00 0x00 0x00 或 0x00 0x00 0x01 序列,请确保插入 3。也就是说,任何一个看起来都像这样:0x00 0x00 0x03 0x00 和 0x00 0x00 0x03 0x01。你可以尝试压缩纯黑框,你会看到很多那些0x03出现在NAL图片数据中。