第1年1月9日 librtmp发送h264

1.

流媒体之RTMP——librtmp拉流测试

		bool keyframe = 0x17 == packet.m_body[0] ? true : false;
		bool sequence = 0x00 == packet.m_body[1];


https://blog.csdn.net/yibu_refresh/article/details/102495831?spm=1001.2014.3001.5501

雷神的代码是解析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\n",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;
    

//		bool keyframe = 0x17 == packet.m_body[0] ? true : false;
//		bool sequence = 0x00 == packet.m_body[1];

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);
//		bool keyframe = 0x17 == packet.m_body[0] ? true : false;
//		bool sequence = 0x00 == packet.m_body[1];

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

 

posted @ 2021-01-09 21:51  lianhuaren  阅读(254)  评论(0编辑  收藏  举报