一段从摄像头RTP流提取H264NAL流的代码


/*视频RTP负载数据的处理, 提取摄像头NAL信息*/
void RTPDecoder::DoH264Decode(LPVOID param)
{
    ASSERT(param);
    rtspLinker*linker = (rtspLinker*)param;
    RtpDecoderInfo* decoderStatusInfo = &(linker->m_VideoRtpDecoderStatus);

    RTP_FIX_HEADER* pkt = (RTP_FIX_HEADER*)m_data;

    pkt->sequence_number = ntohs(pkt->sequence_number);
    pkt->timestamp = ntohl(pkt->timestamp);
    pkt->ssrc = ntohl(pkt->ssrc);

    BYTE*  rtpPayloadStart = &m_data[sizeof(RTP_FIX_HEADER)+pkt->cc * 4];                    //skip fix-header

    if (pkt->x){
        RTP_EXT_HEADER* pkt_ext = (RTP_EXT_HEADER*)m_data;
        rtpPayloadStart += sizeof(RTP_FIX_HEADER)+pkt_ext->extDataCnt * 4;//skip ext-header
    }

    int nal_data_len = (&m_data[m_dataLen] - rtpPayloadStart);
    if (pkt->p){
        //负载长度去掉填充字节, 若填料比特被设置,则此包包含一到多个附加在末端的填充比特,填充比特不算作负载的一部分。填充的最后一个字节指明可以忽略多少个填充比特
        nal_data_len -= m_data[m_dataLen - 1]/*填充字节数量, 注意m_data[m_dataLen - 1]包含了m_data[m_dataLen - 1]字节本身*/;
    }


    bool  bRtpStampTimeChanged   =false; //时间标签发生变化
  
    if(0 == decoderStatusInfo->frame_dataLen) {
        decoderStatusInfo->preRtpStampTime = pkt->timestamp;
        decoderStatusInfo->preNalType = 0xFF;
         decoderStatusInfo->preRtpSN = pkt->sequence_number-1;
    }

    //TRACE("pretmsamp=%u,  curtmsamp = %u....................................\n\n", decoderStatusInfo->preRtpSN ,  pkt->sequence_number);    
    //TRACE("pretmsamp=%u,  curtmsamp = %u....................................\n\n", decoderStatusInfo->preRtpStampTime ,  pkt->timestamp);    

 //时间标签或者NAL变化,上一帧已经结束
    if (decoderStatusInfo->preRtpStampTime != pkt->timestamp)
    {
        bRtpStampTimeChanged = true;
        decoderStatusInfo->preRtpStampTime = pkt->timestamp; 
        //TRACE("bRtpStampTimeChanged= true\n");    
    }
    
    /*
      1-23  NAL单元  单个 NAL 单元包                这些值的H24保留使用的,RTP包头用到这些值,就是单一NAL单元模式
      24    STAP-A   单一时间的组合包
      25    STAP-B   单一时间的组合包
      26    MTAP16   多个时间的组合包
      27    MTAP24   多个时间的组合包
      28    FU-A     分片的单元                    h264仅用1-23,24以后的用在RTP H264负载类型头中    , FU-A类型使用最多
      29    FU-B     分片的单元
      30-31 没有定义
      */
    RTP_FU_INDICATOR* fu_indicator = (RTP_FU_INDICATOR*)rtpPayloadStart;
    RTP_FU_HEADER*    fu_header    = (RTP_FU_HEADER*)(rtpPayloadStart + 1);
    
    BYTE *nalstart = NULL; //nalstart指向000001, 如果是该NAL是贞的开始,则nalstart指向00 000001
    BYTE *temp = NULL;

    UINT8 preNalType = decoderStatusInfo->preNalType;
    bool  bNalTypeChanged =false; //NAL type发生变化
    if(fu_indicator->fu_type<24){
        //TRACE("preNalType=%x,  curNalType = %x....................................\n\n" , decoderStatusInfo->preNalType, fu_indicator->fu_type);    
        if(fu_indicator->fu_type != decoderStatusInfo->preNalType){
            //TRACE("bNalTypeChanged= true\n");

            bNalTypeChanged = true; //时间标签或者NAL变化,上一帧已经结束
            decoderStatusInfo->preNalType = fu_indicator->fu_type;
        }
    }
    else{
        //TRACE("preNalType=%x,  curNalType = %x....................................\n\n" , decoderStatusInfo->preNalType, fu_header->nalType);    
        if(fu_header->nalType != decoderStatusInfo->preNalType){
            //TRACE("bNalTypeChanged= true\n");
            bNalTypeChanged = true; //时间标签或者NAL变化,上一帧已经结束
            decoderStatusInfo->preNalType = fu_header->nalType;
        }
    }

    static FILE* fH264 = NULL;
    if (!fH264)    fopen_s(&fH264, "e:\\1080P.h264", "wb");
    ASSERT(fH264);


    //Slice种的三种编码模式:I_slice、P_slice、B_slice
    //贞类型变化, 前一帧数据肯定已经结束

    if(bNalTypeChanged||bRtpStampTimeChanged){
        if(decoderStatusInfo->frame_dataLen){
            TSPacker * tsPacker = new TSPacker(linker, decoderStatusInfo->frame_buf, decoderStatusInfo->frame_dataLen, pkt->timestamp);
            linker->m_rtpTsPackerThreadPool->PushJob(tsPacker, linker);

            TRACE("$$$$$$$$$$>>>>>>>>>>>>>>>>>========NAL[%d]===========LEN[%d]============xxxxxxxxxxxxxxxx=======...[%d]....[%d]\n",  decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, bNalTypeChanged, bRtpStampTimeChanged);
        //    if(preNalType == NALU_TYPE_PPS||preNalType == NALU_TYPE_SPS||preNalType == NALU_TYPE_IDR)
            //if(NALU_TYPE_IDR == preNalType){
            //    TRACE("$$$$$$$$$$>>>>>>>>>>>>>>>>>========NAL[%d]===========LEN[%d]============xxxxxxxxxxxxxxxx=======...[%d]....[%d]\n",  decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, bNalTypeChanged, bRtpStampTimeChanged);
            //}

            UINT8* p = (UINT8*)decoderStatusInfo->frame_buf;
            TRACE("%02x %02x %02x %02x %02x  \n", p[0],p[1],p[2],p[3],p[4]);

 //时间标签或者NAL变化,上一帧已经结束把上一帧NAL数据写入H264文件

            fwrite(decoderStatusInfo->frame_buf, 1, decoderStatusInfo->frame_dataLen, fH264);  //向文件写入一个完整的贞数据
            fflush(fH264);
            //fH264=NULL;
            decoderStatusInfo->frame_dataLen=0;
        }
    }

    if (fu_indicator->fu_type < 24){
        //经过跟踪, 大华的RTP流中包含了PPS和SPS(他们的 时间标签用的是后面IDR贞的时间标签, 时间标签间隔3600)
        //这种单一NAL分片包, 打包时去除 "00 00 01" 或 "00 00 00 01" 的开始码, 把其他数据封包的 RTP 包即可, 所以还原时, 自己加上000001 或者 00000001 , 对于pps和sps, 加上00 000001
        //TRACE("%d      %08x++++++++++++++++++++++++++  %d,%d,%02x      %02x %02x %02x %02x %02x %02x \n", nal_data_len, pkt->timestamp
        //    , fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_indicator->fu_type
        //    , ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6]); //这里打印信息证实了FU-A类型的包最多, 
        //TRACE("LINE#%d  =============================   spsGot[%d]\n", __LINE__, decoderStatusInfo->spsGot);
        //H264裸流文件的第一个NAL必须是SPS参数集合
        if(1 == decoderStatusInfo->spsGot  ||  NALU_TYPE_SPS == fu_indicator->fu_type)
        {
            if(0 == decoderStatusInfo->spsGot)
            {
                decoderStatusInfo->spsGot =1;
                TRACE("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pkt->sequence_number = %d\n", pkt->sequence_number);
            }

                //NAL数据增加前缀
                nalstart= temp= rtpPayloadStart - 4; //倒退4个字节,fu_indicator(相当于NALTYPE字节)前增加NAL前缀000001 或 00 000001
                nal_data_len += 4;
                //frame-start-flag
                *temp++ = 0x00; 
                //fill nal-start-code
                *temp++ = 0x00;
                *temp++ = 0x00;
                *temp++ = 0x01;
                //TRACE("LINE#%d  =============================   spsGot[%d]\n", __LINE__, decoderStatusInfo->spsGot);
                if(bNalTypeChanged||bRtpStampTimeChanged){
                    //增加NAL前缀00 000001

                    ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
                    memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart,  nal_data_len);
                    decoderStatusInfo->frame_dataLen += nal_data_len;
                    //TRACE("++++++++++++++++++++++++ addLen %d\n", decoderStatusInfo->frame_dataLen);
                }
                else{
                    //增加NAL前缀 000001
                    nalstart++;
                    nal_data_len--;

                    ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
                    memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart,  nal_data_len);
                    decoderStatusInfo->frame_dataLen += nal_data_len;
                    //TRACE("++++++++++++++++++++++++ addLen %d\n", decoderStatusInfo->frame_dataLen);
                }

                ASSERT(decoderStatusInfo->frame_dataLen);
        }

        //TRACE("LINE#%d..¥¥¥¥¥¥¥¥¥¥.....single nal_type[%d].........................%d, spsGot[%d]\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, decoderStatusInfo->spsGot);
        //if(0==decoderStatusInfo->frame_dataLen&&decoderStatusInfo->spsGot)
        //{
            //TRACE("LINE#%d..¥¥¥¥¥¥¥¥¥¥.....single nal_type[%d].........................%d, spsGot[%d]\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen, decoderStatusInfo->spsGot);
        //}
    }
    else if(decoderStatusInfo->spsGot){
        // I/P/B=  0x01/0x05 也就是说只判断naltype = 0x01/0x05是判断不出来I/P/B帧类型的,需要到slice层去判断用到“熵编码”具体的“熵编码”内容请看:“H.264官方中文版.pdf”.
        //经过抓包大华摄像头,当采用FU-A分片, 只存在一个分片,下1个NAL的fu-header的
        //    TRACE("%d       %08x++++++++++++++++++++++++++%d  %d,%d,%02x  s%d------e%d    %02x %02x %02x %02x %02x %02x\n", nal_data_len, pkt->timestamp
        //        , pkt->sequence_number, fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_header->nalType, fu_header->nalStart, fu_header->nalEnd
        //        , ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7]); //这里打印信息证实了FU-A类型的包最多
        //这里打印信息证实了FU-A类型的包最多
        //TRACE("++++++++++++++++++++++++++%d  %d,%d,%02x  s %d------%d\n", pkt->sequence_number, fu_indicator->forbidden_zero_bit, fu_indicator->nal_ref_idc, fu_header->nalType, fu_header->nalStart, fu_header->nalEnd); //这里打印信息证实了FU-A类型的包最多
        //打包时去除了 "00 00 01" 或 "00 00 00 01" 的开始码, 如果贞的第一个NAL, 在还原时添加前缀00 000001,  否则添加前缀 000001

        /*
        表示支持的封包模式.
        当 packetization-mode 的值为 0 时或不存在时, 必须使用单一 NALU 单元模式.
        当 packetization-mode 的值为 1 时必须使用非交错(non-interleaved)封包模式.   -------------------- NALU必须是严格按照解码顺序传输
        当 packetization-mode 的值为 2 时必须使用交错(interleaved)封包模式.

        “非交错模式”:NALU必须是严格按照解码顺序传输,也就是说,假设1s中连续的24帧分别标记为:frame1,frame2...,frame24,则传输必须严格按frame1,frame2...,frame24这个顺序传输。该模式可以分包(指应用层的分包,并非指传输层)。
        “交错模式”  :NALU可以不按照解码顺序传输,也就是说,假设1s中连续的24帧分别标记为:frame1,frame2...,frame24,则传输顺序可以是frame15,frame7,frame9...。该模式可以分包(指应用层的分包,并非指传输层)。
        */            

        if(fu_header->nalStart) //这是NAL的第一个RTP封包--需要增加NAL前缀
        {
            //重建NALHead
            NAL_HEADER* pNalHead = (NAL_HEADER*) fu_header;
            pNalHead->forbidden_zero_bit = fu_indicator->forbidden_zero_bit;
            pNalHead->nal_ref_idc        = fu_indicator->nal_ref_idc;

            //倒退3个字节,fu_header(相当于NALTYPE字节)前增加NAL前缀000001 或 00 000001
            nalstart= temp= rtpPayloadStart - 3;
            nal_data_len += 3;
            //frame-start-flag
            *temp++ = 0x00; 
            //fill nal-start-code
            *temp++ = 0x00;
            *temp++ = 0x00;
            *temp++ = 0x01;


            if(bNalTypeChanged||bRtpStampTimeChanged){
                //增加NAL前缀00 000001
                ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
                memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart,  nal_data_len);
                decoderStatusInfo->frame_dataLen += nal_data_len;

                //TRACE("LINE#%d......fua[%d].........s[%d]................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart,nal_data_len);
            }
            else{
                nalstart++;
                nal_data_len--;
                //增加NAL前缀000001
                ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
                memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart,  nal_data_len);
                decoderStatusInfo->frame_dataLen += nal_data_len;

                //TRACE("LINE#%d......fua[%d].........s[%d]................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart,nal_data_len);
            }
        }
        else if(decoderStatusInfo->frame_dataLen>0){ //fu_header->nalStart=0时, 如果decoderStatusInfo->frame_dataLen==0 可以肯定是出现RTP掉包了
            nalstart= rtpPayloadStart;
            nalstart     +=2;          //跳过 fu_indicator + fu_header字节, 后面就是NAL的数据
            nal_data_len -=2;
            ASSERT(decoderStatusInfo->frame_dataLen);
            ASSERT(decoderStatusInfo->frame_bufSize >(decoderStatusInfo->frame_dataLen + nal_data_len));
            memcpy(&decoderStatusInfo->frame_buf[decoderStatusInfo->frame_dataLen], nalstart, nal_data_len);
            decoderStatusInfo->frame_dataLen += nal_data_len;    

            //TRACE("LINE#%d.......fua[%d].......s[%d].................nal_data_len = %d\n", __LINE__, decoderStatusInfo->preNalType, fu_header->nalStart, nal_data_len);
        }
        
        ASSERT(decoderStatusInfo->frame_dataLen);
        //TRACE("LINE#%d..@@@@@@@@.....fua[%d].........................%d\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen);
    }

    decoderStatusInfo->preRtpSN++;
    if(decoderStatusInfo->preRtpSN != pkt->sequence_number){
        TRACE("LINE#%d..@@@@@@@@.....fua[%d].........................%d\n", __LINE__, decoderStatusInfo->preNalType, decoderStatusInfo->frame_dataLen);
    }    


    //工作结束,线程池任务销毁
    delete this;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值