Windows平台Unity Camera场景实现轻量级RTSP服务和RTMP推送
技术背景
随着VR技术在医疗、军事、农业、学校、景区、消防、公共安全、研学机构、展厅展馆,商场等场所普及,开发者对Unity平台下的直播体验提出了更高的要求。
技术实现
Unity平台下的RTMP推流、RTMP、RTSP播放前几年已经覆盖了Windows、Linux、Android、iOS平台。本文主要介绍Windows平台Unity环境下的轻量级RTSP服务。通过对外提供RTSP拉流URL的形式,供内网其他终端调用。
RTMP的技术方案,我们之前有探讨过,这里先说轻量级RTSP服务,轻量级RTSP服务,我们的设计是,可以启动一个RTSP Service,然后发布多个RTSP流实例,这个在多实例的设计,非常有价值,简单来说,一个RTSP Service下面挂载多个RTSP Stream,对外提供RTSP拉流的URL,整体设计方案如下:

我们看看支持的音视频采集选项,其中视频这块,除了Unity下的Camera场景覆盖,还有Windows摄像头、屏幕数据,音频采集覆盖了Unity声音、扬声器、麦克风,还有混音数据。
音视频原始数据采集到后,编码注入RTSP服务和RTMP推送模块。二者可以单独使用,也可同时使用。其中轻量级RTSP服务,可实时查看链接的RTSP会话数。

首先看启动RTSP service封装:
/** PublisherWrapper.cs* Author: daniusdk.com*/
public bool StartRtspService()
{if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_OpenRtspServer(ref rtsp_handle_, 0)){Debug.LogError("创建rtsp server实例失败! 请检查sdk有效性.");return false;}if (IntPtr.Zero == rtsp_handle_){Debug.LogError("创建rtsp server实例失败! 请检查sdk有效性.");return false;}int port = 28554;if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_SetRtspServerPort(rtsp_handle_, port)){NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);rtsp_handle_ = IntPtr.Zero;Debug.LogError("设置rtsp server端口失败,请检查端口是否重复或者端口不在范围内!");return false;}//String user_name = "admin";//String password = "123456";//NTSmartPublisherSDK.NT_PB_SetRtspServerUserNamePassword(rtsp_handle, user_name, password);if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_StartRtspServer(rtsp_handle_, 0)){Debug.Log("StartRtspServer suc..");}else{NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);rtsp_handle_ = IntPtr.Zero;Debug.LogError("启动rtsp server失败, 请检查设置的端口是否被占用!");return false;}is_rtsp_service_running_ = true;return true;
}
停止RTSP Service:
public void StopRtspService()
{if (is_rtsp_service_running_ == false) return;NTSmartPublisherSDK.NT_PB_StopRtspServer(rtsp_handle_);NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);rtsp_handle_ = IntPtr.Zero;is_rtsp_service_running_ = false;
}
服务启动后,可以发布或停止RTSP流:
public bool StartRtspStream()
{if (CheckPublisherHandleAvailable() == false) return false;if (publisher_handle_ == IntPtr.Zero){return false;}if (publisher_handle_count_ < 1){SetCommonOptionToPublisherSDK();}String rtsp_stream_name = "stream1";NTSmartPublisherSDK.NT_PB_SetRtspStreamName(publisher_handle_, rtsp_stream_name);NTSmartPublisherSDK.NT_PB_ClearRtspStreamServer(publisher_handle_);NTSmartPublisherSDK.NT_PB_AddRtspStreamServer(publisher_handle_, rtsp_handle_, 0);if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRtspStream(publisher_handle_, 0)){if (0 == publisher_handle_count_){NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);publisher_handle_ = IntPtr.Zero;}Debug.LogError("调用发布rtsp流接口失败");return false;}publisher_handle_count_++;is_rtsp_publisher_running_ = true;return true;
}
停止RTSP流:
public void StopRtspStream()
{publisher_handle_count_--;NTSmartPublisherSDK.NT_PB_StopRtspStream(publisher_handle_);if (0 == publisher_handle_count_){NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);publisher_handle_ = IntPtr.Zero;}is_rtsp_publisher_running_ = false;
}
获取RTSP session连接数:
public int GetRtspSessionNumbers()
{int num = 0;if (rtsp_handle_!=IntPtr.Zero){if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetRtspServerClientSessionNumbers(rtsp_handle_, ref num)){Debug.LogError("Call NT_PB_GetRtspServerClientSessionNumbers failed..");}}return num;
}
封装部分看过后,我们看看我们Unity下调用示例:
启动、停止RTSP服务:
public void btn_rtsp_service_Click()
{if (publisher_wrapper_.IsRtspServiceRunning()){publisher_wrapper_.StopRtspService();btn_rtsp_service_.GetComponentInChildren<Text>().text = "启动RTSP服务";btn_rtsp_publisher_.interactable = false;return;}if (!publisher_wrapper_.StartRtspService()){Debug.LogError("调用StartRtspService失败..");return;}btn_rtsp_publisher_.interactable = true;btn_rtsp_service_.GetComponentInChildren<Text>().text = "停止RTSP服务";
}
发布、停止RTSP流:
public void btn_rtsp_publisher_Click()
{if (publisher_wrapper_.IsRtspPublisherRunning()){publisher_wrapper_.StopRtspStream();if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp()){StopCaptureAvData();if (coroutine_ != null){StopCoroutine(coroutine_);coroutine_ = null;}}btn_rtsp_service_.interactable = true;btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "发布RTSP";}else{if (!publisher_wrapper_.IsRtspServiceRunning()){Debug.LogError("RTSP service is not running..");return;}if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp()){publisher_wrapper_.SetVideoPushType(video_push_type_);publisher_wrapper_.SetAudioPushType(audio_push_type_);}publisher_wrapper_.StartRtspStream();if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "停止RTSP";btn_rtsp_service_.interactable = false;}
}
获取RTSP Session链接数:
public void btn_get_rtsp_session_numbers_Click()
{if (publisher_wrapper_.IsRtspServiceRunning()){btn_get_rtsp_session_numbers_.GetComponentInChildren<Text>().text = "RTSP会话数:" + publisher_wrapper_.GetRtspSessionNumbers();}
}
RTMP推送、停止推送:
public void btn_start_rtmp_pusher_Click()
{if (publisher_wrapper_.IsPushingRtmp()){StopPushRTMP();btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "推送RTMP";return;}String url = rtmp_pusher_url_.text;if (url.Length < 8){publisher_wrapper_.Close();Debug.LogError("请输入RTMP推送地址");return;}if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning()){publisher_wrapper_.SetVideoPushType(video_push_type_);publisher_wrapper_.SetAudioPushType(audio_push_type_);}if (!publisher_wrapper_.StartRtmpPusher(url)){Debug.LogError("调用StartPublisher失败..");return;}btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "停止推送";if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}
}
总结
轻量级RTSP服务和RTMP推送的区别在于,轻量级RTSP服务不需要单独部署流媒体服务器(类似于网络摄像头),在内网小并发场景下,使用起来非常方便,如果需要上公网,还是需要用RTMP推送,感兴趣的开发者可酌情参考。
相关文章:

Windows平台Unity Camera场景实现轻量级RTSP服务和RTMP推送
技术背景随着VR技术在医疗、军事、农业、学校、景区、消防、公共安全、研学机构、展厅展馆,商场等场所普及,开发者对Unity平台下的直播体验提出了更高的要求。技术实现Unity平台下的RTMP推流、RTMP、RTSP播放前几年已经覆盖了Windows、Linux、Android、i…...

LSB 题解
今天来刷一道Misc的题目,LSB原理进行图片隐写 LSB原理 LSB是一种利用人类视觉的局限性设计的幻术 PNG和BMP图片中的图像像素一般是由RGB(RED红 GREEN绿 BLUE蓝)三原色组成 记住,JPG图片是不适合使用LSB隐写的,JPG图片对像数进行了有损压缩…...
离线部署docker与镜像
离线部署docker与镜像 1.离线部署docker 1).在docker官网上下载,合适的安装文件 本次使用的是“docker-20.10.9.tgz ” 下载地址:https://download.docker.com/linux/static/stable/x86_64/ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下…...
Linux文件系统介绍(上)
使用 Linux 系统时,需要作出的决策之一就是为存储设备选用什么文件系统。大多数 Linux 发行版在安装时会非常贴心地提供默认的文件系统,大多数入门级用户想都不想就用了默认的那个。 使用默认文件系统未必就不好,但了解一下可用的选择有时也会…...

创建SpringBoot注意事项
作为一个java小白,你是否因为创建SpringBoot项目那些莫名其妙的错误搞得头皮发麻。不要慌张,这篇文章能帮你解决90%的问题【持续更新…】 本文结合创建SpringBoot项目的完整过程来讲 在idea中新建项目 虽然SpringBoot项目是由maven内核组成的࿰…...
2023年全国最新二级建造师精选真题及答案9
百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 11.关于施工合同违约赔偿损失范围的说法,正确的是()。 A.…...

解决MySQL的 Row size too large (> 8126).
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:无尽的折腾后,终于又回到…...
最优传输问题和Sinkhorn
最优传输问题 假设有M堆土,每堆土的大小是ama_mam,有N个坑,每个坑的大小是bnb_nbn,把单位土从土堆m运送到坑n的代价是c(m,n)c(m,n)c(m,n),如何找到一种运输方法填满坑,并且代价最小,这就是…...

Netty核心组件EventLoop源码解析
源码解析目标 分析最核心组件EventLoop在Netty运行过程中所参与的事情,以及具体实现 源码解析 依然用netty包example下Echo目录下的案例代码,单我们写一个NettyServer时候,第一句话就是 EventLoopGroup bossGroup new NioEventLoopGroup(…...
排障命令-汇总
目录 日志查询 1. grep 2. zgrep cpu 1. top 内存 1. free tcp相关 1. netstat 2. ulimit 3. lsof jvm常用 1. jps 2. jinfo 3. jstack 4. jmap 5. jstat 进制转换 1. 十进制转16进制 日志查询 1. grep 定义:(global regular expression) 命令用于查…...

python+pytest接口自动化(4)-requests发送get请求
python中用于请求http接口的有自带的urllib和第三方库requests,但 urllib 写法稍微有点繁琐,所以在进行接口自动化测试过程中,一般使用更为简洁且功能强大的 requests 库。下面我们使用 requests 库发送get请求。requests库简介requests 库中…...

开源电子书工具Calibre 6.3 发布
Calibre 开源项目是 Calibre 官方出的电子书管理工具。它可以查看,转换,编辑和分类所有主流格式的电子书。Calibre 是个跨平台软件,可以在 Linux、Windows 和 macOS 上运行。Calibre 6.3 正式发布,此次更新内容如下:新…...
C++ STL:适配器 Adapter
文章目录1、容器适配器1.1、stack1.2、queue1.3、priority_queue2、迭代器适配器2.1、插入迭代器2.2、反向迭代器2.3、流迭代器3、函数适配器3.1、* bindbind 使用方法bind 简化原理3.2、mem_fn适配器就是接口,对容器、迭代器、算法进行包装,但其实质还是…...
防抖和节流
防抖和节流的区别?防抖:触发高频事件后n 秒内 函数只会执行一次,如果n秒内 高频事件在在次触发,则会重新计算节流:高频事件触发,但在n 秒内 只会执行一次,所以节流会稀释函数的执行频率下面就是…...
vue3 微信扫码登录及获取个人信息实现的三种方法
一、流程: 微信提供的扫码方式有两种,分别是: 跳转二维码扫描页面 内嵌式二维码根据文档我们可以知道关于扫码授权的模式整体流程为: 1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站&…...

Java8 新特性强大的Stream API
一、Stream API 说明 Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。 Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Ja…...

day22_IO
今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、作业 二、缓冲流 三、字符流 四、缓冲字符流 五、匿名内部类 零、 复习昨日 File: 通过路径代表一个文件或目录 方法: 创建型,查找类,判断类,其他 IO …...

第三十八章 linux-并发解决方法二(信号量)
第三十八章 linux-并发解决方法二(信号量) 文章目录第三十八章 linux-并发解决方法二(信号量)信号量的定义DOWN操作UP操作相对于自旋锁,信号量的最大特点是允许调用它的线程进入睡眠状态这意味着试图获得某一信号的进程…...

数据结构-考研难点代码突破(C++实现树型查找 - B树插入与遍历,B+树基本概念)
数据结构(C)[B树(B-树)插入与中序遍历,效率分析]、B树、B*树、B树系列应用 文章目录1. B树B树的插入与删除流程2. B树(MySQL)3. B树与B树对比4. C实现B树插入,中序遍历1. B树 B树类…...

Python可视化界面编程入门
Python可视化界面编程入门具体实现代码如所示: (1)普通可视化界面编程代码入门: import sys from PyQt5.QtWidgets import QWidget,QApplication #导入两个类来进行程序界面编程if __name__"__main__":#创建一个Appl…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...