1.
雷神的代码是解析h264文件拿到sps,pps,然后判断每帧是否是关键帧,是关键帧就用librtmp先发送spspps,再发送视频帧。
unsigned int tick = 0; unsigned int tick_gap = 1000/metaData.nFrameRate; ReadOneNaluFromBuf(naluUnit,read_buffer); int bKeyframe = (naluUnit.type == 0x05) ? TRUE : FALSE; while(1) { if (bKeyframe) { SendVideoSpsPps(metaData.Pps,metaData.nPpsLen,metaData.Sps,metaData.nSpsLen); } if (SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick) <= 0) { break; } got_sps_pps: //if(naluUnit.size==8581) printf("NALU size:%8d ",naluUnit.size); last_update=RTMP_GetTime(); if(!ReadOneNaluFromBuf(naluUnit,read_buffer)) goto end; if(naluUnit.type == 0x07 || naluUnit.type == 0x08) goto got_sps_pps; bKeyframe = (naluUnit.type == 0x05) ? TRUE : FALSE; tick +=tick_gap; now=RTMP_GetTime(); // msleep(tick_gap-now+last_update); msleep(40); //msleep(40); } end: free(metaData.Sps); free(metaData.Pps);
/** * 发送RTMP数据包 * * @param nPacketType 数据类型 * @param data 存储数据内容 * @param size 数据大小 * @param nTimestamp 当前包的时间戳 * * @成功则返回 1 , 失败则返回一个小于0的数 */ int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp) { RTMPPacket* packet; /*分配包内存和初始化,len为包体长度*/ packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+size); memset(packet,0,RTMP_HEAD_SIZE); /*包体内存*/ packet->m_body = (char *)packet + RTMP_HEAD_SIZE; packet->m_nBodySize = size; memcpy(packet->m_body,data,size); packet->m_hasAbsTimestamp = 0; packet->m_packetType = nPacketType; /*此处为类型有两种一种是音频,一种是视频*/ packet->m_nInfoField2 = m_pRtmp->m_stream_id; packet->m_nChannel = 0x04; packet->m_headerType = RTMP_PACKET_SIZE_LARGE; if (RTMP_PACKET_TYPE_AUDIO ==nPacketType && size !=4) { packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM; } packet->m_nTimeStamp = nTimestamp; /*发送*/ int nRet =0; if (RTMP_IsConnected(m_pRtmp)) { nRet = RTMP_SendPacket(m_pRtmp,packet,TRUE); /*TRUE为放进发送队列,FALSE是不放进发送队列,直接发送*/ } /*释放内存*/ free(packet); return nRet; } /** * 发送视频的sps和pps信息 * * @param pps 存储视频的pps信息 * @param pps_len 视频的pps信息长度 * @param sps 存储视频的pps信息 * @param sps_len 视频的sps信息长度 * * @成功则返回 1 , 失败则返回0 */ int SendVideoSpsPps(unsigned char *pps,int pps_len,unsigned char * sps,int sps_len) { static unsigned char data[1024] = {0}; unsigned char * body=data;//NULL; // RTMPPacket * packet=NULL;//rtmp包结构 // packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+1024); // //RTMPPacket_Reset(packet);//重置packet状态 // memset(packet,0,RTMP_HEAD_SIZE+1024); // packet->m_body = (char *)packet + RTMP_HEAD_SIZE; // body = (unsigned char *)packet->m_body; int i; i = 0; body[i++] = 0x17; body[i++] = 0x00; body[i++] = 0x00; body[i++] = 0x00; body[i++] = 0x00; /*AVCDecoderConfigurationRecord*/ body[i++] = 0x01; body[i++] = sps[1]; body[i++] = sps[2]; body[i++] = sps[3]; body[i++] = 0xff; /*sps*/ body[i++] = 0xe1; body[i++] = (sps_len >> 8) & 0xff; body[i++] = sps_len & 0xff; memcpy(&body[i],sps,sps_len); i += sps_len; /*pps*/ body[i++] = 0x01; body[i++] = (pps_len >> 8) & 0xff; body[i++] = (pps_len) & 0xff; memcpy(&body[i],pps,pps_len); i += pps_len; int bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i,0); return bRet; // RTMPPacket * packet=NULL;//rtmp包结构 // packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE+1024); // //RTMPPacket_Reset(packet);//重置packet状态 // memset(packet,0,RTMP_HEAD_SIZE+1024); // packet->m_body = (char *)packet + RTMP_HEAD_SIZE; // memcpy(packet->m_body,body,i); // // packet->m_packetType = RTMP_PACKET_TYPE_VIDEO; // packet->m_nBodySize = i; // packet->m_nChannel = 0x04; // packet->m_nTimeStamp = 0; // packet->m_hasAbsTimestamp = 0; // packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM; // packet->m_nInfoField2 = m_pRtmp->m_stream_id; // // /*调用发送接口*/ // int nRet = RTMP_SendPacket(m_pRtmp,packet,TRUE); // free(packet); //释放内存 // return nRet; } /** * 发送H264数据帧 * * @param data 存储数据帧内容 * @param size 数据帧的大小 * @param bIsKeyFrame 记录该帧是否为关键帧 * @param nTimeStamp 当前帧的时间戳 * * @成功则返回 1 , 失败则返回0 */ int SendH264Packet(unsigned char *data,unsigned int size,int bIsKeyFrame,unsigned int nTimeStamp) { if(data == NULL && size<11){ return false; } unsigned char *body = (unsigned char*)malloc(size+9); memset(body,0,size+9); int i = 0; if(bIsKeyFrame){ body[i++] = 0x17;// 1:Iframe 7:AVC // SendVideoSpsPps(metaData.Pps,metaData.nPpsLen,metaData.Sps,metaData.nSpsLen); }else{ body[i++] = 0x27;// 2:Pframe 7:AVC } body[i++] = 0x01;// AVC NALU body[i++] = 0x00; body[i++] = 0x00; body[i++] = 0x00; // NALU size body[i++] = size>>24 &0xff; body[i++] = size>>16 &0xff; body[i++] = size>>8 &0xff; body[i++] = size&0xff; // NALU data memcpy(&body[i],data,size); int bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp); free(body); return bRet; }
https://blog.csdn.net/leixiaohua1020/article/details/42105049
https://www.cnblogs.com/babosa/p/6032987.html
https://my.oschina.net/jerikc/blog/501948
2.LFLiveKit
判断数据类型,如果是视频数据。还会再判断是否发送视频头信息如果没有则首先发送视频头信息,如果已经发送头信息则直接发送视频信息。同理音频数据也是如此。
这里值得注意的是虽然我们的每一帧音视频数据中都包含了头信息,但是发送过程中却值发送了一次。
这里值得注意的是虽然我们的每一帧音视频数据中都包含了头信息,但是发送过程中却值发送了一次。
发送音视频数据过程回掉用如下四个方法。
[_self sendVideoHeader:(LFVideoFrame *)frame];
[_self sendVideo:(LFVideoFrame *)frame];
[_self sendAudioHeader:(LFAudioFrame *)frame];
[_self sendAudio:frame];
这四个方法最终都会掉用下面这个方法实际发送数据。
- (NSInteger)sendPacket:(unsigned int)nPacketType data:(unsigned char *)data size:(NSInteger)size nTimestamp:(uint64_t)nTimestamp {
NSInteger rtmpLength = size;
PILI_RTMPPacket rtmp_pack;
PILI_RTMPPacket_Reset(&rtmp_pack);
PILI_RTMPPacket_Alloc(&rtmp_pack, (uint32_t)rtmpLength);
rtmp_pack.m_nBodySize = (uint32_t)size;
memcpy(rtmp_pack.m_body, data, size);
rtmp_pack.m_hasAbsTimestamp = 0;
rtmp_pack.m_packetType = nPacketType;
if (_rtmp) rtmp_pack.m_nInfoField2 = _rtmp->m_stream_id;
rtmp_pack.m_nChannel = 0x04;
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_LARGE;
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size != 4) {
rtmp_pack.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
rtmp_pack.m_nTimeStamp = (uint32_t)nTimestamp;
NSInteger nRet = [self RtmpPacketSend:&rtmp_pack];
PILI_RTMPPacket_Free(&rtmp_pack);
return nRet;
}
可以看到该方法改方法中大量掉用rmtp.c文件中的方法来完成最终数据发送。
- (NSInteger)RtmpPacketSend:(PILI_RTMPPacket *)packet {
if (_rtmp && PILI_RTMP_IsConnected(_rtmp)) {
int success = PILI_RTMP_SendPacket(_rtmp, packet, 0, &_error);
return success;
}
return -1;
}
注意: 建立连接发送数据的过程中都创建了线程,在子线程中完成。
https://www.jianshu.com/p/4dd2009b0902