GB28181设备接入端如何播放语音广播数据?

news2024/12/24 8:53:48

技术背景

语音广播功能是GB28181设备接入端非常重要的功能属性,语音广播让终端和平台之间,有了实时双向互动,可以满足执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景的技术诉求。

这里我们先回顾下GB28181规范关于语音广播的描述:

语音广播功能实现用户通过语音输入设备向前端语音输出设备的语音广播。

语音输入设备/语音输入联网系统(以下简称“语音流发送者”)、SIP 服务器向语音输出设备/语音输出视频监控联网系统(以下简称“语音流接收者”)发送通知消息,语音流接收者收到通知消息后,进行判断处理。

若能够接收广播,则向语音流发送者发起呼叫请求,获取广播媒体流。

语音输入设备、语音输出设备编码应符合E.1 的规定。如果设备具备语音输出能力,则在设备目录查询和订阅时,需要上报语音输出设备。如果不上报语音输出设备,则表示该设备没有语音输出能力。

上报语音输出通道时,ParentID 填写其父设备的 ID。例如,IPC 具备语音输出能力,在 IPC 上报设备目录时,需要上报语音输出设备。该语音输出设备ID的类型编码为 137,其父设备为该IPC。NVR 本身具备语音输出能力,在 NVR 上报设备目录时,除了上报 NVR 接入的 IPC 以及IPC 自身的语音输出设备之外,还需要上报语音输出设备。该语音输出设备ID的类型编码为137.其父设备为该NVR。监控中心与设备之间进行语音广播,可以直接对语音输出设备发送语音广播通知,也可以对语音输出设备所属的前端主设备发送语音广播通知。

对前端主设备发送语音广播通知消息中仅需携带前端主设备编码,表示对该设备上所有的语音输出设备进行语音广播。例如,对IPC 发送语音广播通知,表示对该IPC 接入的所有语音输出设备进行广播;对 NVR 发送语音广播通知,表示对 NVR 下所有 IPC以及自身的语音输出设备进行广播。

语音流的封装格式应符合 C.2.4 音频流的 RTP 封装的定义。

语音广播宜符合附录 K 规定的媒体流保活机制。

技术实现

本文我们不再探讨GB28181语音广播的具体流程,这里我们假定信令交互已经完成,准备接收数据:

收到broadcast语音广播后,我们的处理逻辑如下:

private boolean startAudioPlay() {
  if (player_handle_ != 0 )
    return false;

  player_handle_ = lib_player_.SmartPlayerOpen(context_);
  if (player_handle_ == 0)
    return false;

  lib_player_.SetSmartPlayerEventCallbackV2(player_handle_,new EventHandlerPlayerV2());

  // 缓存大小可以调整
  lib_player_.SmartPlayerSetBuffer(player_handle_, 0);

  // lib_player_.SmartPlayerSetFastStartup(player_handle_, 0);

  // set report download speed(默认2秒一次回调 用户可自行调整report间隔)
  lib_player_.SmartPlayerSetReportDownloadSpeed(player_handle_, 1, 2);

  lib_player_.SmartPlayerClearRtpReceivers(player_handle_);
  lib_player_.SmartPlayerAddRtpReceiver(player_handle_, rtp_receiver_handle_);

  lib_player_.SmartPlayerSetSurface(player_handle_, null);

  lib_player_.SmartPlayerSetAudioOutputType(player_handle_, 1);

  lib_player_.SmartPlayerSetMute(player_handle_, 0);

  lib_player_.SmartPlayerSetAudioVolume(player_handle_, 100);

  lib_player_.SmartPlayerSetExternalAudioOutput(player_handle_, new PlayerExternalPCMOutput());

  lib_player_.SmartPlayerSetUrl(player_handle_, "rtp://ntinternal/rtpreceiver/implemention0");

  if (0 != lib_player_.SmartPlayerStartPlay(player_handle_)) {
    lib_player_.SmartPlayerClose(player_handle_);
    player_handle_ = 0;

    Log.e(TAG,  "[daniusdk]start audio play failed");
    return false;
  }

  lib_player_.SmartPlayerSetAudioDataCallback(player_handle_, new PlayerAudioDataOutput());
  lib_player_.SmartPlayerSetPullStreamAudioTranscodeAAC(player_handle_, 0);

  if (0 ==lib_player_.SmartPlayerStartPullStream(player_handle_) ) {
    // 启动定时器,长时间收不到音频数据,则停止播放,发送BYE
    last_received_audio_data_time_.set(SystemClock.elapsedRealtime());
    handler_.postDelayed(new AudioPlayerPCMTimer(player_handle_), AudioPlayerPCMTimer.INTERVAL_MS);
  }

  return true;
}

简单来说,就是启动了个纯语音播放的实例,来处理过来的PCMA或PS的audio数据。

其中PlayerExternalPCMOutput()主要是把数据塞到GB28181数据采集处理的模块,来实现语音广播的回音消除的目的。

class PlayerExternalPCMOutput implements NTExternalAudioOutput {
  private int buffer_size_ = 0;
  private ByteBuffer pcm_buffer_ = null;

  @Override
  public ByteBuffer getPcmByteBuffer(int size)  {
    //Log.i("getPcmByteBuffer", "size: " + size);

    if(size < 1)
      return null;

    if(buffer_size_ != size) {
      buffer_size_ = size;
      pcm_buffer_ = ByteBuffer.allocateDirect(buffer_size_);
    }

    return pcm_buffer_;
  }

  public void onGetPcmFrame(int ret, int sampleRate, int channel, int sampleSize, int is_low_latency) {
    if (null == pcm_buffer_)
      return;

    pcm_buffer_.rewind();

    if (ret == 0 && isGB28181StreamRunning && publisherHandle != 0 )
      libPublisher.SmartPublisherOnFarEndPCMData(publisherHandle, pcm_buffer_, sampleRate, channel, sampleSize, is_low_latency);
  }
}

如果需要停止播放,调用以下逻辑即可:

private void stopAudioPlayer() {
  if (player_handle_ != 0 ) {
    lib_player_.SmartPlayerStopPullStream(player_handle_);
    lib_player_.SmartPlayerStopPlay(player_handle_);
    lib_player_.SmartPlayerClose(player_handle_);
    player_handle_ = 0;
  }
}

总结

GB28181语音广播这块,如果平台侧和终端,都是按照规范来实现的话,问题会少很多,实际尴尬的是,大厂或部分厂商先入为主,实际生产环境,不一定按照预期的,谁的问题谁处理,作为Android终端模块,push不动国标平台侧的时候,有时候只有兼容它,这种痛苦真是一言难尽。

 

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

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

相关文章

Docker介绍以及实战教程

Docker简介 Docker为什么出现 从事软件开发的朋友&#xff0c;可能经常会碰到以下场景&#xff1a;运维&#xff1a;你这程序有Bug啊&#xff0c;怎么跑不起来啊&#xff01;开发&#xff1a;我机子上能跑啊&#xff0c;你会不会用啊究其原因还是开发环境与生产环境不同造成的…

基于linux下的高并发服务器开发(第二章)- 2.18 内存映射(2)

1.如果对mmap的返回值(ptr)做操作(ptr), munmap是否能够成功? void * ptr mmap(...);ptr; 可以对其进行操作munmap(ptr, len); // 错误,要保存地址 2.如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样? 错误&#xff0c;返回MAP_FAILEDopen()函数中的…

cpolar+calibre搭建自己的kindle书库

cpolarcalibre搭建自己的kindle书库 在我们身边众多的便携电子设备中&#xff0c;Kindle无疑是最为矛盾的设备之一&#xff0c;很多人在买它时都想读书破万卷&#xff0c;可是到最后Kindle的归宿都是盖泡面。尽管如此&#xff0c;当亚马逊不讲武德&#xff0c;打算将Kindle真正…

FPGA中RAM的结构理解

FPGA中RAM的结构理解 看代码的过程中对RAM的结构不是很理解&#xff0c;搞脑子一片浆糊&#xff0c;反复推算&#xff0c;好不容易理清了思路&#xff0c;记录下来&#xff0c;防止忘记。开辟的RAM总容量为128bytes&#xff0c;数据的位宽为32位&#xff08;即一个单元有32bit…

CodeForces:Madoka and Underground Competitions

经过观察&#xff0c;发现只要延小区域 右上-左下 的对角线填满X即可&#xff0c;那么就是可以总结为满足(i j) % k (r c) % k #include <bits/stdc.h> using namespace std; int t; void solve(){int n, k, r, c;cin >> n >> k >> r >> c…

团队如何选择合适的Git分支策略?

现代软件开发过程中要实现高效的团队协作&#xff0c;需要使用代码分支管理工具实现代码的共享、追溯、回滚及维护等功能。目前流行的代码管理工具&#xff0c;包括CVS&#xff0c;SVN&#xff0c;Git&#xff0c;Mercurial等。 相比CVS和SVN的集中管理&#xff0c;Git具有非常…

【stable diffusion】保姆级入门课程03-Stable diffusion(SD)图生图-涂鸦(绘图)的用法

目录 0.本章素材 1.涂鸦有什么用 2.涂鸦的使用场景是什么 3.操作面板 4.提示词与涂鸦 5.涂鸦与重绘幅度 6.涂鸦的其他用法(自由创作) 7.课后训练 0.本章素材 Realistic Vision V3.0模型(真实系模型)百度网盘链接&#xff1a;https://pan.baidu.com/s/1HkSKW2t4L6wMg…

openlayers系列:加载arcgis和geoserver在线离线切片

https://www.freesion.com/article/1751396517/ 1.背景 有个项目需要使用openlayer加载各种服务上发布的数据&#xff0c;坐标系也不同&#xff0c;我们都知道openalyer默认可以加载EPAG:3857,要加载4490的坐标系的数据需要重新定义一下&#xff0c;之后再加载。一想起要重新…

[SQL系列] 从头开始学PostgreSQL 事务 锁 子查询

[SQL系列] 从头开始学PostgreSQL 索引 修改 视图_Edward.W的博客-CSDN博客https://blog.csdn.net/u013379032/article/details/131818865 事务 事务是一系列逻辑相关的数据库操作&#xff0c;可以作为一个整体进行操作或者回滚。事务通常会包含一个序列的读或者写操作&#xf…

No4: Python脚本的交互式运用

No4: Python脚本的交互式运用 1、 先安装了Python和环境变量设置 2、编写Pthon脚本 3、在脚本所在目录位置下&#xff0c;Python 脚本&#xff1b;

Nginx系列之 一 负载均衡

目录 一、Nginx概述 1.1 负载均衡概述 1.2 负载均衡的作用 1.3 四/七层负载均衡 1.3.1 网络模型简介 1.3.2 四层和七层负载均衡对比 1.3.3 Nginx七层负载均衡实现 1.4 Nginx负载均衡配置 1.5 Nginx负载均衡状态 1.6 Nginx负载均衡策略 二、负载均衡实战 2.1 测试服…

论文笔记--Won’t Get Fooled Again: Answering Questions with False Premises

论文笔记--Won’t Get Fooled Again: Answering Questions with False Premises 1. 文章简介2. 文章概括3 文章重点技术3.1 大模型面对FPQs的表现3.2 False QAs数据集3.3 训练和评估 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;Won’t Get Fooled Again: Answerin…

LLMs之LLaMA2:LLaMA2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略

LLMs之LLaMA2&#xff1a;LLaMA2的简介(技术细节)、安装、使用方法(开源-免费用于研究和商业用途)之详细攻略 导读&#xff1a;2023年7月18日&#xff0c;Meta重磅发布Llama 2&#xff01;这是一组预训练和微调的大型语言模型&#xff08;LLM&#xff09;&#xff0c;规模从70亿…

(三)springboot实战——web新特性之函数式实现

前言 本节内容我们主要介绍一下web访问的另一种形式&#xff0c;通过函数式web实现一个restful风格的http请求案例。函数式web是spring5.2之后的一个新特性&#xff0c;可以通过函数去定义web请求的处理流程&#xff0c;使得代码更为简洁&#xff0c;耦合性也降低了。 正文 …

在vue3中配置ByteMD掘金同款markdown编辑器

最近因为想要一个富文本编辑器集合到项目中&#xff0c;在查找网上很多资料后&#xff0c;选择了ByteMD 编辑器&#xff0c;ByteMD 编辑器是字节跳动的掘金团队所开源的一个编辑器组件&#xff0c;还挺好用的&#xff0c;那如果要在vue3项目中配置ByteMD编辑器要如何配置呢&…

【YOLO】关闭控制台推理日志

问题 每次推理时&#xff0c;控制台都会打一条日志 消除方法 外部库里找到\site-packages\ultralytics\engine\predictor.py 将代码的282、283、292、293&#xff0c;这四行注释掉&#xff08;v5和v8一样&#xff09; 也可以搜关键词# Print time (inference-only)可以定位代…

基于PHP+ vue2 + element +mysql自主研发的医院不良事件上报系统

医院不良事件上报管理系统源码 不良事件上报是为了响应卫生部下发的等级医院评审细则中第三章第9条规定&#xff1a;医院要有主动报告医疗安全&#xff08;不良&#xff09;事件的制度与工作流程。由医疗机构医院或医疗机构报告医疗安全不良事件信息&#xff0c;利用报告进行研…

WEB安全测试通常要考虑的测试点

1、问题&#xff1a;没有被验证的输入 测试方法&#xff1a; 数据类型&#xff08;字符串&#xff0c;整型&#xff0c;实数&#xff0c;等&#xff09; 允许的字符集 最小和最大的长度 是否允许空输入 参数是否是必须的 重复是否允许 数值范围 特定的值&#xff08;枚举型&a…

基于UDP的可靠传输,文件+目录(C++,Qt)

一、基础知识 UDP&#xff08;UserDatagramProtocol&#xff09;是一个简单的面向消息的传输层协议&#xff0c;尽管UDP提供标头和有效负载的完整性验证&#xff08;通过校验和&#xff09;&#xff0c;但它不保证向上层协议提供消息传递&#xff0c;并且UDP层在发送后不会保留…