Android平台GB28181实时回传流程和技术实现
规范解读
GB28181 中的 “INVITE” 是会话初始协议(SIP)中的一种请求方法,主要用于邀请一个或多个参与者加入特定的会话。在 GB28181 标准中,“INVITE” 请求通常用于发起媒体流的传输请求。当一个设备想要接收来自另一个设备的媒体流时,它会向目标设备发送一个 “INVITE” 请求,其中包含了关于会话的描述信息,如媒体类型、编码格式、传输协议等。
以下是 GB28181 中 “INVITE” 请求的一些关键特点和作用:
一、发起会话
- 触发媒体流传输:“INVITE” 请求是启动媒体流传输的关键步骤。当源设备发送 “INVITE” 请求时,它向目标设备表明了希望建立一个媒体会话的意图。这个请求中包含了源设备支持的媒体格式、编码方式和传输协议等信息,以便目标设备能够确定是否可以满足这些要求并接受会话邀请。
- 携带会话描述信息:“INVITE” 请求中通常包含会话描述协议(SDP)信息,用于描述媒体流的特性。SDP 信息包括媒体类型(音频、视频或两者兼有)、编码格式、媒体的传输地址和端口等。目标设备通过解析 SDP 信息,可以了解源设备的媒体能力,并决定是否能够参与会话。
二、协商媒体参数
- 媒体能力协商:在 GB28181 中,不同的设备可能具有不同的媒体处理能力。通过 “INVITE” 请求和响应的交互过程,可以进行媒体能力的协商。目标设备在接收到 “INVITE” 请求后,会检查自己的媒体能力,并根据源设备的要求进行相应的调整。如果目标设备无法满足源设备的要求,它可以返回一个错误响应,或者提出替代的媒体参数建议。
- 编码格式选择:“INVITE” 请求还可以用于协商媒体流的编码格式。不同的编码格式在压缩效率、图像质量和带宽需求等方面有所不同。通过协商,可以选择一种双方都支持的编码格式,以确保媒体流的正常传输和播放。
三、建立连接
- 确定传输路径:一旦目标设备接受了 “INVITE” 请求,双方就可以开始建立媒体流的传输连接。这个过程涉及确定媒体流的传输协议(如 RTP/RTCP)、传输地址和端口等信息。根据 GB28181 标准,媒体流可以通过 IP 网络进行传输,使用 UDP 或 TCP 协议。
- 会话管理:在媒体流传输过程中,“INVITE” 请求所建立的会话可以通过其他 SIP 方法进行管理,如 “BYE” 用于结束会话,“ACK” 用于确认会话的建立等。这些方法可以帮助设备在会话期间进行状态监测、错误处理和资源管理。
Android GB28181技术实现
本文以大牛直播SDK的GB28181设备接入模块为例,大牛直播SDK推出的Android平台GB28181接入SDK(SmartGBD),可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景。进入系统后,先启动GB28181,注册到平台,等待国标平台发起回传请求。

class ButtonGB28181AgentListener implements View.OnClickListener {public void onClick(View v) {record_executor_.cancel_tasks();stopRecordDownloads(true);stopPlaybacks(true);stopAudioPlayer();destoryRTPReceiver();gb_broadcast_source_id_ = null;gb_broadcast_target_id_ = null;btnGB28181AudioBroadcast.setText("GB28181语音广播");btnGB28181AudioBroadcast.setEnabled(false);stopGB28181Stream();destoryRTPSender();if (null == gb28181_agent_ ) {if( !initGB28181Agent() )return;}if (gb28181_agent_.isRunning()) {gb28181_agent_.terminateAllAudioBroadcasts(true);gb28181_agent_.terminateAllPlays(true);// 目前测试下来,发送BYE之后,有些服务器会立即发送INVITE,是否发送BYE根据实际情况看gb28181_agent_.stop();btnGB28181Agent.setText("启动GB28181");}else {record_executor_.cancel_tasks();initPlaybacks(null);initRecordDownloads(null);if ( gb28181_agent_.start() ) {btnGB28181Agent.setText("停止GB28181");}}}
}
GBSIPAgentPlayListener主要系GB28181的Invite、Ack、Bye等处理:
public interface GBSIPAgentPlayListener {/**收到s=Play的实时视音频点播*/void ntsOnInvitePlay(String deviceId, SessionDescription sessionDescription);/**发送play invite response 异常*/void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo);/** 收到CANCEL play INVITE请求*/void ntsOnCancelPlay(String deviceId);/** 收到Ack*/void ntsOnAckPlay(String deviceId);/** 收到Bye*/void ntsOnByePlay(String deviceId);/** 不是在收到BYE Message情况下, 终止Play*/void ntsOnTerminatePlay(String deviceId);/** Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发收到这个, 请做相关清理处理*/void ntsOnPlayDialogTerminated(String deviceId);
}
平台发起回传请求时,ntsOnInvitePlay()来响应invite请求。
/** Camera2MainActivity.java* Author: daniusdk.com* WeChat: xinsheng120*/
@Override
public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {handler_.postDelayed(new Runnable() {@Overridepublic 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() + " ssrc:" + video_des.getSSRC()+ " 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.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC());libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2MlibPublisher.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.setSSRC(video_des.getSSRC());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);
}@Override
public void ntsOnCancelPlay(String deviceId) {// 这里取消Play会话handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);
}
收到Ack后,ntsOnAckPlay()处理后续逻辑:
@Override
public void ntsOnAckPlay(String deviceId) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);InitAndSetConfig();stream_publisher_.SetGB28181RTPSender(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);boolean start_ret = stream_publisher_.StartGB28181MediaStream();if (!start_ret) {stream_publisher_.try_release();destoryRTPSender();Log.e(TAG, "Failed to start GB28181 service..");return;}startAudioRecorder();startLayerPostThread();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);
}@Override
public void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {// 这里要释放掉响应的资源Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + deviceId + " statusCode=" +statusCode+ " errorInfo:" + errorInfo);handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + device_id_);destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);
}
总结
GB28181 中的 “INVITE” 请求在媒体流传输中起着至关重要的作用,它通过发起会话、协商媒体参数和建立连接等步骤,实现了设备之间的媒体通信。在实际应用中,需要根据具体的需求和场景,合理地使用 “INVITE” 请求和其他 SIP 方法,以确保媒体流的稳定传输和高质量播放。
相关文章:
Android平台GB28181实时回传流程和技术实现
规范解读 GB28181 中的 “INVITE” 是会话初始协议(SIP)中的一种请求方法,主要用于邀请一个或多个参与者加入特定的会话。在 GB28181 标准中,“INVITE” 请求通常用于发起媒体流的传输请求。当一个设备想要接收来自另一个设备的媒…...
Text-to-SQL方法研究
有关Text-to-SQL实现细节,可以查阅我的另一篇文章text-to-sql将自然语言转换为数据库查询语句 1、面临的挑战 自然语言问题往往包含复杂的语言结构,如嵌套语句、倒装句和省略等,很难准确映射到SQL查询上。此外,自然语言本身就存在歧义,一个问题可能有多种解读。消除…...
【Router】路由功能之MAC地址过滤(MAC Filter)功能介绍及实现
MAC地址过滤(MAC Filter) MAC 地址过滤是一种网络安全技术,通过在网络设备(如路由器)上设置规则,允许或阻止特定 MAC 地址的设备连接到网络。其主要作用是增强网络的安全性,防止未经授权的设备接入网络。 MAC Filter工作原理 MAC 地址过滤的工作原理是根据设备…...
Flink 本地 idea 调试开启 WebUI
Flink 本地 idea 调试开启 WebUI Maven 引用相关的包配置端口使用本地带UI环境启动 // maven 导入<!-- flink运行时的webUI --><dependency><groupId>org.apache.flink</groupId><artifactId>flink-runtime-web</artifactId><version…...
如何识别IP地址是独享的还是共享的
在网络环境中,IP地址的分配和使用方式直接影响到用户的在线隐私和访问安全。选择独享IP还是共享IP取决于用户的具体需求,理解这两种IP地址的差异及其特点至关重要。本文将探讨如何区分独享IP和共享IP,以及各自的优缺点。 1. 什么是独享IP与共…...
X-Spreadsheet使用教程:打造你的Web端电子表格应用
在Web开发中,经常需要处理数据表格的展示与编辑,而X-Spreadsheet作为一款轻量级、功能强大的JavaScript电子表格库,为开发者提供了一个便捷的解决方案。本文将详细介绍如何使用X-Spreadsheet在Web项目中创建和配置电子表格,让你的…...
订餐点餐|订餐系统基于java的订餐点餐系统小程序设计与实现(源码+数据库+文档)
订餐点餐系统小程序 目录 基于java的订餐点餐系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布…...
Tkinter制作登录界面以及登陆后页面切换(一)
Tkinter制作登录界面以及登陆后页面切换(一) 前言序言1. 由来2. 思路3. 项目结构描述4. 项目实战1. 登录界面实现(代码)2. 首页界面实现(代码)3. 打包build.py(与main.py同级目录)4.…...
Colorful/七彩虹将星X17 AT 23 英特尔13代处理器 Win11原厂OEM系统 带COLORFUL一键还原
安装完毕自带原厂驱动和预装软件以及一键恢复功能,自动重建COLORFUL RECOVERY功能,恢复到新机开箱状态。 【格式】:iso 【系统类型】:Windows11 原厂系统下载网址:http://www.bioxt.cn 注意:安装系统会…...
《Ubuntu20.04环境下的ROS进阶学习8》
一、中断和定时器中断 在ROS中我们经常会遇到要使用中断函数的情况,中断函数的触发方式有很多种,比如检测到某个引脚的电平变化,或某个数据达到了一定的范围,但最实用的中断触发方式还是定时器中断。 二、编写ROS的中断代码 ros中…...
ubuntu24.04 怎么调整swap分区的大小,调整为16G
在Ubuntu中,swap分区的大小通常建议为物理内存的1到2倍,具体取决于你的使用需求和系统内存。例如,如果你有8GB内存,swap可以设置为8GB到16GB。swap的主要作用是当物理内存不足时,提供额外的虚拟内存,帮助防…...
【论文阅读】视觉里程计攻击
Adversary is on the Road: Attacks on Visual SLAM using Unnoticeable Adversarial Patch 一、视觉SLAM的不安全因素 根据论文的分析,视觉SLAM由于完全依赖于特征,缺少验证机制导致算法不安全。前端在受到干扰的情况下,会导致误匹配增加&…...
解决 Git LFS 切换分支失败问题
场景描述 在本地已有分支 A 的情况下,目前工作在分支 B。当尝试从 B 分支切回 A 分支时,由于 A 分支存在 LFS 上传的大文件,导致切换失败。这个问题通常是因为某些 LFS 文件在服务器上不存在或没有权限访问。 报错日志 切换分支时遇到的错…...
BaoStock 的安装
安装 pip3 install baostock使用这个库登录免费帐户时有时候会出现登录失败的问题 import baostock as bs # 登录系统 lg bs.login() # 登出系统 bs.logout()login failed! logout failed!可能是由于高版本的python需要验证ssl,本地将其设置为可信服务器地址可以…...
聚势启新 智向未来 | 重庆华阳通用科技有限公司揭牌成立
助推两江新区汽车产业高质量发展 (以下文字内容转载自两江新区网) 9月26日,重庆华阳通用科技有限公司(华阳通用重庆子公司)在两江新区揭牌成立,将致力于智能座舱、智能驾驶两大领域,不断加大技术研发投入…...
【数据结构与算法】Z算法(扩展KMP)(C++和Python写法)
Z算法(扩展KMP) 文章目录 Z算法(扩展KMP)朴素求法线性求法力扣类型题变种题:[3303. 第一个几乎相等子字符串的下标](https://leetcode.cn/problems/find-the-occurrence-of-first-almost-equal-substring/) 所谓Z算法&…...
免费语音转文字软件全览:开启高效记录新时代
在当今快节奏的信息时代,高效地处理和记录信息变得至关重要。语音转文字技术的出现,为我们带来了极大的便利,今天,就让我们一同探讨这些语音转文字免费的软件的使用方法。 1.365在线转文字 链接直达:https://www.pdf…...
PHP“===”的意义
在PHP中, 运算符被称为“恒等比较运算符”(Identical Comparison Operator),它用于比较两个变量的值和类型是否完全相同。这个运算符与双等号 (等值比较运算符)不同,后者在比较时会对两边的值进…...
Tomcat架构解析
Tomcat: 是基于JAVA语言的轻量级应用服务器,是一款完全开源免费的Servlet服务器实现。 1. 总体设计 socket: 其实就是操作系统提供给程序员操作“网络协议栈”的接口,你能通过socket的接口,来控制协议,实现网络通信,达…...
如何在 Kubernetes 上部署和配置开源数据集成平台 Airbyte?
在 Kubernetes 上部署和配置 Airbyte 是一个复杂但非常有价值的过程,特别是对于需要强大数据集成和数据处理能力的企业或团队。Airbyte 是一个开源的数据集成平台,允许用户从各种来源提取数据并加载到目标存储中。其强大的插件系统支持多种数据源与目标&…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
Qwen系列之Qwen3解读:最强开源模型的细节拆解
文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...
背包问题双雄:01 背包与完全背包详解(Java 实现)
一、背包问题概述 背包问题是动态规划领域的经典问题,其核心在于如何在有限容量的背包中选择物品,使得总价值最大化。根据物品选择规则的不同,主要分为两类: 01 背包:每件物品最多选 1 次(选或不选&#…...
职坐标物联网全栈开发全流程解析
物联网全栈开发涵盖从物理设备到上层应用的完整技术链路,其核心流程可归纳为四大模块:感知层数据采集、网络层协议交互、平台层资源管理及应用层功能实现。每个模块的技术选型与实现方式直接影响系统性能与扩展性,例如传感器选型需平衡精度与…...
