最近在做关于rtmp直播推流的项目,本身对rtmp也是一窍不通的,关于连接封装方面的资料也是看了很多,软件实现还是没有什么头绪,在看了雷神的基于librtmp的例子后(https://blog.csdn.net/leixiaohua1020/article/details/42105049) 在大神的轮子上修改,增加了一些东西,简单实现了一个摄像头rtmp实时推流的功能。
在看这篇文章之前,我还是建议大家先去上文给的雷神的博文中看看,里面还有大神写的源代码,本文实现的推流功能在关于librtmp库(rtmp的初始化,连接及发送接口)的运用方面都是参照雷神的来写的,没有什么区别,关于这部分代码的详解我会另起一篇或几篇文章来写(有关RTMP的东西实在太多),这篇文章主要介绍的是我跟雷神写的不同的地方,看过雷神的例子应该都知道,雷神的例子是推一个H264的视频文件,而在直播中,码流数据大多时候是不会存在本地(摄像头本身可用内存就少)而是直接推送到流媒体服务器上,推完之后缓冲就会被清除,所以在关于码流数据的循环读取和拆分部分需要重写,主要是三个函数ReadFirstNaluFromBuf(),ReadNaluFromBuf(),FindNaluFromNextPacket()。
在分析了海思,RealTek等厂家的编码芯片出来的码流可以发现,视频开始的第一个数据包通常都由SPS,PPS,I帧构成,接下来的几个数据包大多都是P帧,然后又会有一个包含了SPS,PPS,I帧的数据包出现,这样往复循环。而RTMP是不能直接推h264码流的,必须把264码流封装成FLV格式才能推出去。所以,这个264的RTMP的主要实现思想就是,把每包数据以NALU为单位拆分开来,并对每一个NALU进行FLV格式封装,封装好后调用librtmp的发送接口发送出去。
- 循环读取,拆分码流数据示意图(三个函数运作流程图)
现在上代码
1.ReadFirstNaluFromBuf()分离码流包第一个Nalu,当头下标nalhead_pos为0时调用
int ReadFirstNaluFromBuf(NaluUnit *nalu,unsigned char *buf, unsigned int buf_size)
{
int naltail_pos=nalhead_pos;
if(buf_tmp != NULL){
free(buf_tmp);
}
buf_tmp=(unsigned char*)malloc(buf_size);
memset(buf_tmp,0,buf_size);
//Nalu头标志为00000001,头下标找到第一个Nalu头标志
while(nalhead_pos<buf_size)
{
if(buf[nalhead_pos++] == 0x00 &&
buf[nalhead_pos++] == 0x00)
{
if(buf[nalhead_pos++] == 0x00 &&
buf[nalhead_pos++] == 0x01 ){
goto gotnal_head;
}
else
continue;
}
else
continue;
gotnal_head:
//尾下标找到下一个Nalu头标志
naltail_pos = nalhead_pos;
while (naltail_pos<buf_size)
{
if(buf[naltail_pos++] == 0x00 &&
buf[naltail_pos++] == 0x00 )
{