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平台。

相关推荐
水瓶丫头站住4 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
xvch4 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
浪浪山小白兔6 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
limit for me7 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架