国网B接口实现(QGDW1517.1)

        上篇GB28181实现已经将实现sip所需要用到的三方库,以及一些注意点描述了。由于B接口与281都是基于sip,实现方式差不多,这里就不再描述。可以将GB28181实现_LinuxZQ的博客-CSDN博客_gb28181 实现 文章sip部分作为补充学习。本篇仅描述难点部分,也就是验收的硬性要求,视频预览着重讲解

        因手上只有B接口2014的验收软件,所以本篇以2014标准进行讲解。2019协议也是增加了tcp支持,与281类型,简单扩展就可以实现

视频预览

        与281一致,同样是基于RTP流的传输,交互流程也一致,都是基于SIP INVITE+SDP。

 支持的RTP Payload如下

        这里的Payload值定义与281里面还是有区别的,B接口种的264为100,我们也是以这种方式向平台进行推流

        2014种的视频以udp为通道传输,所以先创建udp通道。

        

bool CRtpVideo::rtpTrans::setupUDP(int localPort, const std::string &remoteIp, int remotePort, int payloadType, uint32_t ssrc)
{
    RTPUDPv4TransmissionParams transParams;
    RTPSessionParams sessParams;
    sessParams.SetOwnTimestampUnit(1.0 / 90000.0);

    // sessParams.SetAcceptOwnPackets(true);
    sessParams.SetUsePredefinedSSRC(true);  //设置使用预先定义的SSRC
    sessParams.SetPredefinedSSRC(ssrc);
    transParams.SetPortbase(localPort);
    int ret = sess.Create(sessParams, &transParams);
    if (ret < 0)
    {
        errorf("setup rtp[%s:%d] failed, msg[%s]\n", remoteIp.c_str(), remotePort, RTPGetErrorString(ret).c_str());
        return false;
    }
    infof("payType is %d, ssrc[%u], remoteIP[%s], remotePort[%d]\n", payloadType, ssrc, remoteIp.c_str(), remotePort);
    sess.SetDefaultPayloadType(payloadType);//设置传输类型
    sess.SetDefaultMark(true);      //设置位
    sess.SetTimestampUnit(1.0 / 90000.0); //设置采样间隔
    sess.SetDefaultTimestampIncrement(3600);//设置时间戳增加间隔

    uint32_t destip;
    destip = inet_addr(remoteIp.c_str());

    if (destip == INADDR_NONE)
    {
        errorf("bad ip[%s]\n", remoteIp.c_str());
        return false;
    }
    destip = ntohl(destip);

    RTPIPv4Address addr(destip, remotePort);

    ret = sess.AddDestination(addr);
    if (ret < 0)
    {
        errorf("add dest[%s:%d] failed, masg[%s]\n", remoteIp.c_str(), remotePort, RTPGetErrorString(ret).c_str());
        return false;
    }
    H264 = payloadType;

    return true;
}

        通道搭建成功后,就可以着手推流了,由于仅是H264裸流推送,所以不需要额外的封装。但是考虑到MTU的大小,需要进行分包。协议也明确了,需要进行分包,参考multi-slice或者FU-A。我们选择FU-A的方式。这个实现,网上也是有很多代码,示例如下

/// 因264的流开头可能为00 00 00 01,也有可能为00 00 01,所以需要动态找到nalu头
int CRtpVideo::rtpTrans::findNalu(uint8_t *data)
{
    char code[3] = {0x0, 0x0, 0x1};
    char code1[4] = {0x0, 0x0, 0x0, 0x1};
    bool bFind = true;
    for (int i = 0; i < 3; i++)
    {
        if (data[i] != code[i])
        {
            bFind = false;
        }
    }
    if (bFind)
    {
        return 3;
    }
    bFind = true;
    for (int i = 0; i < 4; i++)
    {
        if (data[i] != code1[i])
        {
            bFind = false;
        }
    }
    if (bFind)
    {
        return 4;
    }
    return -1;
}

        

bool CRtpVideo::rtpTrans::sendNALU(uint8_t *data, int len)
{
    int posN = findNalu(data);
    if (posN == -1)
    {
        warnf("not valid h264\n");
        return false;
    }
    unsigned char *pSendbuf = &data[posN]; //发送数据指针
    len = len - posN;

    char sendbuf[1500];   //发送的数据缓冲
    memset(sendbuf, 0, 1500);

    int ret = 0;
    /// MAX_RTP_PKT_LENTH 此处定义为1360,小于这个值的直接发送
    if ( len <= MAX_RTP_PKT_LENGTH )
    {
        // infof("min size is %d\n", len);
        ret = sess.SendPacket(pSendbuf, len, H264, true, 3600);
        if (ret < 0)
        {
            errorf("send pkt failed, msg[%s]\n", RTPGetErrorString(ret).c_str());
            return false;
        }
    }
    else if(len > MAX_RTP_PKT_LENGTH)
    {
        //设置标志位Mark为0
        sess.SetDefaultMark(false);
        //printf("buflen = %d\n",buflen);
        //得到该需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送
        int k = 0, l = 0;
        len = len - 1;
        k = len / MAX_RTP_PKT_LENGTH;
        l = len % MAX_RTP_PKT_LENGTH;
        int t = 0; //用指示当前发送的是第几个分片RTP包

        char nalHeader = pSendbuf[0]; // NALU 头
        while( t < k || ( t == k && l > 0 ) )
        {
            if( (0 == t ) || ( t < k && 0 != t ) ) //第一包到最后包的前一包
            {
                sendbuf[0] = (nalHeader & 0x60) | 28;
                sendbuf[1] = (nalHeader & 0x1f);
                if ( 0 == t )
                {
                    sendbuf[1] |= 0x80;
                }
                memcpy(sendbuf + 2, &pSendbuf[t * MAX_RTP_PKT_LENGTH + 1], MAX_RTP_PKT_LENGTH);
                // infof("select size is %d\n", MAX_RTP_PKT_LENGTH + 2);
                /// 分包的时间戳不要增加
                ret = sess.SendPacket((void *)sendbuf, MAX_RTP_PKT_LENGTH + 2, H264, false, 0);
                if (ret < 0)
                {
                    errorf("send pkt failed, msg[%s]\n", RTPGetErrorString(ret).c_str());
                    return false;
                }
                t++;
                memset(sendbuf, 0, 1500);
            }
            //最后一包
            else if( ( k == t && l > 0 ) || ( t == (k - 1) && l == 0 ))
            {
                //设置标志位Mark为1
                sess.SetDefaultMark(true);

                int iSendLen;
                if ( l > 0)
                {
                    iSendLen = len - t * MAX_RTP_PKT_LENGTH;
                }
                else
                {
                    iSendLen = MAX_RTP_PKT_LENGTH;
                }

                sendbuf[0] = (nalHeader & 0x60) | 28;
                sendbuf[1] = (nalHeader & 0x1f);
                sendbuf[1] |= 0x40;

                memcpy(sendbuf + 2, &pSendbuf[t * MAX_RTP_PKT_LENGTH + 1], iSendLen);
                // infof("last size is %d\n", iSendLen + 2);
                /// 最后一包时间戳再增加
                ret = sess.SendPacket((void *)sendbuf, iSendLen + 2, H264, true, 3600);
                if (ret < 0)
                {
                    errorf("send pkt failed, msg[%s]\n", RTPGetErrorString(ret).c_str());
                    return false;
                }
                t++;
                memset(sendbuf, 0, 1500);
            }
        }
    }
    return true;
}

        基本上实现到此处,B接口的流已经可以推送了。264流的来源,通过ffmpeg的avpacket即可获取,代码如下

void CRtpVideo::process()
{
    AVPacket *packet = NULL;
    infof("process GB video thread enter\n");
    while (m_thread.looping())
    {
        if (!m_quePkt.recvMessage(packet, 1000))
        {
            continue;
        }
        if (m_bTrans)
        {
            if (m_flowType == "PS")
            {
                sendPS(packet);
            }
            else if (m_flowType == "H264")
            {
                sendH264(packet);
            }
        }
        av_packet_unref(packet);
        av_packet_free(&packet);
    }
}
void CRtpVideo::sendH264(AVPacket *&packet)
{
    if (m_bSendIframe)
    {
        if (packet->flags == AV_PKT_FLAG_KEY)
        {
            m_rtpTrans.sendNALU(packet->data, packet->size);
            m_bSendIframe = false;
            infof("send iframe\n");
        }
    }
    else
    {
        m_rtpTrans.sendNALU(packet->data, packet->size);
    }
}

        demo程序已经和281程序整合在一起,地址如下

        https://download.csdn.net/download/z5201314100/85271657

        没有积分可进行百度网盘下载,路径如下:

        链接:https://pan.baidu.com/s/1LbGs9MXVXXEIBNmfL_AkyQ 
        提取码:4cp5
        二次开发接口及程序免费运行license请联系微信HardAndBetter获取,或者加入QQ群586166104讨论。

        B接口平台测试软件ABDemo,下载地址如下

        https://download.csdn.net/download/z5201314100/85573201

        链接:https://pan.baidu.com/s/1NIg2imgrlKq5ftbKVaujYA 
        提取码:wnv2

        

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值