一、前言
本文旨在将h264裸流数据分解成一个个NALU单元,以便后续对每一个NALU单元进行RTP封包将其发送出去。
二、实现-解析H264裸流数据
h264裸流数据中一般以“00 00 00 01”或者“00 00 01”来分隔NALU单元,所有要做的就是识别到这两种头,将两个头之间的所需要的NALU数据提取出来。直接上代码。
typedef struct
{
unsigned len;
char *buf;
} MY_NALU_t;
/*
从h264裸流数据中提取出NALU单元数据
srcStr:裸流数据
srcStrLen: 裸流数据长度
nalu:存放所有的nalu单元
*/
int getAllNalu(char* srcStr, int srcStrLen,MY_NALU_t nalu[])
{
int srcIndex = 0;
int lastNaluPos = -1;
int naluLen = 0;
int naluCount = 0;
int maxLen = 0;
int lastNaluStartType = 0;
static BYTE byHeadLong[] = {
0x00, 0x00, 0x00, 0x01 };
static BYTE byHeadShort[] = {
0x00, 0x00, 0x01 };
if (!srcStr || !srcStrLen) return -1;
qDebug("srcLen: %d", srcStrLen);
while (srcStrLen)
{
if (memcmp(srcStr + srcIndex, byHeadShort, sizeof(byHeadShort)) == 0)
{
if (lastNaluPos != -1)
{
// 之前找到过一个开头,那么这一次再次找到,则上一个完整nalu长度为 srcIndex-lastNaluPos
naluLen = srcIndex - lastNaluPos;
if (naluLen > maxLen)
{
maxLen = naluLen;
}
if (lastNaluStartType == 1)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadShort)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadShort)];
nalu[naluCount].len = naluLen - sizeof(byHeadShort); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
else if (lastNaluStartType == 2)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadLong)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadLong)];
nalu[naluCount].len = naluLen - sizeof(byHeadLong); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
}
lastNaluPos = srcIndex;
lastNaluStartType = 1;
srcStrLen -= sizeof(byHeadShort);
srcIndex += sizeof(byHeadShort);
}
else if (memcmp(srcStr + srcIndex, byHeadLong, sizeof(byHeadLong)) == 0)
{
if (lastNaluPos != -1)
{
// 之前找到过一个开头,那么这一次再次找到,则上一个完整nalu长度为 srcIndex-lastNaluPos
naluLen = srcIndex - lastNaluPos;
if (naluLen > maxLen)
{
maxLen = naluLen;
}
if (lastNaluStartType == 1)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadShort)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadShort)];
nalu[naluCount].len = naluLen - sizeof(byHeadShort); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
else if (lastNaluStartType == 2)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadLong)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadLong)];
nalu[naluCount].len = naluLen - sizeof(byHeadLong); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
}
lastNaluPos = srcIndex;
lastNaluStartType = 2;
srcStrLen -= sizeof(byHeadLong);
srcIndex += sizeof(byHeadLong);
}
else
{
srcStrLen--;
srcIndex++;
}
if (!srcStrLen)
{
if (lastNaluPos != -1)
{
// 之前找到过一个开头,那么这一次再次找到,则上一个完整nalu长度为 srcIndex-lastNaluPos
naluLen = srcIndex - lastNaluPos;
if (naluLen > maxLen)
{
maxLen = naluLen;
}
if (lastNaluStartType == 1)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadShort)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadShort)];
nalu[naluCount].len = naluLen - sizeof(byHeadShort); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
else if (lastNaluStartType == 2)
{
// qDebug("find %d nalu type: %d,len: %d", naluCount, srcStr[lastNaluPos + sizeof(byHeadLong)] & 0x1f, naluLen);
nalu[naluCount].buf = &srcStr[lastNaluPos + sizeof(byHeadLong)];
nalu[naluCount].len = naluLen - sizeof(byHeadLong); // 不要起始头
qDebug("find %d nalu type: %d,len: %d", naluCount, nalu[naluCount].buf[0] & 0x1f, nalu[naluCount].len);
naluCount++;
}
}
}
}
qDebug("max nalu len: %d", maxLen);
return naluCount;
}
三、测试-使用getAllNalu接口
int naluNum = 0;
MY_NALU_t nalu[10]; // 一帧数据里面包含的nalu数量应该不会太多,最多不会超过10个。
// 一帧数据中可能有多个nalu单元,需要将其识别出来一个一个封包处理,根据00 00 00 01或者00 00 01起始码识别
naluNum = getAllNalu(databuff, len,nalu);
for(int i=0;i<naluNum;i++)
{
// 对每一个nalu单元处理
qDebug(" %d nalu type: %d,len: %d nalu head: 0x%x", i, nalu[i].buf[0] & 0x1f, nalu[i].len,nalu[i].buf[0]);
// 对每个nalu单元进行rtp封包处理。
// package_rtp(nalu[i].buf,nalu[i].len,transport);
}