GB/T28181平台C++实现学习笔记5: 使用eXosip获取国标摄像头的媒体流

GB/T28181平台C++实现学习笔记5: 使用eXosip获取国标摄像头的媒体流

获取国标媒体流的流程

在这里插入图片描述
GB/T 28181-2016 中有两种方式,分别是客户端主动发起和第三方呼叫控制,但是作为SIP服务器,和摄像头交互的流程其实是一样的。

开启媒体流

1. SIP服务器 ---> Invite(sdp) ---> 媒体流发送者
2. 媒体流发送者 <--- 200(sdp) <--- SIP服务器
3. SIP服务器 ---> Ack ---> 媒体流发送者
4. 媒体流发送者 <--- 数据流(rtp) <--- SIP服务器

停止媒体流

1. SIP服务器 ---> Bye ---> 媒体流发送者
2. 媒体流发送者 <--- 200 <--- SIP服务器

开启获取国标流任务线程

同样使用任务池扫描任务,自动获取任务。

void TaskMgr::start()
{
    stl::ThreadTask recvTask = std::bind(&TaskMgr::DoScanTasks, this);
    stl::ThreadPool::instance()->pushTask(recvTask);
}
int TaskMgr::DoScanTasks()
{
    static int dump = 0;
    if (dump++ < 10)
    {
        return 0;
    }
    dump = 0;

    // stl::debug("DoScanTasks");

    std::map<int, DBMgr::TaskStatus> tasks = DBMgr::instance()->getTaskList();
    std::map<int, DBMgr::TaskStatus>::iterator it;
    for (it = tasks.begin(); it != tasks.end(); it++)
    {
        if (it->second == DBMgr::TaskStatus::NEW)
        {
            int deviceId = it->first;
            stl::debug("Start Task[%d]", deviceId);
            DBMgr::instance()->setTask(deviceId, DBMgr::TaskStatus::WORK);
            auto deviceInfo = DBMgr::instance()->getDevice(deviceId);
            send_invite_sdp(g_excontext, deviceInfo);
        }
    }
}

发送Invite

  int TaskMgr::send_invite_sdp(eXosip_t *context_eXosip, DBMgr::Device deviceInfo)
{
    std::string channel_code  = "41010200001310000001";// TODO 通过查询获取<CmdType>Catalog</CmdType>

    int ret = 0;
    osip_message_t *invitesip = NULL;

    // std::string from = g_sip_from_ipport;
    std::string from = g_sip_from_domain;
    std::string to = "sip:" + channel_code + "@" + deviceInfo.host + ":" + std::to_string(deviceInfo.port);

    eXosip_lock(context_eXosip);
    ret = eXosip_call_build_initial_invite(context_eXosip, &invitesip, to.c_str(), from.c_str(), NULL, "");
    if (ret < 0)
    {
        stl::error("(%t)%D [GB28181ServerTask] send_invite_sdp eXosip_call_build_initial_invite fail,from:%s,to:%s", from.c_str(), to.c_str());
        return -1;
    }
    ret = eXosip_call_send_initial_invite(context_eXosip, invitesip);
    if (ret < 0)
    {
        stl::error("(%t)%D [GB28181ServerTask] send_invite eXosip_call_send_initial_invite fail,from:%s,to:%s", from.c_str(), to.c_str());
        return -1;
    }

    {
        std::string sdpcontent = SDPHandler::generateSDP(g_local_sip_cfg_svr_id, g_local_sip_cfg_svr_ip, 13398, "0102000001");
        osip_message_set_body(invitesip, sdpcontent.c_str(), sdpcontent.length());
        osip_message_set_content_type(invitesip, "application/sdp");
    }

    ret = eXosip_call_send_initial_invite(context_eXosip, invitesip);
    eXosip_unlock(context_eXosip);
    if (ret < 0)
    {
        stl::error("(%t)%D [GB28181ServerTask] send_invite_sdp eXosip_call_send_initial_invite fail,from:%s,to:%s", from.c_str(), to.c_str());
        return -1;
    }

    stl::info("(%t)%D [GB28181ServerTask] send_invite_sdp from:%s,to:%s\n", from.c_str(), to.c_str());
    return ret;
}

SDP的生成

关于SDP的生成,本来是想使用osip库来生成,但是接口不满足我的需求,暂时懒得改源码,没有搞定,直接自己手工拼的字符串。

std::string SDPHandler::generateSDP(std::string username,std::string rtpIp,uint16_t rtpPort,std::string ssrc)
{
    return "v=0\r\no="+username+" 0 0 IN IP4 "+rtpIp+"\r\ns=Play\r\nc=IN IP4 "+rtpIp+"\r\nt=0 0\r\nm=video "+std::to_string(rtpPort)+" RTP/AVP 96 97 98\r\na=recvonly\r\na=rtpmap:96 PS/90000\r\na=rtpmap:97 MPEG4/90000\r\na=rtpmap:98 H264/90000\r\ny="+ssrc+"\r\n";
}

不过还是粘出来我使用osip库的代码,有高手请指点(微信号:yjkhtddx),测试时信息写死的,没有使用变量。

int SDPHandler::AssembleSdp(std::string &sdp)
{
    // int SDPHandler::AssembleSdp(std::string &sdp, const sdp_description_t &sdp_desc)
    sdp_message_t *osip_sdp;
    sdp_message_init(&osip_sdp);

    sdp_message_v_version_set(osip_sdp, osip_strdup("0"));
    osip_sdp->o_username = osip_strdup("34020000002000000001");
    osip_sdp->o_sess_id = osip_strdup("0");
    osip_sdp->o_sess_version = osip_strdup("0");
    osip_sdp->o_nettype = osip_strdup("IN");
    osip_sdp->o_addrtype = osip_strdup("IP4");
    osip_sdp->o_addr = osip_strdup("10.0.0.42");
    sdp_message_c_connection_add(osip_sdp, -1, osip_strdup("IN"), osip_strdup("IP4"), osip_strdup("10.0.0.42"), nullptr, nullptr);
    sdp_message_s_name_set(osip_sdp, osip_strdup("Play"));
    sdp_message_t_time_descr_add(osip_sdp, osip_strdup("0"), osip_strdup("0"));
    sdp_message_m_media_add(osip_sdp, osip_strdup("video"), osip_strdup("30016"), osip_strdup("96 97 98"), osip_strdup("RTP/AVP"));
    sdp_message_a_attribute_add(osip_sdp, -1, osip_strdup("recvonly"), nullptr);
    sdp_message_a_attribute_add(osip_sdp, -1, osip_strdup("rtpmap"), osip_strdup("96 PS/90000"));
    sdp_message_a_attribute_add(osip_sdp, -1, osip_strdup("rtpmap"), osip_strdup("97 MPEG4/90000"));
    sdp_message_a_attribute_add(osip_sdp, -1, osip_strdup("rtpmap"), osip_strdup("98 H264/90000"));
    char *sdpPtr = nullptr;
    int ret = sdp_message_to_str(osip_sdp, &sdpPtr);
    if (sdpPtr)
    {
        sdp = std::string(sdpPtr);
        osip_free(sdpPtr);
    }
    stl::debug("[%d]>>>\n%s\n<<<", ret, sdp.c_str());
    sdp_message_free(osip_sdp);
    stl::debug(">>>忒不好用了<<<");
    return 0;
}

发送ACK

当国标摄像头返回200时,会触发EXOSIP_CALL_ANSWERED事件,事件处理直接返回ACK就好

int HandlerCall::HandleResponseSuccess(const sip_event_sptr &e)
{

    stl::debug("CALL RES status_code:%d",e->exevent->response->status_code);
    if(e->exevent->response->status_code != 200) return 0;

    osip_message_t *ack = NULL;
    eXosip_lock(e->excontext);
    eXosip_call_build_ack(e->excontext,e->exevent->tid,&ack);
    eXosip_call_send_ack(e->excontext,e->exevent->tid,ack);
    eXosip_unlock(e->excontext);

    stl::log("send CALL ACK");
    return 0;
}

测试

使用命令监听rtp的端口

nc -lu 30016

满屏乱码就对了,下篇使用jrtplib接收数据。

微信号:yjkhtddx

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值