一、背景说明
随着 HarmonyOS NEXT 在行业终端、移动执法、应急指挥、车载视频、无人机巡检、智慧园区和工业现场等场景中的落地,越来越多鸿蒙设备开始承担“前端视频设备”的角色。它们不仅需要完成摄像头采集、音频采集和本地预览,还需要接入现有视频监管平台、行业调度平台或安防联网平台。
在国内安防和行业视频系统中,GB/T 28181 是非常常见的视频联网协议。传统接入对象通常是 IPC、NVR、执法记录仪、车载终端、边缘网关等设备。对于鸿蒙 NEXT 应用来说,如果希望被国标平台统一纳管,就不能只做一个普通的 RTMP 推流端,而是需要具备平台注册、心跳保活、设备目录、平台点播、语音广播、位置上报、设备控制等完整的国标设备接入能力。
大牛直播SDK(SmartMediaKit)鸿蒙 NEXT GB28181 设备接入模块,正是面向这一类场景设计。开发者可以在鸿蒙 NEXT 应用中,通过 ArkTS 层封装接口完成 GB28181 平台接入,而无需直接处理复杂的 SIP 状态机、平台鉴权、国标会话维护和底层媒体传输细节。
本文档从工程集成角度出发,说明如何在鸿蒙 NEXT 项目中集成大牛直播SDK的 GB28181 设备接入能力,并给出推荐的接入步骤、参数配置、事件处理、视频点播、语音广播、位置上报和异常恢复流程。
二、方案定位
大牛直播SDK(SmartMediaKit)鸿蒙 NEXT GB28181 模块主要面向“设备端接入平台”的场景。
也就是说,鸿蒙 NEXT 应用可以作为一个标准 GB28181 前端设备,注册到国标视频平台,并接受平台侧调度。
典型链路如下:
鸿蒙 NEXT 设备
↓
GB28181 平台注册
↓
心跳保活与设备在线维护
↓
平台发起实时视频点播
↓
设备启动摄像头/屏幕采集与编码
↓
通过国标媒体链路上传音视频
↓
平台接收实时视频并完成业务调度
与普通 RTMP 推流不同,GB28181 不是“填一个 URL 后主动推流”这么简单。GB28181 更像是一套设备联网体系:设备先被平台注册和纳管,平台可以查看设备在线状态,可以按需点播实时视频,也可以下发语音广播、位置订阅、PTZ 控制、远程重启、录像查询等命令。
因此,鸿蒙 NEXT GB28181 接入不应被理解为单一推流功能,而应被理解为“鸿蒙终端进入国标视频平台体系”的完整设备接入能力。
三、核心能力
大牛直播SDK鸿蒙 NEXT GB28181 设备接入模块主要覆盖以下能力:
| 能力 | 说明 |
|---|---|
| 平台注册 | 设备作为国标前端注册到 GB28181 平台 |
| Digest 认证 | 支持平台侧账号密码鉴权 |
| 心跳保活 | 通过 Keepalive 维持设备在线状态 |
| 平台点播 | 支持平台发起实时视频点播 |
| 实时视频上传 | 与 SmartPublisher 媒体模块协同完成采集、编码和国标媒体发送 |
| 语音广播 | 支持平台向设备下发语音广播,设备侧实时播放 |
| 位置上报 | 支持 MobilePosition 位置订阅与上报 |
| 设备控制 | 支持 PTZ 控制、远程重启等设备控制事件 |
| 预置位查询 | 支持平台查询预置位,可按业务扩展 |
| 录像信息查询 | 支持平台录像查询,可返回空列表或对接真实录像记录 |
| 生命周期管理 | 支持启动、停止、释放、异常恢复 |
| TCP/UDP | 支持 SIP 信令 TCP/UDP 配置,媒体链路按平台协商处理 |
| 模块协同 | 可与 RTMP 推流、轻量级 RTSP 服务、本地录像、截图等能力组合使用 |
四、典型应用场景
五、推荐工程结构
鸿蒙 NEXT GB28181 接入建议采用清晰的分层方式,不建议把 SIP 信令、媒体采集、页面交互、定位、语音广播全部混在同一个页面文件里。
推荐结构如下:
entry/src/main/
├── ets/
│ ├── pages/
│ │ └── SmartPublisherPage.ets
│ │ 业务页面、参数配置、按钮操作、日志展示
│ │
│ ├── gb28181/
│ │ ├── Gb28181SipAgent.ets
│ │ │ GB28181 信令封装,负责注册、心跳、点播、广播、控制等事件
│ │ ├── Gb28181SipAgentNative.ets
│ │ │ Native 能力封装层
│ │ └── GbLocationProvider.ets
│ │ 位置获取、缓存和位置上报辅助模块
│ │
│ └── media/
│ ├── SmartPublisherWrapper.ets
│ │ 推送封装层,负责摄像头、麦克风、编码和媒体链路管理
│ ├── PublisherSnapshotHelper.ets
│ │ 快照辅助模块
│ ├── RecordFileHelper.ets
│ │ 本地录像文件辅助模块
│ └── GbAudioBroadcastPlayer.ets
│ 语音广播接收与播放辅助模块
│
├── cpp/
│ └── types/
│ ├── libGb28181SipAgent/
│ │ └── index.d.ts
│ ├── libSmartPublisher/
│ │ └── index.d.ts
│ └── libSmartPlayer/
│ └── index.d.ts
│
└── resources/rawfile/
├── libGb28181SipAgent.so
├── libSmartPublisher.so
└── libSmartPlayer.so
整体分层建议如下:
语音广播和位置上报可以作为独立辅助模块挂接到 GB28181 Agent 事件中:
这种分层方式有几个好处:
- 页面层只负责交互、参数配置和状态展示;
- 信令层只处理 GB28181 平台事件;
- 媒体层只处理采集、编码和发送;
- 位置和语音广播单独封装,后续维护更清晰;
- 便于后续扩展 RTMP 推流、RTSP 服务、本地录像、快照等能力。
六、集成步骤总览
实际项目中,建议按照以下顺序接入,不要一开始就把所有能力同时打开。
这样可以快速定位问题。如果注册都未成功,就不要直接排查媒体链路;如果点播事件没有收到,也不要先怀疑编码器。
七、工程依赖配置
7.1 添加 SDK 文件
将 SDK 交付包中的相关文件放入工程对应目录。实际文件名和目录结构以 SDK 交付包为准,以下为推荐组织方式:
entry/src/main/cpp/types/
├── libGb28181SipAgent/
│ └── index.d.ts
├── libSmartPublisher/
│ └── index.d.ts
└── libSmartPlayer/
└── index.d.ts
Native 库文件放入工程可加载位置:
entry/src/main/resources/rawfile/
├── libGb28181SipAgent.so
├── libSmartPublisher.so
└── libSmartPlayer.so
ArkTS 封装层建议放在:
entry/src/main/ets/gb28181/
├── Gb28181SipAgent.ets
├── Gb28181SipAgentNative.ets
└── GbLocationProvider.ets
entry/src/main/ets/media/
├── SmartPublisherWrapper.ets
├── GbAudioBroadcastPlayer.ets
├── PublisherSnapshotHelper.ets
└── RecordFileHelper.ets
7.2 配置 oh-package.json5
在 oh-package.json5 中声明依赖:
{
"dependencies": {
"libGb28181SipAgent.so": "file:./src/main/cpp/types/libGb28181SipAgent",
"libSmartPublisher.so": "file:./src/main/cpp/types/libSmartPublisher",
"libSmartPlayer.so": "file:./src/main/cpp/types/libSmartPlayer"
}
}
说明:
libGb28181SipAgent.so:GB28181 信令接入模块;libSmartPublisher.so:媒体采集、编码、推送、录像等能力;libSmartPlayer.so:语音广播接收和播放等能力;index.d.ts:提供 ArkTS 类型提示,便于开发阶段调用和检查。
八、权限配置
在 entry/src/main/module.json5 中声明必要权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.CAMERA"
},
{
"name": "ohos.permission.MICROPHONE"
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
],
"backgroundModes": ["dataTransfer"]
}
}
权限说明:
| 权限 | 是否必选 | 说明 |
|---|---|---|
| INTERNET | 必选 | SIP 信令和媒体链路均需要网络访问 |
| CAMERA | 使用摄像头时必选 | 平台点播实时视频时需要采集画面 |
| MICROPHONE | 使用音频时必选 | 推流含音频或语音广播相关能力需要 |
| LOCATION | 使用位置上报时必选 | MobilePosition 上报需要 |
| APPROXIMATELY_LOCATION | 可选 | 按实际定位精度需求配置 |
| backgroundModes | 可选 | 需要后台保持链路时配置 |
注意:
- 相机、麦克风、位置权限不仅要在配置文件中声明,还需要在运行时向用户申请;
- 如果项目只做无音频视频上报,可以不启用麦克风采集;
- 如果项目不需要 MobilePosition,可以暂不申请位置权限;
- 若应用需要长时间运行,需结合鸿蒙后台机制合理处理后台保活策略。
九、初始化 GB28181 Agent
9.1 引入模块
示例:
import {
Gb28181SipAgent,
Gb28181SipAgentConfig,
Gb28181AgentListener,
Gb28181PlayListener,
Gb28181AudioBroadcastListener,
Gb28181DeviceControlListener,
Gb28181SipEvent
} from '../gb28181/Gb28181SipAgent'
9.2 创建 Agent 实例
建议在页面或独立业务管理类中维护 Agent 实例:
private gbSipAgent: Gb28181SipAgent = new Gb28181SipAgent()
private isGB28181Running: boolean = false
private gb28181StatusText: string = '未启动'
private currentConfig?: Gb28181SipAgentConfig
建议把 GB28181 状态独立维护,不要和 RTMP 推流、RTSP 服务、本地录像状态完全混用。GB28181 是平台调度型链路,它的状态通常包括:
未启动
启动中
注册中
已注册
点播中
语音广播中
心跳异常
重连中
已停止
9.3 配置 License 上下文
在首次启动前,建议完成 License 校验上下文配置:
aboutToAppear(): void {
Gb28181SipAgent.configureLicenseVerifyContext(getContext(this))
this.setupGB28181Listeners()
}
说明:
- 该步骤建议放在页面初始化或业务管理类初始化阶段;
- 建议在调用
start()之前完成; - 如果项目中还使用 SmartPublisher 或 SmartPlayer,也应保证对应模块的授权校验流程正常。
十、配置 GB28181 参数
启动 GB28181 Agent 前,需要构造平台参数。
示例:
private buildGB28181Config(): Gb28181SipAgentConfig {
return {
serverAddress: '192.168.1.100',
serverPort: 5060,
serverId: '34020000002000000001',
serverDomain: '3402000000',
localAddress: '',
localPort: 5060,
username: '34020000001320000001',
password: '12345678',
deviceId: '34020000001320000001',
deviceName: 'HarmonyOS Camera',
manufacturer: 'Daniulive',
model: 'SmartPublisherOhos',
transport: 'UDP',
expires: 3600,
heartbeatInterval: 20,
heartbeatCount: 3,
owner: 'Owner',
civilCode: '340200',
deviceAddress: 'HarmonyOS NEXT Device',
parental: 0,
safetyWay: 0,
registerWay: 1,
secrecy: 0,
statusOnline: 1
}
}
主要参数说明:
| 参数 | 说明 |
|---|---|
| serverAddress | GB28181 平台 SIP 服务 IP 或域名 |
| serverPort | 平台 SIP 端口,通常为 5060 |
| serverId | 平台 SIP 服务器 ID,通常为 20 位国标编码 |
| serverDomain | SIP 域,通常取平台 ID 前 10 位 |
| localAddress | 本机绑定 IP,多网卡环境建议指定 |
| localPort | 本地 SIP 端口,默认可用 5060 |
| username | 设备注册用户名,通常为设备国标 ID |
| password | 平台侧配置的设备注册密码 |
| deviceId | 设备或通道 ID,用于平台目录展示和点播 |
| deviceName | 平台设备树中显示的名称 |
| manufacturer | 厂商名称 |
| model | 设备型号 |
| transport | SIP 信令传输方式,可选 UDP 或 TCP |
| expires | 注册有效期,建议 3600 秒 |
| heartbeatInterval | 心跳间隔,建议 20 秒左右 |
| heartbeatCount | 连续心跳失败阈值,超过后触发异常恢复 |
参数配置建议:
serverId、serverDomain、username、deviceId必须与平台侧添加的设备信息一致;serverDomain不一定总是serverId前 10 位,最终以平台要求为准;- 多网卡、VPN、热点、蜂窝网络等场景,建议显式指定
localAddress; password不建议硬编码到正式版本中,可通过安全配置或服务端下发;- SIP 信令协议和媒体传输协议不是一回事,
transport只代表 SIP 信令传输方式,媒体链路按平台点播协商结果处理; heartbeatInterval不建议设置过大,部分平台的在线超时阈值较短。
十一、启动与停止 GB28181
11.1 启动
private async startGB28181(): Promise<void> {
if (this.isGB28181Running) {
this.pushLog('[GB28181] 已启动,无需重复启动')
return
}
this.setupGB28181Listeners()
const config = this.buildGB28181Config()
this.currentConfig = config
const ok = await this.gbSipAgent.start(config)
this.isGB28181Running = ok
this.gb28181StatusText = ok ? '注册中' : '启动失败'
this.pushLog(ok ? '[GB28181] 已启动,等待平台注册' : '[GB28181] 启动失败')
}
说明:
start()成功仅代表 Agent 启动成功,并不等于已经注册成功;- 注册结果应通过
onRegisterOK、onRegisterTimeout、onRegisterRejected等回调判断; - 不建议短时间内频繁 start/stop;
- 如果启动失败,应输出
getLastError()或业务日志,便于定位配置或端口问题。
11.2 停止
private async stopGB28181(): Promise<void> {
this.pushLog('[GB28181] 准备停止')
await this.stopGB28181Media('manual stop')
this.audioBroadcastPlayer?.stop()
this.gbSipAgent.terminateAllPlays(true)
this.gbSipAgent.terminateAllAudioBroadcasts(true)
this.gbSipAgent.stop()
this.isGB28181Running = false
this.gb28181StatusText = '未启动'
this.pushLog('[GB28181] 已停止')
}
停止时建议遵循以下顺序:
停止媒体链路
↓
停止语音广播
↓
终止平台点播会话
↓
终止语音广播会话
↓
停止 GB28181 Agent
↓
更新页面状态
11.3 页面销毁释放
aboutToDisappear(): void {
this.gbSipAgent.release()
this.audioBroadcastPlayer?.release()
this.publisher?.release()
this.pushLog('[GB28181] 页面退出,资源已释放')
}
stop() 和 release() 的区别:
| 方法 | 适用场景 | 说明 |
|---|---|---|
| stop() | 临时停止、异常重连、用户手动停止 | 停止当前链路,保留对象,后续可再次启动 |
| release() | 页面销毁、应用退出 | 释放对象和监听器,不建议继续复用 |
十二、注册与心跳监听
GB28181 注册和心跳是基础链路。只有设备稳定在线,平台点播、语音广播、位置订阅和设备控制才有意义。
示例:
private setupGB28181AgentListener(): void {
const agentListener = new Gb28181AgentListener()
agentListener.onRegisterOK = (event: Gb28181SipEvent): void => {
this.gb28181StatusText = '已注册'
this.pushLog('[GB28181] 注册成功')
}
agentListener.onRegisterRejected = (event: Gb28181SipEvent): void => {
this.gb28181StatusText = '注册被拒绝'
this.pushLog(`[GB28181] 注册被拒绝: ${event.statusCode}, ${event.message ?? ''}`)
}
agentListener.onRegisterTimeout = (event: Gb28181SipEvent): void => {
this.gb28181StatusText = '注册超时'
this.pushLog('[GB28181] 注册超时,请检查平台地址、端口、设备 ID 和网络连通性')
}
agentListener.onRegisterTransportError = (event: Gb28181SipEvent): void => {
this.gb28181StatusText = '传输异常'
this.pushLog('[GB28181] 注册传输异常,请检查网络、防火墙或端口占用')
}
agentListener.onHeartbeatWarning = (event: Gb28181SipEvent): void => {
this.pushLog(`[GB28181] 心跳异常预警,失败次数: ${event.statusCode}`)
}
agentListener.onHeartbeatException = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 心跳异常,准备重启链路')
await this.restartGB28181()
}
agentListener.onMobilePositionRequest = (event: Gb28181SipEvent): void => {
this.reportLastKnownLocation()
}
this.gbSipAgent.setAgentListener(agentListener)
}
十三、平台点播处理
平台点播是 GB28181 设备接入的核心流程。
整体流程可以理解为:
平台发起点播
↓
设备收到点播事件
↓
业务层准备媒体链路
↓
平台确认会话
↓
设备开始发送视频流
↓
平台停止点播
↓
设备释放媒体资源
推荐通过 Listener 方式处理点播事件:
private setupGB28181PlayListener(): void {
const playListener = new Gb28181PlayListener()
playListener.onInvitePlay = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 收到平台点播请求')
await this.prepareGB28181Media(event)
}
playListener.onAckPlay = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 平台确认点播,启动媒体发送')
await this.startGB28181Media(event)
}
playListener.onByePlay = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 平台停止点播')
await this.stopGB28181Media('bye')
}
playListener.onCancelPlay = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 平台取消点播')
await this.stopGB28181Media('cancel')
}
playListener.onTerminatePlay = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 点播会话终止')
await this.stopGB28181Media('terminate')
}
playListener.onPlayDialogTerminated = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 点播会话超时,释放媒体资源')
await this.stopGB28181Media('dialog terminated')
}
this.gbSipAgent.setPlayListener(playListener)
}
十四、媒体链路与 SmartPublisher 协同
GB28181 点播最终需要摄像头采集、音频采集、编码和媒体发送。大牛直播SDK在鸿蒙 NEXT 端通过 SmartPublisher 统一处理媒体能力。
建议业务层封装三个方法:
private async prepareGB28181Media(event: Gb28181SipEvent): Promise<void> {
this.pushLog('[GB28181] 准备媒体链路')
const ok = await this.publisher.prepareGB28181Media({
videoWidth: this.publishWidth,
videoHeight: this.publishHeight,
fps: this.publishFps,
bitrateKbps: this.publishBitrateKbps,
audioEnabled: this.audioEnabled,
videoCodec: 'H264',
audioCodec: 'PCMA'
}, event)
if (!ok) {
this.pushLog('[GB28181] 媒体链路准备失败')
return
}
this.pushLog('[GB28181] 媒体链路准备完成')
}
启动媒体发送:
private async startGB28181Media(event: Gb28181SipEvent): Promise<void> {
const ok = await this.publisher.startGB28181Media(event)
if (!ok) {
this.pushLog('[GB28181] 媒体发送启动失败')
await this.stopGB28181Media('start failed')
return
}
this.gb28181StatusText = '点播中'
this.pushLog('[GB28181] 媒体发送已启动')
}
停止媒体链路:
private async stopGB28181Media(reason: string): Promise<void> {
await this.publisher.stopGB28181Media()
this.gb28181StatusText = this.isGB28181Running ? '已注册' : '未启动'
this.pushLog(`[GB28181] 媒体链路已停止: ${reason}`)
}
推荐媒体参数:
| 参数 | 建议值 | 说明 |
|---|---|---|
| 视频编码 | H.264 | 兼容性最好,适合大多数国标平台 |
| 视频分辨率 | 720P / 1080P | 根据平台能力和网络带宽选择 |
| 帧率 | 20~25 fps | 监控和行业场景通常足够 |
| 码率 | 800~3000 kbps | 根据分辨率和网络情况调整 |
| GOP | 1~2 秒 | 兼顾首屏和带宽 |
| 音频编码 | PCMA / AAC | 国标平台通常更偏好 G.711 A-law |
| 音频采样率 | 8000 / 16000 / 48000 | 以平台兼容性为准 |
| 传输方式 | 按平台协商 | UDP/TCP 根据平台点播参数处理 |
对外文档中不建议展示过多底层调用。开发者更关心的是:如何把平台点播事件和 SmartPublisher 的采集编码能力打通,以及如何在停止、异常、页面退出时安全释放资源。
十五、语音广播接入
GB28181 语音广播适用于平台向前端设备下发语音指令,例如应急指挥、移动执法、车载调度、巡检提醒等场景。
整体流程可以理解为:
平台发起语音广播
↓
设备收到广播通知
↓
业务层决定是否接受
↓
建立音频接收链路
↓
播放平台下发的音频
↓
广播结束后释放资源
示例:
private setupGB28181AudioBroadcastListener(): void {
const audioListener = new Gb28181AudioBroadcastListener()
audioListener.onAudioBroadcastNotify = (event: Gb28181SipEvent): void => {
this.pushLog('[GB28181] 收到语音广播通知')
// 可根据业务状态决定是否接受
this.gbSipAgent.respondBroadcastCommand(event.sourceId, event.targetId, true)
}
audioListener.onAudioBroadcast = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 准备接收语音广播')
await this.audioBroadcastPlayer.prepare(event)
}
audioListener.onAudioBroadcastResponse = async (event: Gb28181SipEvent): Promise<void> => {
if (event.statusCode === 200) {
this.pushLog('[GB28181] 语音广播链路建立,开始播放')
await this.audioBroadcastPlayer.start(event)
this.gb28181StatusText = '语音广播中'
} else {
this.pushLog(`[GB28181] 语音广播被平台拒绝: ${event.statusCode}`)
this.audioBroadcastPlayer.stop()
}
}
audioListener.onByeAudioBroadcast = (event: Gb28181SipEvent): void => {
this.pushLog('[GB28181] 语音广播结束')
this.audioBroadcastPlayer.stop()
this.gb28181StatusText = this.isGB28181Running ? '已注册' : '未启动'
}
audioListener.onAudioBroadcastInviteTimeout = (event: Gb28181SipEvent): void => {
this.pushLog('[GB28181] 语音广播建立超时')
this.audioBroadcastPlayer.stop()
}
this.gbSipAgent.setAudioBroadcastListener(audioListener)
}
十六、位置上报 MobilePosition
移动终端、车载设备、无人机、巡检设备接入 GB28181 平台时,平台通常不仅关心视频画面,也关心设备位置。
建议把定位能力独立封装,不要直接写在 GB28181 事件回调中。
示例:
private async initLocationProvider(): Promise<void> {
const granted = await this.locationProvider.requestLocationPermission(getContext(this))
if (granted) {
this.locationProvider.startListening()
this.pushLog('[GB28181] 定位模块已启动')
} else {
this.pushLog('[GB28181] 未获取定位权限,位置上报不可用')
}
}
响应平台位置请求:
private reportLastKnownLocation(): void {
const location = this.locationProvider.getLastLocation()
if (!location) {
this.pushLog('[GB28181] 当前暂无可上报位置')
return
}
const ok = this.gbSipAgent.updateDevicePosition({
longitude: location.longitude,
latitude: location.latitude,
altitude: location.altitude ?? 0,
speed: location.speed ?? 0,
direction: location.direction ?? 0,
time: new Date().toISOString()
})
this.pushLog(ok ? '[GB28181] 位置上报成功' : '[GB28181] 位置上报失败')
}
十七、设备控制事件
GB28181 平台可能下发设备控制命令,例如 PTZ 控制、远程重启等。
对于普通鸿蒙手机或平板,部分控制命令可以只做日志记录或业务提示;对于机器人、云台、巡检设备、无人机或车载终端,则可以映射到实际硬件控制。
示例:
private setupGB28181DeviceControlListener(): void {
const controlListener = new Gb28181DeviceControlListener()
controlListener.onDeviceControlPtz = (event: Gb28181SipEvent): void => {
this.pushLog(`[GB28181] 收到 PTZ 控制命令: ${event.ptzCmdValue ?? ''}`)
// 可根据实际设备能力映射到云台、摄像头、机器人或无人机控制
this.handlePtzCommand(event.ptzCmdValue)
}
controlListener.onDeviceControlTeleBoot = async (event: Gb28181SipEvent): Promise<void> => {
this.pushLog('[GB28181] 收到远程重启指令,准备重启业务链路')
// 建议理解为“重启 GB28181 业务链路”,不建议直接重启系统设备
await this.restartGB28181()
}
this.gbSipAgent.setDeviceControlListener(controlListener)
}
十八、预置位查询与录像查询
GB28181 平台可能发起预置位查询或录像信息查询。
18.1 预置位查询
如果设备不支持预置位,可以返回空列表;如果支持云台预置位,可以返回真实预置位数据。
private setupPresetQueryListener(): void {
const presetListener = new Gb28181PresetQueryListener()
presetListener.onPresetQuery = (event: Gb28181SipEvent): void => {
this.pushLog('[GB28181] 收到预置位查询')
// 不支持预置位时,可返回空结果
this.gbSipAgent.respondPresetQuery(
event.fromUserName,
event.fromUserNameAtDomain,
event.sn,
event.deviceId
)
}
this.gbSipAgent.setPresetQueryListener(presetListener)
}
18.2 录像信息查询
如果设备侧没有本地录像能力,可以返回空列表;如果已经对接本地录像管理模块,可按时间范围检索并返回对应记录。
private setupRecordInfoListener(): void {
const recordListener = new Gb28181RecordInfoListener()
recordListener.onRecordInfoQuery = (event: Gb28181SipEvent): void => {
this.pushLog(`[GB28181] 收到录像查询: ${event.startTime} - ${event.endTime}`)
// 可根据业务情况返回空列表或真实录像记录
this.gbSipAgent.respondRecordInfoQuery(
event.fromUserName,
event.fromUserNameAtDomain,
event.toUserName,
event.sn,
event.deviceId
)
}
this.gbSipAgent.setRecordInfoListener(recordListener)
}
十九、设备配置与远程升级
部分 GB28181 平台可能下发设备配置或升级命令。
对于鸿蒙 NEXT 设备端,建议按业务安全策略处理:
| 指令 | 建议处理 |
|---|---|
| 基础参数配置 | 可记录日志,必要时映射到业务配置 |
| OSD 配置 | 如项目支持水印/文字叠加,可映射处理 |
| 编码参数配置 | 可根据项目策略决定是否允许平台修改 |
| 远程升级 | 默认只记录日志,正式启用前需建立完整安全校验机制 |
二十、完整接入示例
以下示例展示一个较完整的业务组织方式,仅体现上层集成流程,不展开底层协议和媒体实现细节。
@Entry
@Component
struct Gb28181DemoPage {
private gbSipAgent: Gb28181SipAgent = new Gb28181SipAgent()
private publisher: SmartPublisherWrapper = new SmartPublisherWrapper()
private audioBroadcastPlayer: GbAudioBroadcastPlayer = new GbAudioBroadcastPlayer()
private locationProvider: GbLocationProvider = new GbLocationProvider()
private isGB28181Running: boolean = false
private currentConfig?: Gb28181SipAgentConfig
@State gb28181StatusText: string = '未启动'
aboutToAppear(): void {
Gb28181SipAgent.configureLicenseVerifyContext(getContext(this))
this.setupGB28181Listeners()
this.initLocationProvider()
}
aboutToDisappear(): void {
this.publisher.release()
this.audioBroadcastPlayer.release()
this.locationProvider.stopListening()
this.gbSipAgent.release()
}
private setupGB28181Listeners(): void {
this.setupGB28181AgentListener()
this.setupGB28181PlayListener()
this.setupGB28181AudioBroadcastListener()
this.setupGB28181DeviceControlListener()
}
private async startGB28181(): Promise<void> {
if (this.isGB28181Running) {
this.pushLog('[GB28181] 已启动')
return
}
const config = this.buildGB28181Config()
this.currentConfig = config
const ok = await this.gbSipAgent.start(config)
this.isGB28181Running = ok
this.gb28181StatusText = ok ? '注册中' : '启动失败'
this.pushLog(ok ? '[GB28181] 启动成功,等待注册' : '[GB28181] 启动失败')
}
private async stopGB28181(): Promise<void> {
await this.stopGB28181Media('manual stop')
this.audioBroadcastPlayer.stop()
this.gbSipAgent.terminateAllPlays(true)
this.gbSipAgent.terminateAllAudioBroadcasts(true)
this.gbSipAgent.stop()
this.isGB28181Running = false
this.gb28181StatusText = '未启动'
this.pushLog('[GB28181] 已停止')
}
private buildGB28181Config(): Gb28181SipAgentConfig {
return {
serverAddress: '192.168.1.100',
serverPort: 5060,
serverId: '34020000002000000001',
serverDomain: '3402000000',
localAddress: '',
localPort: 5060,
username: '34020000001320000001',
password: '12345678',
deviceId: '34020000001320000001',
deviceName: 'HarmonyOS Camera',
manufacturer: 'Daniulive',
model: 'SmartPublisherOhos',
transport: 'UDP',
expires: 3600,
heartbeatInterval: 20,
heartbeatCount: 3
}
}
private pushLog(msg: string): void {
console.info(msg)
}
}
二十一、与 RTMP 推流、RTSP 服务、本地录像协同
大牛直播SDK鸿蒙 NEXT 方案并不只支持 GB28181。实际项目中,可以根据业务需要组合多种能力:
| 能力 | 定位 |
|---|---|
| GB28181 设备接入 | 面向国标平台纳管和调度 |
| RTMP 推流 | 面向直播平台或业务服务器主动推流 |
| 轻量级 RTSP 服务 | 设备本地提供 RTSP 拉流能力 |
| 本地录像 | 设备侧保存音视频文件 |
| 快照 | 保存当前视频画面 |
| 语音广播 | 平台向设备下发语音调度 |
| 位置上报 | 平台侧掌握移动设备位置 |
典型组合方式:
GB28181 接入监管平台
RTMP 推流到业务直播系统
轻量级 RTSP 服务供局域网客户端拉流
本地录像用于留痕
快照用于事件取证
位置上报用于调度
语音广播用于现场指挥
这也是大牛直播SDK(SmartMediaKit)的核心价值:它不是单点协议能力,而是一套面向实时视频系统的模块化能力。开发者可以按项目需要组合,而不是为每个协议重新搭一套采集、编码、传输和播放链路。
二十二、联调建议
GB28181 平台差异较多,不同平台在设备编码、SIP 域、目录结构、点播参数、传输方式、心跳超时、音频编码等方面可能存在差异。建议按以下顺序联调。
23.1 平台侧准备
确认平台侧已添加设备:
| 项目 | 检查点 |
|---|---|
| 设备 ID | 与 App 中 username/deviceId 一致 |
| 注册密码 | 与 App 中 password 一致 |
| SIP 域 | 与 App 中 serverDomain 一致 |
| 平台 ID | 与 App 中 serverId 一致 |
| 设备类型 | 按平台要求选择 IPC/移动设备/执法仪等 |
| 传输协议 | UDP 或 TCP,与 App 配置一致 |
| 端口策略 | 平台和设备之间 SIP/RTP 端口可达 |
23.2 设备侧检查
| 项目 | 检查点 |
|---|---|
| 网络 | 设备能访问平台 IP 和端口 |
| 权限 | 相机、麦克风、位置权限已授权 |
| 本地 IP | 多网卡场景下 localAddress 正确 |
| 本地端口 | localPort 未被占用 |
| License | SDK 授权校验通过 |
| 日志 | Demo 日志能看到注册、心跳、点播事件 |
23.3 验证顺序
二十三、总结
大牛直播SDK(SmartMediaKit)鸿蒙 NEXT GB28181 设备接入模块,解决的是鸿蒙 NEXT 终端如何作为标准国标前端设备接入视频平台的问题。
从工程集成角度看,开发者不需要直接关注 SIP 状态机、底层会话维护、媒体包发送和复杂协议细节,而是可以通过 ArkTS 层封装完成以下工作:
配置平台参数
注册事件监听
启动 GB28181 Agent
响应平台注册和心跳
处理平台点播事件
启动 SmartPublisher 媒体链路
处理语音广播、位置上报和设备控制
统一停止、释放和异常恢复
对于行业客户而言,这种方式的价值在于:
- 能让鸿蒙 NEXT 设备快速进入现有 GB28181 视频平台体系;
- 能复用大牛直播SDK长期积累的低延迟音视频能力;
- 能将信令、媒体、位置、语音广播、录像、截图等能力模块化组合;
- 能减少开发者对底层协议细节的重复投入;
- 能更好适配移动执法、应急指挥、车载视频、无人机巡检、智慧园区和工业现场等复杂业务场景。
建议实际项目按“先注册、再点播、再媒体、再扩展能力”的顺序逐步集成和验证。这样不仅排查效率更高,也更容易形成稳定、可维护、可交付的鸿蒙 NEXT 国标设备接入方案。
📎 CSDN官方博客:音视频牛哥-CSDN博客