跨平台轻量级RTSP服务模块设计思路及实现探讨

news2024/11/17 3:36:31

技术背景

为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,我们发布了轻量级RTSP服务模块,轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

以下是轻量级RTSP服务的一些特点:

1. 简单易用:SDK 提供了完整的 RTSP服务功能,包括音频格式、视频格式、传输模式、端口设置、鉴权设置等,开发者可以轻松地使用这些功能进行 RTSP服务开发。

2. 跨平台支持:SDK 支持 Windows、Android 和 iOS 平台,开发者可以在不同的平台上进行 RTSP服务开发,并且可以使用多种编程语言进行开发。

3. 音频和视频处理:SDK 提供了音频和视频的处理功能,包括采集麦克风、采集扬声器、音频格式转换、视频格式转换等,开发者可以使用这些功能实现音视频的采集和处理。

4. 摄像头和屏幕合成:SDK 支持摄像头和屏幕的实时切换,可以单画面显示摄像头或屏幕,并且支持摄像头和屏幕的合成处理,让开发者能够实现更多的监控和展示功能。

5. 音频处理:SDK 支持音频的噪音抑制、自动增益控制等功能,可以帮助开发者实现更好的音频处理效果。

6. 安全性:SDK 支持密码验证和鉴权设置,可以保护 RTSP服务的网络安全和数据安全。

7. 多服务支持:SDK 支持同时创建多个内置 RTSP 服务,并且支持 H.265 视频编码,可以帮助开发者实现更多的监控和服务需求。

功能支持:

  • [音频格式]AAC、PCMA;
  • [视频格式]H.264、H.265;
  • [协议类型]RTSP;
  • [传输模式]支持单播和组播模式;
  • [端口设置]支持RTSP端口设置;
  • [鉴权设置]支持RTSP鉴权用户名、密码设置;
  • [获取session连接数]支持获取当前RTSP服务会话连接数;
  • [多服务支持]支持同时创建多个内置RTSP服务;
  • [H.265支持]Windows内置rtsp server支持发布H.265视频(64位库);
  • [RTSP url回调]支持设置后的rtsp url通过event回调到上层。

接口设计

以Android平台为例,接口设计如下:

  /*
   * SmartPublisherJniV2.java
   * Author: daniusdk.com
   * Created on 2015/09/20.
   */
  /*
	 * Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)
	 *
	 * @param ctx: get by this.getApplicationContext()
	 *
	 * @return {0} if successful
	 */
	public native int InitRtspServer(Object ctx);

	/*
	 * 创建一个rtsp server
	 *
   	 * @param reserve:保留参数传0
	 *
	 * @return rtsp server 句柄
	 */
	public native long OpenRtspServer(int reserve);

	/*
	 * 设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口
	 *
   	 * @param rtsp_server_handle: rtsp server 句柄
	 *
   	 * @param port: 端口号,可以设置为554,或者是1024到65535之间,其他值返回失败
	 *
	 * @return {0} if successful
	 */
	public native int SetRtspServerPort(long rtsp_server_handle, int port);

	/*
	 * 设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置
	 *
   	 * @param rtsp_server_handle: rtsp server 句柄
	 *
   	 * @param user_name: 用户名(必须是英文)
	 * 
   	 * @param password:密码(必须是英文)
	 *
	 * @return {0} if successful
	 */
	public native int SetRtspServerUserNamePassword(long rtsp_server_handle, String user_name, String password);

	/*
	 * 设置rtsp server 组播, 如果server设置成组播就不能单播,组播和单播只能选一个, 一般来说单播网络设备支持的好,wifi组播很多路由器不支持
	 *
	 * @param rtsp_server_handle: rtsp server 句柄
	 *
	 * @param is_multicast: 是否组播, 1为组播, 0为单播, 其他值接口返回错误, 默认是单播
	 *
	 * @return {0} if successful
	 */
	public native int SetRtspServerMulticast(long rtsp_server_handle, int is_multicast);

	/*
	 * 设置rtsp server 组播组播地址
	 *
	 * @param rtsp_server_handle: rtsp server 句柄
	 *
	 * @param multicast_address: 组播地址
	 *
	 * 如果设置的不是组播地址, 将返回错误
	 * 组播地址范围说明: [224.0.0.0, 224.0.0.255] 为组播预留地址, 不能设置. 可设置范围为[224.0.1.0, 239.255.255.255], 其中SSM地址范围为[232.0.0.0, 232.255.255.255]
	 *
	 *  @return {0} if successful
	 */
	public native int SetRtspServerMulticastAddress(long rtsp_server_handle, String multicast_address);

	/*
	 * 获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用
	 *
   	 * @param rtsp_server_handle: rtsp server 句柄
	 *
	 * @return {当前rtsp server会话数}
	 */
	public native int GetRtspServerClientSessionNumbers(long rtsp_server_handle);

	/*
	 * 启动rtsp server
	 *
   	 * @param rtsp_server_handle: rtsp server 句柄
	 *
   	 * @param reserve: 保留参数传0
	 *
	 * @return {0} if successful
	 */
	public native int StartRtspServer(long rtsp_server_handle, int reserve);

	/*
	 * 停止rtsp server
	 *
   	 * @param rtsp_server_handle: rtsp server 句柄
	 *
	 * @return {0} if successful
	 */
	public native int StopRtspServer(long rtsp_server_handle);

	/*
	 * 关闭rtsp server
	 *
	 * @param rtsp_server_handle: rtsp server 句柄
	 *
	 * NOTE: 调用这个接口之后rtsp_server_handle失效,
	 *
	 * @return {0} if successful
	 */
	public native int CloseRtspServer(long rtsp_server_handle);

	/*
	 * UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)
	 *
	 * @return {0} if successful
	 */
	public native int UnInitRtspServer();

	/*
	 * 设置rtsp的流名称
	 *
	 * @param handle: 推送实例句柄
	 *
   	 * @param stream_name: 流程名称,不能为空字符串,必须是英文
	 *
	 * 这个作用是: 比如rtsp的url是:rtsp://192.168.0.111/test, test就是设置下去的stream_name
	 *
	 * @return {0} if successful
	 */
	public native int SetRtspStreamName(long handle, String stream_name);

	/*
	 * 给要发布的rtsp流设置rtsp server, 一个流可以发布到多个rtsp server上,rtsp server的创建启动请参考OpenRtspServer和StartRtspServer接口
	 *
   	 * @param handle: 推送实例句柄
	 *
   	 * @param rtsp_server_handle:rtsp server句柄
   	 *
	 * @param reserve:保留参数,传0
	 *
	 * @return {0} if successful
	 */
	public native int AddRtspStreamServer(long handle, long rtsp_server_handle, int reserve);

	/*
	 * 清除设置的rtsp server
	 *
	 * @param handle: 推送实例句柄
	 *
	 * @return {0} if successful
	 */
	public native int ClearRtspStreamServer(long handle);

	/*
	 * 启动rtsp流
	 *
	 * @param handle: 推送实例句柄
	 *
	 * @param reserve: 保留参数,传0
	 *
	 * @return {0} if successful
	 */
	public native int StartRtspStream(long handle, int reserve);

	/*
	 * 停止rtsp流
	 *
	 * @param handle: 推送实例句柄
	 *
	 * @return {0} if successful
	 */
	public native int StopRtspStream(long handle);

接口调用示例

启动、停止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实例失败! 请检查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会话数弹出框
private void PopRtspSessionNumberDialog(int session_numbers) {
  final EditText inputUrlTxt = new EditText(this);
  inputUrlTxt.setFocusable(true);
  inputUrlTxt.setEnabled(false);

  String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers;
  inputUrlTxt.setText(session_numbers_tag);

  AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
  builderUrl
    .setTitle("内置RTSP服务")
    .setView(inputUrlTxt).setNegativeButton("确定", null);
  builderUrl.show();
}

总结

轻量级RTSP服务模块是一个跨平台的、简单易用的 RTSP 服务开发工具包,可以帮助开发者快速实现 RTSP 服务,并且提供多种音视频处理和安全性保障功能,无需单独部署RTSP服务,在内网环境特别是移动端,非常方便。

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

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

相关文章

(4)【轨迹优化篇】方法一:基于Frenet车道线坐标系,采用解耦采样五次多项式拟合进行局部规划

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言A、第一章:Frenet车道线坐标系介绍及坐标转换一、frenet坐标系介绍1.Frenet坐标的定义2.ST图(纵向速度…

【ARM Coresight 系列文章 3 - ARM Coresight 组件 DAP(Debug Access Port) 介绍】

文章目录 1.1 Debug Access Port1.2 Access Port1.2.1 IDR 寄存器 1.3 Mem-APs 介绍1.3.1 Debug 寄存器访问模型1.3.2 APs 中寄存器的介绍 1.1 Debug Access Port 外部 Debugger(DS-5/Trace32) 会通过JTAG接口或者SWD接口和DAP相连: JITAG 一般是5个pin&#xff1…

JAVA每日一练(1)

【程序1】 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? import java.util.Scanner;/*…

【ElasticSearch】数据聚合语法与Java实现

文章目录 1、聚合的分类2、DSL实现bucket聚合3、DSL实现Metrics 聚合4、RestClient实现聚合5、需求:返回过滤条件的信息6、带过滤条件的聚合 1、聚合的分类 聚合(aggregations)可以实现对文档数据的统计、分析、运算。(类比MySQL…

如何记牢托福口语考试的关键词?

一般情况下,托福独立口语一类问题是自由回答间题(Free-choice Response),如:If you could have any job in the world, what would it be? Use details to support your. response;另一类是选择类问题(Paired-choice Response),如…

BERT论文解读及实现(一)

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 1 论文解读 1.1 模型概览 There are two steps in our framework: pre-training and fine-tuning. bert由预训练模型微调模型组成。 ① pre-training, the model is trained on unlabele…

前端Vue入门-day01

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 Vue 快速上手 Vue 概念 创建实例 插值表达式 响应式特性 开发者工具 Vue 指令 v-show v-if …

【Spring Boot】第一个Spring Boot项目:helloworld

第一个Spring Boot项目:helloworld 本节从简单的helloworld程序开始介绍创建Spring Boot项目的方法和流程,以及Spring Boot项目结构,最后介绍项目中非常重要的pom.xml文件。 1.创建Spring Boot项目 有两种方式来构建Spring Boot项目的基础…

【1++的C++初阶】之string

👍作者主页:进击的1 🤩 专栏链接:【1的C初阶】 文章目录 一,浅谈string类二,string 类常用接口2.1 string的构造2.2 string类对象的容量操作2.3 string类对象的访问及遍历操作2.4 string类对象的修改操作2.…

Python 有趣的模块之pynupt——通过pynput控制鼠标和键盘

Python 有趣的模块之pynupt ——通过pynput控制鼠标和键盘 文章目录 Python 有趣的模块之pynupt ——通过pynput控制鼠标和键盘1️⃣简介2️⃣鼠标控制与移动3️⃣键盘控制与输入4️⃣结语📢 1️⃣简介 🚀🚀🚀学会控制鼠标和键盘是…

Mongodb连接数据库

1.初始化 npm init 2.安装mongoose npm i mongoose 3.导入mongoose const mongooserequire("mongoose") 4.连接mongodb服务 mongoose.connect("mongodb://127.0.0.1:27017/user") 说明:mongodb是协议,user是数据库,如果没有会自动创…

经OPA运放后,读取电压出错

问题: 在焊接完两块板子上传程序测试时,程序上传完成,有一块板子在使用OPA读取电压时,在未插入电阻情况下,电压读取是正确的,在插入50K电压后,电压值应该是之前的两倍,但是电压变化…

unittest单元测试2

目录 unittest框架解析 构建测试套件 用例的执行顺序 unittest断言 HTML报告生成 异常捕捉与错误截图 数据驱动 🎁更多干货 完整版文档下载方式: unittest框架解析 unittest 是python 的单元测试框架,unittest 单元测试提供了创建测…

怎么把CAJ转换成PDF文件格式?分享这两个方法!

随着互联网的发展,中国知网(CNKI)已成为许多学术研究人员和学生们获取文献资料的重要来源。在CNKI上,常见的文件格式是CAJ(China Academic Journals)。然而,由于个人喜好或特定需求,我们有时会希望将这些CA…

PDF文档转化为HTML网页格式怎么操作?分享这三个方法给大家!

PDF文档作为一种常见的文档格式,广泛应用于各个领域。然而,如果您想将PDF文档直接发布到网站上,或是想在网页上进行展示,您可能需要将PDF转化为HTML格式。在此,我为大家介绍三种将PDF转化为HTML格式的方法。 方法一&am…

mysql语句练习题,创建表create ,枚举中文字符集设置,修改(update)

作业: 1.创建表: 创建员工表employee,字段如下: id(员工编号),name(员工名字),gender(员工性别),salary(员工薪…

d3dx9_43.dll丢失怎么解决(分享三个解决方法)

d3dx9_43.dll是一个Microsoft DirectX的动态链接库文件,它包含了一系列用于图形、音频和输入的功能和接口。它是DirectX 9的一部分,用于提供游戏和其他图形应用程序所需的图形和声音效果。如果计算机中d3dx9_43.dll丢失,会造成很多游戏无法打…

opencv图片根据规则改变颜色

解析 1. 读入图片 2.通道分离 3.像素值在【100,200】之间,赋值128。大于200赋值255,小于100赋值0。 源码 import cv2 img_raw_path"past/unet-test_result0-0-1-0.png" img_rawcv2.imread(img_raw_path) (r,g,b)cv2.split(img_…

运动控制介绍

运动控制介绍 1 介绍1.1 概述1.2 运动控制的基本架构1.3 常见的控制功能1.4 运动控制研究的问题分类位置变化问题周期式旋转速度变化问题 1.5 知识体系1.6 路径规划 和 轨迹规划区别与联系1.7 运动控制系统 2 《运动控制系统》[班华 李长友 主编] 摘要1 绪论1.1 运动控制研究的…

信息系统项目管理师(第四版)教材精读思维导图-第二章信息技术发展

请参阅我的另一篇文章,综合介绍软考高项: 信息系统项目管理师(软考高项)备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 思维导图源文件下载: https://download.csdn.net/download/hanjingjava/88023847 …