Android前端音视频数据接入GB28181平台意义

技术背景

在华脉智联研发Android平台GB28181前端音视频接入模块之前,业内听到最多的是,如何用Android端在没有国标摄像头设备的前提下,模拟GB28181的信令和媒体流交互流程,实现GB28181整体方案的测试。

Android端真的没有必要做个支持GB28181的接入模块?

如果说做一个设备端摄像头国标设备接入模拟模块是完成从0到1的工作,那么从设备端模拟摄像头到一个可以产品化的Android平台GB28181前端音视频接入模块,需要更严谨更符合相关spec的方式,实现不具备国标音视频能力的Android终端,通过平台注册的形式,接入到现有的GB28181服务,最终用于如智能监控、智慧零售、智慧教育、远程办公、生产运输、智慧交通、车载或执法记录仪等场景,可以说应用场景非常广泛。

除了支持常规的音视频媒体流数据接入外,还可以支持Subscribe订阅实时位置(MobilePosition)、实时目录查询等,完成标准服务的对接。产品设计方面,媒体流支持最新GB28181-2016的UDP和TCP被动模式,参数配置,支持注册有效期、心跳间隔、心跳间隔次数、TCP/UDP信令设置,支持RTP Sender IP地址类型、RTP Socket本地端口、SS-R-C、RTP socket 发送Buffer大小、RTP时间戳时钟频率设置,支持注册成功、注册超时、INVITE、ACK、BYE状态回调。

设计思路

信令设计和媒体数据传输分离,上层实现国标GB28181的注册、注销、CATALOG、INVITE、ACK、BYE、SUBSCRIBE等交互处理,如注册成功后,返回注册时间,并检测传输或心跳等异常状态,服务端发送catalog请求后,组织本地catalog信息,并以message的形式发送到服务端,服务端收到相关信息后,开始发送invite请求,客户端解析INVITE返回的SDP信息,组织相关的response,创建RTP Sender,根据返回的信息,设定相关参数。待收到服务端的Ack后,发送编码、打包后的媒体流数据。在此期间,按照设定间隔,定时发送keepalive。

模块除了常规的音视频参数配置外,系统可同时亦或单独实现如RTMP推送、RTSP推送、轻量级RTSP服务、实时录像、GB28181前端接入。

信令接口设计:

 /**
 * init gb28181
 *
 * @param server_ip          server ip
 * @param port               server port
 * @param server_id          server id
 * @param server_domain      server domain
 * @param device_id          device id
 * @param device_pwd         password
 * @param device_name        device name
 * @param tcpudp             0 - udp; 1 - tcp
 * @param heartbeat_interval gb28181 heartbeat interval, unit is second
 * @param reg_expire         sip reg user expires, unit is second
 * @return
 */
public boolean init(String server_ip, int port, String server_id, String server_domain, String device_id,
                    String device_pwd, String device_name,
                    int tcpudp, int heartbeat_interval, int reg_expire) {
}


/**
 * init gb28181
 *
 * @param server_ip          server ip
 * @param port               server port
 * @param server_id          server id
 * @param server_domain      server domain
 * @param device_id          device id
 * @param device_pwd         password
 * @param device_name        device name
 * @param tcpudp             0 - udp; 1 - tcp
 * @param heartbeat_interval gb28181 heartbeat interval, unit is second
 * @param reg_expire         sip reg user expires, unit is second
 * @return
 */
public boolean init(String server_ip, int port, String server_id, String server_domain, String device_id,
                    String device_pwd, String device_name,
                    int tcpudp, int heartbeat_interval, int reg_expire) {


/**
 * update gb28181 config
 *
 * @param server_ip          server ip
 * @param port               server port
 * @param server_id          server id
 * @param server_domain      server domain
 * @param device_id          device id
 * @param device_pwd         password
 * @param device_name        device name
 * @param tcpudp             0 - udp; 1 - tcp
 * @param heartbeat_interval gb28181 heartbeat interval, unit is second
 * @param reg_expire         sip reg user expires, unit is second
 * @return
 */
public void updateConfig(String server_ip, int port, String server_id, String server_domain, String device_id,
                         String device_pwd, String device_name,
                         int tcpudp, int heartbeat_interval, int reg_expire) {
 }

相关状态回调:

gbEngine.addEventHandler(handler: IEngineEventHandler)

// 国标底层事件回调
private val engineEventHandler =
    IEngineEventHandler { type, state ->
        if (type == EventHandlerStatus.EventHandlerType.type_register) {
            when (state) {
                EventHandlerStatus.RegisterState.unregister -> { //反注册
                    logI("onState: id=${type.toCallTypeString()}, state=unregister($state)")
                }

                EventHandlerStatus.RegisterState.register_fail -> { //注册失败
                    logI("onState: id=${type.toCallTypeString()}, state=register_fail($state)")
                }

                EventHandlerStatus.RegisterState.register_success -> { //注册成功
                    logI("onState: id=${type.toCallTypeString()}, state=register_success($state)")
                }

                EventHandlerStatus.RegisterState.register_forbidden -> { //注册失败,udp/tcp协议不对、密码不对等注册参数不对
                    logI("onState: id=${type.toCallTypeString()}, state=register_forbidden($state)")
                }

                else -> {
                    logI("onState: id=${type.toCallTypeString()}, state=PUEVT_REG_PASS($state)")
                }
            }
        } else {
            logI("onState: id=${type.toCallTypeString()}, state=${state.toEventString()}")
        }

        if (type == EventHandlerStatus.EventHandlerType.type_call_in) { //呼入事件
            when (state) {
                EventHandlerStatus.EventState.PUEVT_CALL_IN -> { //视频监控呼入
                }

                EventHandlerStatus.EventState.PUEVT_CONNECT -> { //视频监控接通

                }

                EventHandlerStatus.EventState.PUEVT_HANGUP -> { //视频监控挂断
                }
            }
        }
    }

总结

Android平台GB28181音视频接入模块研发之前,华脉智联已经在RTSP、RTMP和音视频采集、编码传输等有了多年积累,GB28181接入,对我们来说,只是在现有架构的基础上,完成信令交互和数据打包传输(H264, H265打包成PS流,然后拆成RTP包发送即可),RTP传输支持TCP、UDP模式,配合国标28181服务器测试,延时非常低,设计支持多通道,可实现RTSP或RTMP流数据到GB28181的转换。为Android平台赋能,像支持GB28181协议的IPC一样,方便的把摄像头、屏幕、麦克风或外部RTSP、RTMP流,顺利接入到GB28181平台。

相关推荐
灰啦啦几秒前
HTML的块级元素与行内元素
前端·html
让开,我要吃人了14 分钟前
HarmonyOS开发实战( Beta5.0)蓝牙实现服务端和客户端通讯详解
开发语言·前端·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统
喵果森森14 分钟前
JavaScript控制语句和函数的使用
前端·javascript
秋沐33 分钟前
Vue3实现打印功能
前端·javascript·vue.js
大霞上仙34 分钟前
一个vue前端的例子(六)如何获取table一行的id
前端·javascript·vue.js
张小瑜39 分钟前
elementUI中el-form 嵌套el-from 如何进行表单校验?
前端·vue.js·elementui
盼盼盼1 小时前
react Refine 框架在性能优化方面有哪些具体的技术手段?
前端·react.js·性能优化
听潮阁1 小时前
【卷起来】VUE3.0教程-09-整合Element-plus
前端·javascript·vue.js·spring boot·spring cloud
风清云淡_A1 小时前
react18基础教程系列-- 框架基础理论知识mvc/jsx/createRoot
前端·reactjs
让开,我要吃人了1 小时前
HarmonyOS应用开发( Beta5.0)HOS-用户认证服务:面部识别
服务器·前端·华为·移动开发·嵌入式·harmonyos·鸿蒙