RTP解析音视频帧
RTP解析H264、AAC负载
RTSP中音视频是通过RTP传输的,本文记录从RTP解析出H264、AAC的过程。
协议介绍可参考 https://blog.youkuaiyun.com/lostyears/article/details/51374997
拿到RTP数据后,先去除12字节RTP头部,然后进行下面处理。
解析H264
数据较大的H264包,需要进行RTP分片发送。
实现代码:
/*
* @par pBufIn 待解析RTP(不包含12字节头)
* nLenIn 载荷长度
* pBufOut 装载H264的buf(外部传入,分配空间不小于nLenIn)
* nLenOut 一帧H264数据长度,函数返回true时有效
*
* @return true 一帧结束
* false 分片未结束
*/
bool UnpackRtpH264(const UInt8 *pBufIn, const Int32 nLenIn, UInt8 *pBufOut, Int32& nLenOut)
{
bool bFinished = true;
do
{
nLenOut = 0;
const Int32 eFrameType = pBufIn[0] & 0x1F;
if (eFrameType >= 1 && eFrameType <= 23) //单一NAL单元
{
pBufOut[0] = 0x00;//添加H264四字节头部
pBufOut[1] = 0x00;
pBufOut[2] = 0x00;
pBufOut[3] = 0x01;
memcpy(pBufOut + 4, pBufIn, nLenIn);
nLenOut = nLenIn + 4;
}
else //分片NAL单元,由多个RTP包拼接成完整的NAL单元
{
bFinished = false;
if (pBufIn[1] & 0x80) // 分片Nal单元开始位
{
m_nH264FrmeSize = 0;
pBufOut[0] = 0x00;
pBufOut[1] = 0x00;
pBufOut[2] = 0x00;
pBufOut[3] = 0x01;
pBufOut[4] = ((pBufIn[0] & 0xe0)|(pBufIn[1] & 0x1f));//取pBufIn[0]的前3位 与 pBufIn[1]的后5位
memcpy(pBufOut + 5, pBufIn + 2, nLenIn - 2); //跳过分片RTP的前两字节
m_nH264FrmeSize = nLenIn + 5 - 2;
}
else //后续的Nal单元载荷
{
Assert(m_nH264FrmeSize + nLenIn - 2 <= MAX_FRAME_SISE);
memcpy(pBufOut + m_nH264FrmeSize, pBufIn + 2, nLenIn -2);//跳过分片RTP的前两字节
m_nH264FrmeSize += nLenIn -2;
if (pBufIn[1] & 0x40) // 分片Nal单元结束位
{
nLenOut = m_nH264FrmeSize;
m_nH264FrmeSize = 0;
bFinished = true;
}
}
}
}while (0);
return bFinished;
}
解析AAC
这里要注意,可能是一个RTP包含多个AAC帧,之前按网上找的RTP后直接负载1帧AAC,大部分场景没问题,后面有个输入源解析AAC后没声音,最后发现是一个RTP包含了多个AAC负载。解析协议规范最好还是花时间研读协议规范文档,网上找的博客介绍可能不够全面,导致部分场景失效。
实现代码如下: