Android平台GB28181设备接入端如何实现多视频通道接入?

news2024/11/22 9:49:21

技术背景

我们在设计Android平台GB28181设备接入模块的时候,有这样的场景诉求,一个设备可能需要多个通道,常见的场景,比如车载终端,一台设备,可能需要接入多个摄像头,那么这台车载终端设备可以作为主设备,然后,主设备下,配置多个通道,听起来是不是有点儿类似于DVR或NVR?

技术实现

这里,我们说下,我们当时做这块,是怎么设计的,首先,在InitGB28181Agent的时候,添加设备通道,具体代码如下:

    private boolean initGB28181Agent() {
        if ( gb28181_agent_ != null )
            return  true;

        getLocation(context_);

        String local_ip_addr = IPAddrUtils.getIpAddress(context_);
        Log.i(TAG, "[daniusdk.com]initGB28181Agent local ip addr: " + local_ip_addr);

        if ( local_ip_addr == null || local_ip_addr.isEmpty() ) {
            Log.e(TAG, "[daniusdk.com]initGB28181Agent local ip is empty");
            return  false;
        }

        gb28181_agent_ = GBSIPAgentFactory.getInstance().create();
        if ( gb28181_agent_ == null ) {
            Log.e(TAG, "[daniusdk.com]initGB28181Agent create agent failed");
            return false;
        }

        gb28181_agent_.addListener(this);
        gb28181_agent_.addPlayListener(this);
        gb28181_agent_.addAudioBroadcastListener(this);
        gb28181_agent_.addDeviceControlListener(this);
        gb28181_agent_.addQueryCommandListener(this);

        // 必填信息
        gb28181_agent_.setLocalAddress(local_ip_addr);
        gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_domain_);
        gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_password_);
        //gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_username_, gb28181_sip_password_);

        // 可选参数
        gb28181_agent_.setUserAgent(gb28181_sip_user_agent_filed_);
        gb28181_agent_.setTransportProtocol(gb28181_sip_trans_protocol_==0?"UDP":"TCP");

        // GB28181配置
        gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);

        com.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
                    "宇宙","火星1","火星", true);

        if (mLongitude != null && mLatitude != null) {
            com.gb.ntsignalling.DevicePosition device_pos = new com.gb.ntsignalling.DevicePosition();

            device_pos.setTime(mLocationTime);
            device_pos.setLongitude(mLongitude);
            device_pos.setLatitude(mLatitude);
            gb_device.setPosition(device_pos);

            gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报
        }

        gb28181_agent_.addDevice(gb_device);

        com.gb28181.ntsignalling.Device gb_device1 = new com.gb28181.ntsignalling.Device("34020000001380000002", "安卓测试设备2", Build.MANUFACTURER, Build.MODEL,
                "宇宙","火星1","火星", true);

        if (mLongitude != null && mLatitude != null) {
            com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();

            device_pos.setTime(mLocationTime);
            device_pos.setLongitude(mLongitude);
            device_pos.setLatitude(mLatitude);
            gb_device1.setPosition(device_pos);

            gb_device1.setSupportMobilePosition(true);
        }

        gb28181_agent_.addDevice(gb_device1);

        if (!gb28181_agent_.createSipStack()) {
            gb28181_agent_ = null;
            Log.e(TAG, "[daniusdk.com]initGB28181Agent gb28181_agent_.createSipStack failed.");
            return  false;
        }

        boolean is_bind_local_port_ok = false;

        // 最多尝试5000个端口
        int try_end_port = gb28181_sip_local_port_base_ + 5000;
        try_end_port = try_end_port > 65536 ?65536: try_end_port;

        for (int i = gb28181_sip_local_port_base_; i < try_end_port; ++i) {
            if (gb28181_agent_.bindLocalPort(i)) {
                is_bind_local_port_ok = true;
                break;
            }
        }

        if (!is_bind_local_port_ok) {
            gb28181_agent_.releaseSipStack();
            gb28181_agent_ = null;
            Log.e(TAG, "[daniusdk.com]initGB28181Agent gb28181_agent_.bindLocalPort failed.");
            return  false;
        }

        if (!gb28181_agent_.initialize()) {
            gb28181_agent_.unBindLocalPort();
            gb28181_agent_.releaseSipStack();
            gb28181_agent_ = null;
            Log.e(TAG, "[daniusdk.com]initGB28181Agent gb28181_agent_.initialize failed.");
            return  false;
        }

        return true;
    }

可以看到,我们调用addDevice(gb_device);添加了两个通道,具体如下:

com.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
                                                                      "宇宙","火星1","火星", true);

if (mLongitude != null && mLatitude != null) {
    com.gb.ntsignalling.DevicePosition device_pos = new com.gb.ntsignalling.DevicePosition();

    device_pos.setTime(mLocationTime);
    device_pos.setLongitude(mLongitude);
    device_pos.setLatitude(mLatitude);
    gb_device.setPosition(device_pos);

    gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报
}

gb28181_agent_.addDevice(gb_device);

com.gb28181.ntsignalling.Device gb_device1 = new com.gb28181.ntsignalling.Device("34020000001380000002", "安卓测试设备2", Build.MANUFACTURER, Build.MODEL,
                                                                                 "宇宙","火星1","火星", true);

if (mLongitude != null && mLatitude != null) {
    com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();

    device_pos.setTime(mLocationTime);
    device_pos.setLongitude(mLongitude);
    device_pos.setLatitude(mLatitude);
    gb_device1.setPosition(device_pos);

    gb_device1.setSupportMobilePosition(true);
}

gb28181_agent_.addDevice(gb_device1);

一台设备,分别对应了两个device。

invite请求过来的时候,我们会把deviceid回上来,上层可以针对不同的deviceid做预览处理:

    @Override
    public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 先振铃响应下
                gb28181_agent_.respondPlayInvite(180, device_id_);

                MediaSessionDescription video_des = null;
                SDPRtpMapAttribute ps_rtpmap_attr = null;

                // 28181 视频使用PS打包
                Vector<MediaSessionDescription> video_des_list = session_des_.getVideoPSDescriptions();
                if (video_des_list != null && !video_des_list.isEmpty()) {
                    for(MediaSessionDescription m : video_des_list) {
                        if (m != null && m.isValidAddressType() && m.isHasAddress() ) {
                            video_des = m;
                            ps_rtpmap_attr = video_des.getPSRtpMapAttribute();
                            break;
                        }
                    }
                }

                if (null == video_des) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay get video description is null, response 488, device_id:" + device_id_);
                    return;
                }

                if (null == ps_rtpmap_attr) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay get ps rtp map attribute is null, response 488, device_id:" + device_id_);
                    return;
                }

                Log.i(TAG,"ntsOnInvitePlay, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
                        + " rtp_port:" + video_des.getPort() + " ss rc:" + video_des.getSS-RC()
                        + " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress());

                long rtp_sender_handle = libPublisher.CreateRTPSender(0);
                if ( rtp_sender_handle == 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    Log.i(TAG, "ntsOnInvitePlay CreateRTPSender failed, response 488, device_id:" + device_id_);
                    return;
                }

                gb28181_rtp_payload_type_  = ps_rtpmap_attr.getPayloadType();
                gb28181_rtp_encoding_name_ =  ps_rtpmap_attr.getEncodingName();

                libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1);
                libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1);
                libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);
                libPublisher.SetRTPSenderSS-RC(rtp_sender_handle, video_des.getSS-RC());
                libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2M
                libPublisher.SetRTPSenderClockRate(rtp_sender_handle, ps_rtpmap_attr.getClockRate());
                libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort());

                if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle);
                if (local_port == 0) {
                    gb28181_agent_.respondPlayInvite(488, device_id_);
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    return;
                }

                Log.i(TAG,"get local_port:" + local_port);

                String local_ip_addr = IPAddrUtils.getIpAddress(context_);

                MediaSessionDescription local_video_des = new MediaSessionDescription(video_des.getType());

                local_video_des.addFormat(String.valueOf(ps_rtpmap_attr.getPayloadType()));
                local_video_des.addRtpMapAttribute(ps_rtpmap_attr);

                local_video_des.setAddressType(video_des.getAddressType());
                local_video_des.setAddress(local_ip_addr);
                local_video_des.setPort(local_port);

                local_video_des.setTransportProtocol(video_des.getTransportProtocol());
                local_video_des.setSS-RC(video_des.getSS-RC());

                if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
                    libPublisher.DestoryRTPSender(rtp_sender_handle);
                    Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.");
                    return;
                }

                gb28181_rtp_sender_handle_ = rtp_sender_handle;
            }

            private String device_id_;
            private SessionDescription session_des_;

            public Runnable set(String device_id, SessionDescription session_des) {
                this.device_id_ = device_id;
                this.session_des_ = session_des;
                return this;
            }
        }.set(deviceId, session_des),0);
    }

收到ACK后,也携带了deviceid作为区分:

    @Override
    public void ntsOnAckPlay(String deviceId) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);

                if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                    InitAndSetConfig();
                }

                libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);

                //libPublisher.SetGBTCPConnectTimeout(publisherHandle, 10*60*1000);
                //libPublisher.SetGBInitialTCPReconnectInterval(publisherHandle, 1000);
                //libPublisher.SetGBInitialTCPMaxReconnectAttempts(publisherHandle, 3);

                int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);
                if (startRet != 0) {

                    if (!isRTSPPublisherRunning && !isPushingRtmp  && !isRecording) {
                        if (publisherHandle != 0) {
                            long handle = publisherHandle;
                            publisherHandle = 0;
                            libPublisher.SmartPublisherClose(handle);
                        }
                    }

                    destoryRTPSender();

                    Log.e(TAG, "Failed to start GB28181 service..");
                    return;
                }

                if (!isRTSPPublisherRunning && !isPushingRtmp && !isRecording) {
                    CheckInitAudioRecorder();
                }

                startLayerPostThread();
                isGB28181StreamRunning = true;
            }

            private String device_id_;

            public Runnable set(String device_id) {
                this.device_id_ = device_id;
                return this;
            }

        }.set(deviceId),0);
    }

TerminatePlayer处理如下:

    @Override
    public void ntsOnTerminatePlay(String deviceId) {
        handler_.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "ntsOnTerminatePlay, stop GB28181 media stream, deviceId=" + device_id_);

                stopGB28181Stream();
                destoryRTPSender();
            }

            private String device_id_;

            public Runnable set(String device_id) {
                this.device_id_ = device_id;
                return this;
            }

        }.set(deviceId),0);
    }

这样每个deviceid对应一个实例,如果有多个摄像头的话,摄像头,可以按照deviceid来做区分,国标平台侧请求哪个deviceid的话,就启动这个deviceid对应的camera采集、编码和数据打包上传。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/849324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口测试—Fiddler工具

文章目录 Fiddler 知识1. 导语2. 配置3. 常用命令面试题1. 利用Fiddler抓取android设备https请求 Fiddler 知识 未完待续 1. 导语 为什么要用Fiddler&#xff1f; 提高测试效率. 测试人员通过使用Fiddler自己调试接口&#xff0c;无需麻烦后台同学帮忙. 模拟多种测试环境. 可…

项目管理者联盟创始人宣晓锋受邀为第十二届中国PMO大会演讲嘉宾

项目管理者联盟创始人宣晓锋先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;项目集管理PgMP&#xff1a;升维项目管理认知与能力。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 从组织战略…

【C语言题解】将一句话的单词进行倒置,标点不倒置。

题目描述&#xff1a;将一句话的单词进行倒置&#xff0c;标点不倒置。比如 “I like beijing.”&#xff0c;经过处理后变为&#xff1a;“beijing. like I”。 文章目录 原题目题目描述&#xff1a;输入描述&#xff1a;输出描述&#xff1a;题目链接&#xff1a; 整体思路分…

rv1126移植并部署自写手写数字识别模型

首先搭建好rknntoolkit以及rknpu环境 --> MNIST->https://github.com/warren-wzw/MNIST-pytorch.git 大致流程 生成rknn文件----------------------------------------------------------------------------------------- 1&#xff1a;进入docker docker run -t -i…

【工具插件类教学】电脑端移动端缩放大图自适应Simple Zoom

目录 简介 1.创建Canvas并设置 2.使用预制体Zoom 3.商店地址 简介 特点: •易于使用和高度可定制。 •支持鼠标(桌面)和触摸(移动)。 •指定最小和最大缩放的限制。 •缩放指针(鼠标/手指)或屏幕上预定义的自定义位置。 •变焦时使用夹紧/弹性变焦类型。 •定义缩…

【科研人 学生党】AI辅助学术论文阅读

【适合于学生党和科研人】写文章往往需要找到好的研究点和方向&#xff0c;但是这并不是一件容易的事情&#xff0c;往往需要阅读大量的文献&#xff0c;阅读文献是一个耗时耗力的过程&#xff0c;而且你阅读了也不一定能提取出重要观点和信息。因此&#xff0c;借助GPT帮我们提…

深入理解 this

文章目录 1. 理解 this2. 为了进一步理解 this,我们再看一个案例3. this 的注意事项和使用细节 1. 理解 this 什么是 this&#xff1f; java虚拟机会给每一个对象分配 this&#xff0c;代表当前对象&#xff0c;坦白的讲&#xff0c;要明白 this不是件容易的事&#xff0c;打一…

容器安全的常见风险与防护实践

运行在云平台上的容器产品&#xff0c;因为具备一个完整的可移植应用程序环境&#xff0c;能够帮助用户轻松地完成对应用程序的开关控制&#xff0c;提升应用程序的敏捷性&#xff0c;同时节约企业的IT建设成本。在巨大优势作用下&#xff0c;容器产品的采用率在2021年达到了新…

爬虫来介绍ChromeF12 谷歌开发者工具 -Network

了解网页基础(HTML、CSS、JavaScript) 了解HTTP基本原理 了解JSON格式 了解Ajax请求 了解爬虫基本原理 (一)、Chrome开发者工具面板概述 Elements 查找网页源代码HTML中的任一元素,手动修改任一元素的属性和样式且能实时在浏览器里面得到反馈。 比如我们在Event Listener…

UML-A 卷-知识考卷

UML-A 卷-知识考卷 UML有多少种图&#xff0c;请列出每种图的名字&#xff1a; 常用的几种UML图&#xff1a; 类图&#xff08;Class Diagram&#xff09;&#xff1a;类图是描述类、接口、关联关系和继承关系的图形化表示。它展示了系统中各个类之间的静态结构和关系。时序…

WEB集群——负载均衡集群

目录 一、 LVS-DR 群集。 1、LVS-DR工作原理 2、LVS-DR模式的特点 3、部署LVS-DR集群 3.1 配置负载调度器&#xff08;192.168.186.100&#xff09; 3.2 第一台web节点服务器&#xff08;192.168.186.103&#xff09; 3.3 第二台web节点服务器&#xff08;192.168.186.…

高温老化房软件使用教程

高温老化炉软件通常具有以下几个模块&#xff1a; 1. 参数设置模块&#xff1a;该模块用于设置高温老化炉的相关参数&#xff0c;包括温度、时间、压力等。用户可以通过输入框、滑动条或下拉菜单等方式设定参数&#xff0c;并将参数发送给高温老化炉。 2. 监控模块&#xff1a;…

认识http的方法、Header、状态码以及简单实现一个http的业务逻辑

文章目录 http的方法http状态码http重定向http常见Header实现简单业务逻辑Protocol.hppUtil.hppServer.hppServer.cc 效果 http的方法 方法说明支持的HTTP版本GET获取资源1.0/1.1POST传输实体主体1.0/1.1PUT传输文件1.0/1.1HEAD获得报文首部1.0/1.1DELETE删除文件1.0/1.1OPTIO…

Centos7.6安装mysql8.0.20

安装前 1.安装mysql前&#xff0c;需将系统自带的mariadb卸载。 [rootk8s01 ~]#  rpm ‐qa|grep mariadbmariadb‐libs.x86_643[rootk8s01 ~]#  rpm ‐e ‐‐nodeps mariadb‐libs.x86_643[rootk8s01 ~]#  rpm ‐qa|grep mariadb 2. 下载mysql8.0.20 [rootk8s-01…

【王树森】深度强化学习(DRL)课程笔记:P1 基本概念(含gym安装)

课程信息 课程主讲&#xff1a;王树森&#xff08;史蒂文斯理工学院计算机科学系的终身制助理教授&#xff09; 课程内容&#xff1a;基本概念、价值学习、策略学习、Actor-Critic方法、AlphaGo、Monte Carlo (蒙特卡洛) 课程资料&#xff1a;https://github.com/wangshusen/D…

【MATLAB第66期】#源码分享 | 基于MATLAB的PAWN全局敏感性分析模型(有条件参数和无条件参数)

【MATLAB第66期】#源码分享 | 基于MATLAB的PAWN全局敏感性分析模型&#xff08;有条件参数和无条件参数&#xff09; 文献参考 Pianosi, F., Wagener, T., 2015. A simple and efficient method for global sensitivity analysis based on cumulative distribution functions.…

python多线程及协程

目录 进程和线程 串行和并行 多线程编程 Thread类 创建线程参数 具体案例 继承Thread类 具体案例 线程池 具体案例 协程 协程的使用 协程函数写法 调用多个协程函数 main函数的写法 案例 进程和线程 进程&#xff1a;就是一个程序&#xff0c;运行在系统之上…

[每日习题]年终奖(动态规划) 迷宫问题(DFS+回溯)——牛客习题

hello,大家好&#xff0c;这里是bang___bang_&#xff0c;本篇记录2道牛客习题&#xff0c;年终奖&#xff08;简单&#xff09;&#xff0c;迷宫问题&#xff08;中等&#xff09;&#xff0c;如有需要&#xff0c;希望能有所帮助&#xff01; 目录 1️⃣年终奖 2️⃣迷宫问…

Linux学习-1

Linux学习-1 1.文件系统的常识 本文主要引用鸟哥的Linux私房菜 1.1 常见的标识介绍 > [-][rwx][r-x][r--] > 1 234 567 890 1 为&#xff1a;代表这个文件名为目录或文件&#xff0c;本例中为文件&#xff08;-&#xff09;&#xff1b; 234为&#xff1a;拥有者的权限…

MongoDB数据库操作及操作命令

目录 一、基础概念 二、安装mongod 三、命令交互数据库 &#xff08;1&#xff09;数据库命令 &#xff08;2&#xff09;集合命令 &#xff08;3&#xff09;文档命令 四、Mongoose &#xff08;1&#xff09;增加一条数据 &#xff08;2&#xff09;插入多个数据 &am…