Android平台GB28181设备接入侧如何同时对外输出RTSP流?

news2024/11/17 13:43:48

技术背景

GB28181的应用场景非常广泛,如公共安全、交通管理、企业安全、教育、医疗等众多领域,细分场景可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等:

  1. 公共安全:通过GB28181协议,用户可以实时监控特定区域的视频画面,从而提高公共安全水平。
  2. 交通管理:GB28181可用于交通监控系统,帮助交通部门实时监控道路交通情况,提高交通管理效率。
  3. 企业安全:GB28181可以用于构建企业视频监控系统,保护企业资产,提高安全工作效率。
  4. 教育:通过GB28181协议,用户可以进行远程视频会议和教学,为学生提供更为灵活的学习方式。
  5. 医疗:GB28181可以用于医疗领域的视频监控,提高医疗安全和管理效率。

技术实现

本文以Android平台GB28181设备接入模块为例,谈谈具体实现,还有如何对外输出RTSP流。

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲、云台控制回调和预置位查询,支持对接数据类型如下:

  • 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型);
  • 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  • 拉取RTSP或RTMP流并接入至GB28181平台(比如其他IPC的RTSP流,可通过Android平台GB28181接入到国标平台)。

技术设计架构图:

功能设计:

  • ​ [视频格式]H.264/H.265(Android H.265硬编码);
  •  [音频格式]G.711 A律、AAC;
  •  [音量调节]Android平台采集端支持实时音量调节;
  •  [H.264硬编码]支持H.264特定机型硬编码;
  •  [H.265硬编码]支持H.265特定机型硬编码;
  •  [软硬编码参数配置]支持gop间隔、帧率、bit-rate设置;
  •  [软编码参数配置]支持软编码profile、软编码速度、可变码率设置;
  • 支持纯视频、音视频PS打包传输;
  • 支持RTP OVER UDP和RTP OVER TCP被动模式(TCP媒体流传输客户端);
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持设备目录查询应答;
  • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  • 支持语音广播;
  • 支持语音对讲;
  • 支持云台控制和预置位查询;
  •  [实时水印]支持动态文字水印、png水印;
  •  [镜像]Android平台支持前置摄像头实时镜像功能;
  •  [实时静音]支持实时静音/取消静音;
  •  [实时快照]支持实时快照;
  •  [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  •  [外部编码前视频数据对接]支持YUV数据对接;
  •  [外部编码前音频数据对接]支持PCM对接;
  •  [外部编码后视频数据对接]支持外部H.264数据对接;
  •  [外部编码后音频数据对接]外部AAC数据对接;
  •  [扩展录像功能]支持和录像模块组合使用,录像相关功能。​

Android平台GB28181设备接入模块,除了上述的功能点外,我们遇到的诉求有,如何同时对外输出RTSP,供如内网平台预览播放?

这里就提到了轻量级RTSP服务,音视频数据源过来后,编码分别注入GB28181模块和轻量级RTSP服务模块,如果需要做到对外输出RTSP流,只需要启动RTSP服务,然后发布RTSP流即可,具体的操作如下:

启动、停止RTSP服务:

//启动/停止RTSP服务
class ButtonRtspServiceListener implements View.OnClickListener {
  public void onClick(View v) {
    if (isRTSPServiceRunning) {
      stopRtspService();

      btnRtspService.setText("启动RTSP服务");
      btnRtspPublisher.setEnabled(false);

      isRTSPServiceRunning = false;
      return;
    }

    Log.i(TAG, "onClick start rtsp service..");

    rtsp_handle_ = libPublisher.OpenRtspServer(0);

    if (rtsp_handle_ == 0) {
      Log.e(TAG, "创建rtsp server实例失败! 请联系 https://daniusdk.com 检查SDK有效性");
    } else {
      int port = 8554;
      if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {
        libPublisher.CloseRtspServer(rtsp_handle_);
        rtsp_handle_ = 0;
        Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
      }

      if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {
        Log.i(TAG, "启动rtsp server 成功!");
      } else {
        libPublisher.CloseRtspServer(rtsp_handle_);
        rtsp_handle_ = 0;
        Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
      }

      btnRtspService.setText("停止RTSP服务");
      btnRtspPublisher.setEnabled(true);

      isRTSPServiceRunning = true;
    }
  }
}

发布、停止RTSP流:

//发布/停止RTSP流
class ButtonRtspPublisherListener implements View.OnClickListener {
  public void onClick(View v) {
    if (isRTSPPublisherRunning) {
      stopRtspPublisher();

      btnRtspPublisher.setText("发布RTSP流");
      btnGetRtspSessionNumbers.setEnabled(false);
      btnRtspService.setEnabled(true);
      return;
    }

    Log.i(TAG, "onClick start rtsp publisher..");

    if (!isPushingRtmp && !isGB28181StreamRunning && !isRecording) {
      InitAndSetConfig();
    }

    if (publisherHandle == 0) {
      Log.e(TAG, "Start rtsp publisher, publisherHandle is null..");
      return;
    }

    String rtsp_stream_name = "stream1";
    libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);
    libPublisher.ClearRtspStreamServer(publisherHandle);

    libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);

    if (libPublisher.StartRtspStream(publisherHandle, 0) != 0) {
      Log.e(TAG, "调用发布rtsp流接口失败!");
      return;
    }

    if (!isPushingRtmp && !isGB28181StreamRunning && !isRecording) {
      CheckInitAudioRecorder();    //enable pure video publisher..
    }

    startLayerPostThread();

    btnRtspPublisher.setText("停止RTSP流");
    btnGetRtspSessionNumbers.setEnabled(true);
    btnRtspService.setEnabled(false);
    isRTSPPublisherRunning = true;
  }
}

获取RTSP链接数:

//获取RTSP会话数
class ButtonGetRtspSessionNumbersListener implements View.OnClickListener {
  public void onClick(View v) {
    if (libPublisher != null && rtsp_handle_ != 0) {
      int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);

      Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);

      PopRtspSessionNumberDialog(session_numbers);
    }
  }
}

获取回调上来的RTSP URL,对应的事件ID为EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL

private static class EventHandlerPublisherV2 implements NTSmartEventCallbackV2 {
  @Override
  public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

    Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

    String publisher_event = "";

    switch (id) {
     .....
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
        publisher_event = "开始一个新的录像文件 : " + param3;
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
        if (recorder_io_executor_ != null) {
          ExecutorService executor = recorder_io_executor_.get();
          if (executor != null)
            executor.execute(new RecordFileFinishedHandler().set(handle, param3, param1));
        }
        publisher_event = "已生成一个录像文件 : " + param3;
        break;

      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
        publisher_event = "发送时延: " + param1 + " 帧数:" + param2;
        break;

      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
        publisher_event = "快照: " + param1 + " 路径:" + param3;

        if (param1 == 0) {
          publisher_event = publisher_event + "截取快照成功..";
        } else {
          publisher_event = publisher_event + "截取快照失败..";
        }
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
        publisher_event = "RTSP服务URL: " + param3;
        break;
    }

    String str = "当前回调状态:" + publisher_event;

    Log.i(TAG, str);

    if (handler_ != null) {
      android.os.Handler handler = handler_.get();
      if (handler != null) {
        Message message = new Message();
        message.what = PUBLISHER_EVENT_MSG;
        message.obj = publisher_event;
        handler.sendMessage(message);
      }
    }
  }

  public NTSmartEventCallbackV2 set(android.os.Handler handler, ExecutorService recorder_io_executor) {
    this.handler_ = new WeakReference<>(handler);
    this.recorder_io_executor_ = new WeakReference<>(recorder_io_executor);
    return this;
  }

  private WeakReference<android.os.Handler> handler_;
  private WeakReference<ExecutorService> recorder_io_executor_;
}

总结

GB28181设备接入模块同时输出RTSP流的话,需要注意的是,在一个实例里面完成,确保只编码一路音视频数据,然后分别打包注入两个模块,尽可能的降低设备性能消耗。

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

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

相关文章

云原生架构

1. 何为云原生&#xff1f; 很多IT业内小伙伴会经常听到这个名词&#xff0c;那么什么是云原生呢&#xff1f;云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。 当今时代&#xff0c;众多企业希望构建高度可扩展、灵活且有弹性的应用程序&#xff0c;以便能够快…

Linux CentOS 8 编译安装Apache Subversion

前言 距离上一篇发表已经过去了5年零2个多月&#xff0c;这次重新开始写技术博客&#xff0c;理由和原来一样&#xff0c;也就是想把自己学习和工作中遇到的问题和知识记录下来&#xff0c;今天记录一下Linux CentOS 8通过编译安装svn的过程。 下载SVN 下载地址&#xff1a;…

使用frp中的xtcp映射穿透指定服务实现不依赖公网ip网速的内网穿透p2p

使用frp中的xtcp映射穿透指定服务实现不依赖公网ip网速的内网穿透p2p 管理员Ubuntu配置公网服务端frps配置service自启(可选) 配置内网服务端frpc配置service自启(可选) 使用者配置service自启(可选) 效果 通过frp实现内网client访问另外一个内网服务器 管理员 1&#xff09;…

PHP8的注释-PHP8知识详解

欢迎你来到PHP服务网&#xff0c;学习《PHP8知识详解》系列教程&#xff0c;本文学习的是《PHP8的注释》。 什么是注释&#xff1f; 注释是在程序代码中添加的文本&#xff0c;用于解释和说明代码的功能、逻辑或其他相关信息。注释通常不会被编译器或解释器处理&#xff0c;而…

深度学习实战44-Keras框架下实现高中数学题目的智能分类功能应用

大家好,我是微学AI ,今天给大家介绍一下深度学习实战44-Keras框架实现高中数学题目的智能分类功能应用,该功能是基于人工智能技术的创新应用,通过对数学题目进行智能分类,提供个性化的学习辅助和教学支持。该功能的实现可以通过以下步骤:首先,采集大量的高中数学题目数据…

Clion实现Stm32标准库-HAL库开发配置

1、配置CLion用于STM32开发&#xff08;基于hal库开发&#xff09; 原文链接 https://zhuanlan.zhihu.com/p/145801160 2、配置CLion用于STM32开发&#xff08;基于标准库开发&#xff09; 参考文章&#xff1a;https://www.bilibili.com/read/cv11442303

不再困扰!教你如何方便快捷地将文件转换成PDF格式!

大家都知道PDF是电脑中最常见且兼容性最好的一种文档文件格式&#xff0c;而这种格式的文件既能保留文档的原始布局&#xff0c;又能够方便阅读和打印。那么&#xff0c;当我们需要将其他格式的文件转换成PDF格式时应该如何处理呢&#xff1f;我们可以使用一款文档转换工具&…

【QT 网络云盘客户端】——获取用户文件列表信息

目录 1.获取用户文件列表信息分析 2.设置图标属性 3.向服务器获取文件的数量 4.向服务器获取文件信息列表 4.显示图标 1.获取用户文件列表信息分析 1.将QListWidget设置为图标模式 2. 当我们点击"按下载量升序","按下载量降序",“更新” 菜单选项 都会…

3、HAproxy高级配置

基于cookie的会话保持 在 HAProxy 中&#xff0c;可以通过使用 cookie 配置来实现基于 Cookie 的会话保持。cookie 配置用于配置与会话保持相关的选项&#xff0c;允许您定义要在HTTP响应中插入或重写的Cookie以及其他与Cookie会话保持相关的参数。 以下是一些常用的 cookie 配…

已解决 IDEA Maven 项目中 “Could not find artifact“ 问题的常见情况和解决方案

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

QT第四讲

思维导图 基于QT的网络聊天室 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器类 #include<QTcpSocket> //客户端类 #include<QMessageBox> //对话框类 #include<QList…

Windows使用Notepad++编辑Linux服务器的文件

&#x1f680; Windows使用Notepad编辑Linux服务器的文件 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介…

<el-date-picker>组件选择开始时间,结束时间自动延长30min

背景&#xff1a;选择开始时间&#xff0c;结束时间自动增加30分钟&#xff0c;结束时间也可重新选择&#xff0c;如图&#xff1a; <el-form-item label"预约开始时间" prop"value1"><el-date-pickersize"large"v-model"ruleForm…

Vue2 第三节 数据代理和事件处理

1.Object.defineProperty 方法 2.数据代理 3.Vue中的数据代理 4.事件的基本使用 5.事件修饰符 6.键盘事件 一.Object.defineProperty 方法 &#xff08;1&#xff09;学习Object.defineProperty为下一节数据代理做准备 &#xff08;2&#xff09;更加高级的给对象添加属…

两个csv进行根据相同字段进行合并

源文件&#xff0c;第一列&#xff0c;编号0 目标文件&#xff0c; 编号3 根据社区名称进行匹配&#xff0c;然后将第一个csv文件的经纬度添加到第二个文件中。 import csvsource r"D:\000datasets\链家房价数据\2020去重后社区名称地理编码.csv" target r"…

TAURI桌面平台制作应用程序工具包初体验

原文&#xff1a;https://tauri.app/zh-cn/v1/guides/getting-started/setup/html-css-js 前提&#xff1a;https://tauri.app/zh-cn/v1/guides/getting-started/prerequisites 步骤1&#xff1a; 在 D:\MyStudy\TauriDemo 文件夹下新建 ui 文件夹&#xff0c;在 ui 文件夹中…

QChart笔记5:Polar Chart极坐标用法

QChart还有专门画极坐标的类QPolarChart&#xff0c;它的界面是一个圆盘。 #include <QApplication> #include <QDebug> #include <QtCharts/QScatterSeries> #include <QtCharts/QLineSeries> #include <QtCharts/QPolarChart> #include <Q…

HR:“抱歉不合适!”,竟是没有满足这项硬性要求?

兼容性测试主要通过人工或自动化的方式&#xff0c;在需要覆盖的终端设备上进行功能用例执行&#xff0c;查看软件性能、稳定性等是否正常。 对于需要覆盖的终端设备&#xff0c;大型互联网公司&#xff0c;像 BAT&#xff0c;基本都有自己的测试实验室&#xff0c;拥有大量终…

资深测试总结,自动化测试-ddt数据驱动yaml文件实战(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 ddt 驱动 yaml/ym…

matplotlib从起点出发(5)_Tutorial_5_sytleSheets

1 使用样式表和rcParams自定义matplotlib 以下是自定义matplotlib的属性和默认样式的提示。 有三种方式来自定义matplotlib: 在运行时设置rcParams&#xff1b;使用样式表&#xff1b;改写matplotlibrc文件。 在运行时设置rcParams优先于样式表&#xff0c;样式表优先于matp…