第1年1月9日 librtmp发送h264

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

原文地址:https://www.cnblogs.com/javastart/p/14256631.html