zoukankan      html  css  js  c++  java
  • 第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

  • 相关阅读:
    SpringBoot入门
    Maven相关
    IO流之RandomAccessFile和File
    javaSE之运行时异常和编译时异常
    Map.Entry的由来和使用
    Oracle常用基础语法(未完待补和操作)
    Oracle存储过程和存储函数
    论文阅读笔记(三十七)【AAAI2020】:Frame-Guided Region-Aligned Representation for Video Person Re-identification
    论文阅读笔记(三十六)【AAAI2020】:Relation-Guided Spatial Attention and Temporal Refinement for Video-based Person Re-Identification
    论文阅读笔记(三十五)【CVPR2019】:Perceive Where to Focus: Learning Visibility-aware Part-level Features for Partial Person Re-identification
  • 原文地址:https://www.cnblogs.com/javastart/p/14256631.html
Copyright © 2011-2022 走看看