Unity下如何实现低延迟的全景RTMP|RTSP流渲染
技术背景
Unity3D可以用于创建各种类型的的应用程序,包括虚拟现实、培训模拟器等。以下是一些可以使用Unity3D全景播放的场景:
- 虚拟现实体验:全景视频可以用来创建逼真的虚拟环境,使用户能够感受到身临其境的感觉;
- 培训模拟器:全景视频可以用来创建真实的训练环境,例如飞行模拟器、驾驶模拟器等,以提供更加真实的训练体验;
- 建筑设计:全景视频可以用来展示建筑设计的或室内装潢,使客户能够感受到真实的的效果;
- 文旅导览:全景视频可以用来展示旅游景点或城市,使游客能够感受到身临其境的感觉。
在Unity3D平台上实现全景实时RTMP或RTSP流渲染,可以通过以下方式:
- 获取全景视频数据源:首先,需要拉取RTMP或RTSP流数据,解码后,把RGB或YUV数据,回调到unity,从而获取到全景视频流数据;
- Unity创建个Sphere,创建个材质球(Material),并把材质球挂在到Sphere;
- 实现实时渲染:使用Unity3D的渲染管道,您可以将纹理映射到球体或立方体的表面上,并使用着色器来处理纹理的坐标,以实现全景视频的实时渲染。
技术实现

本文以大牛直播SDK的RTMP推送端作为数据采集,获取全景窗体数据后,编码打包推送到RTMP服务,或启动个轻量级RTSP服务,对外提供个RTSP的拉流URL。
然后,播放端,拉取RTSP或RTMP的URL,把YUV或RGB数据回调上来,然后,再在Unity窗体绘制出来。
获取数据源:
public void Play(int sel){if (videoctrl[sel].is_running){Debug.Log("已经在播放..");return;}lock (videoctrl[sel].frame_lock_){videoctrl[sel].cur_video_frame_ = null;}OpenPlayer(sel);if (videoctrl[sel].player_handle_ == IntPtr.Zero)return;//设置播放URLNTSmartPlayerSDK.NT_SP_SetURL(videoctrl[sel].player_handle_, videoctrl[sel].videoUrl);/* ++ 播放前参数配置可加在此处 ++ */int play_buffer_time_ = 0;NTSmartPlayerSDK.NT_SP_SetBuffer(videoctrl[sel].player_handle_, play_buffer_time_); //设置buffer timeint is_using_tcp = 0; //TCP模式NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(videoctrl[sel].player_handle_, is_using_tcp);int timeout = 10;NTSmartPlayerSDK.NT_SP_SetRtspTimeout(videoctrl[sel].player_handle_, timeout);int is_auto_switch_tcp_udp = 1;NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(videoctrl[sel].player_handle_, is_auto_switch_tcp_udp);Boolean is_mute_ = false;NTSmartPlayerSDK.NT_SP_SetMute(videoctrl[sel].player_handle_, is_mute_ ? 1 : 0); //是否启动播放的时候静音int is_fast_startup = 1;NTSmartPlayerSDK.NT_SP_SetFastStartup(videoctrl[sel].player_handle_, is_fast_startup); //设置快速启动模式Boolean is_low_latency_ = false;NTSmartPlayerSDK.NT_SP_SetLowLatencyMode(videoctrl[sel].player_handle_, is_low_latency_ ? 1 : 0); //设置是否启用低延迟模式//设置旋转角度(设置0, 90, 180, 270度有效,其他值无效)int rotate_degrees = 0;NTSmartPlayerSDK.NT_SP_SetRotation(videoctrl[sel].player_handle_, rotate_degrees);int volume = 100;NTSmartPlayerSDK.NT_SP_SetAudioVolume(videoctrl[sel].player_handle_, volume); //设置播放音量, 范围是[0, 100], 0是静音,100是最大音量, 默认是100// 设置上传下载报速度int is_report = 0;int report_interval = 2;NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(videoctrl[sel].player_handle_, is_report, report_interval);/* -- 播放前参数配置可加在此处 -- *///video frame callback (YUV/RGB)videoctrl[sel].video_frame_call_back_ = new SP_SDKVideoFrameCallBack(NT_SP_SetVideoFrameCallBack);NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(videoctrl[sel].player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FROMAT_I420, window_handle_, videoctrl[sel].video_frame_call_back_);UInt32 flag = NTSmartPlayerSDK.NT_SP_StartPlay(videoctrl[sel].player_handle_);if (flag == DANIULIVE_RETURN_OK){videoctrl[sel].is_need_get_frame_ = true;Debug.Log("播放成功");}else{videoctrl[sel].is_need_get_frame_ = false;Debug.LogError("播放失败");}videoctrl[sel].is_running = true;}
针对数据处理:
private void SDKVideoFrameCallBack(UInt32 status, IntPtr frame, int sel){//这里拿到回调frame,进行相关操作NT_SP_VideoFrame video_frame = (NT_SP_VideoFrame)Marshal.PtrToStructure(frame, typeof(NT_SP_VideoFrame));VideoFrame u3d_frame = new VideoFrame();u3d_frame.width_ = video_frame.width_;u3d_frame.height_ = video_frame.height_;u3d_frame.timestamp_ = (UInt64)video_frame.timestamp_;int d_y_stride = video_frame.width_;int d_u_stride = (video_frame.width_ + 1) / 2;int d_v_stride = d_u_stride;int d_y_size = d_y_stride * video_frame.height_;int d_u_size = d_u_stride * ((video_frame.height_ + 1) / 2);int d_v_size = d_u_size;int u_v_height = ((u3d_frame.height_ + 1) / 2);u3d_frame.y_stride_ = d_y_stride;u3d_frame.u_stride_ = d_u_stride;u3d_frame.v_stride_ = d_v_stride;u3d_frame.y_data_ = new byte[d_y_size];u3d_frame.u_data_ = new byte[d_u_size];u3d_frame.v_data_ = new byte[d_v_size];CopyFramePlane(u3d_frame.y_data_, d_y_stride,video_frame.plane0_, video_frame.stride0_, u3d_frame.height_);CopyFramePlane(u3d_frame.u_data_, d_u_stride,video_frame.plane1_, video_frame.stride1_, u_v_height);CopyFramePlane(u3d_frame.v_data_, d_v_stride,video_frame.plane2_, video_frame.stride2_, u_v_height);lock (videoctrl[sel].frame_lock_ ){videoctrl[sel].cur_video_frame_ = u3d_frame;}}
刷新Texture:
private void UpdateYUVTexture(VideoFrame video_frame, int sel)
{if (video_frame.y_data_ == null || video_frame.u_data_ == null || video_frame.v_data_ == null){Debug.Log("video frame with null..");return;}if (videoctrl[sel].yTexture_ != null){videoctrl[sel].yTexture_.LoadRawTextureData(video_frame.y_data_);videoctrl[sel].yTexture_.Apply();}if (videoctrl[sel].uTexture_ != null){videoctrl[sel].uTexture_.LoadRawTextureData(video_frame.u_data_);videoctrl[sel].uTexture_.Apply();}if (videoctrl[sel].vTexture_ != null){videoctrl[sel].vTexture_.LoadRawTextureData(video_frame.v_data_);videoctrl[sel].vTexture_.Apply();}
}
全景播放的时候,如果需要移动显示区域,可以用以下代码:
public class MouseMove : MonoBehaviour {Vector2 p1, p2;private Vector3 PreMouseMPos;private Vector3 PreMouseLPos;public float minimumY = -60F;public float maximumY = 60F;float rotationY = 0F;private float wheelSpeed = 5.0f;// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {if (Input.GetMouseButton(0)){if (PreMouseLPos.x <= 0){PreMouseLPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0.0f);}else{Vector3 CurMouseLPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0.0f);Vector3 offset = CurMouseLPos - PreMouseLPos;Quaternion tt = Quaternion.Euler(offset);float rotationX = transform.localEulerAngles.y - tt.x * 20;rotationY += tt.y * 20;rotationY = Mathf.Clamp(rotationY, minimumY, maximumY);transform.localEulerAngles = new Vector3(rotationY, rotationX, 0);PreMouseLPos = CurMouseLPos;}}else{PreMouseLPos = new Vector3(0.0f, 0.0f, 0.0f);}}
}
总结
Unity全景播放RTMP或RTSP实时流,可以广泛用于各种需要提供真实场景或沉浸式体验的场景,为用户带来更加逼真的体验。与此同时,Unity全景实时播放,需要有非常高的延迟要求和性能要求,特别是全景数据源,分辨率和码率都非常高,对解码效率和解码后的数据拷贝投递,提了更高的要求。
相关文章:
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
技术背景 Unity3D可以用于创建各种类型的的应用程序,包括虚拟现实、培训模拟器等。以下是一些可以使用Unity3D全景播放的场景: 虚拟现实体验:全景视频可以用来创建逼真的虚拟环境,使用户能够感受到身临其境的感觉;培…...
STM32 USB使用记录:HID类设备(后篇)
文章目录 目的基础说明项目构建与代码调整接收发送代码与测试示例链接报告描述符总结 目的 接上篇: 《STM32 USB使用记录:HID类设备(前篇)》 USB HID 类的设备有个比较大的好处是大部分时候接入主机中都是可以免驱使用的。这篇文…...
C# 快速写入日志 不卡线程 生产者 消费者模式
有这样一种场景需求,就是某个方法,对耗时要求很高,但是又要记录日志到数据库便于分析,由于访问数据库基本都要几十毫秒,可在方法里写入BlockingCollection,由另外的线程写入数据库。 可以看到,在…...
Pandas将对角线元素设为1
Pandas将对角线元素设为1 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 入门之pandas的使用 提示:np.fill_diagonal(df.values, 1)的用法 Pandas将对角线元素设为1 Pandas将对角线元素设为…...
WPF实战学习笔记28-登录界面
添加登录界面UI 添加文件loginview.xaml。注意本界面使用的是md内的图标。没有登录界面的图片 <UserControlx:Class"Mytodo.Views.LoginView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsof…...
01背包
动态规划解题步骤: 动态规划问题,一般从三个步骤进行考虑。 步骤一:集合及集合的状态。 所谓的集合,就是一些方案的集合。 用 g[i][j] 表示从前 i 种物品中进行选择,且总体积不大于 j 的各个选法获得的价值的集合。注意&#…...
064、故障处理之OMM_TiDB
oom 内存溢出,内存泄漏,相当于TiDB不能用了 TiDB Server OOM对业务的影响 TiDB Server上的业务SQL会失败业务响应时间升高前端体验变差 诊断方法 客户端应用 ERROR 2013(HY000): Lost connection to MySQL Server during query日志 dmesg -T | gr…...
网络设备中的配置文件管理
建立强大网络的第一步是为灾难和网络中断做好准备,许多企业在中断期间遭受损失,因为他们缺乏备份计划并且配置管理不达标,使用配置文件管理工具进行适当的配置文件管理不仅有助于处理网络中断,还有助于优化网络性能。 使用配置文…...
HCIP BGP综合实验
题目 1、AS1存在两个环回,一个地址为192.168.1.0/24该地址不能在任何协议中宣告; 2、AS3中存在两个环回,一个地址为192.168.2.0/24该地址不能在任何协议中宣告,最终要求这两个环回可以互相通讯; 3、AS间的骨干链路I…...
【mysql学习篇】Order by与Group by优化以及排序算法详解
一、Order by与Group by优化 Case1: 分析: 利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len74也能看出,age索引列用在排序过程中,因为Extra字段里没有using filesort 注意…...
【业务功能篇60】Springboot + Spring Security 权限管理 【终篇】
4.4.7 权限校验扩展 4.4.7.1 PreAuthorize注解中的其他方法 hasAuthority:检查调用者是否具有指定的权限; RequestMapping("/hello")PreAuthorize("hasAuthority(system:user:list)")public String hello(){return "hello Sp…...
文章详情页 - 评论功能的实现
目录 1. 准备工作 1.1 创建评论表 1.2 创建评论实体类 1.3 创建 mapper 层评论接口和对应的 xml 实现 1.4 准备评论的 service 层 1.5 准备评论的 controller 层 2. 总的初始化详情页 2.1 加载评论列表 2.1.1 实现前端代码 2.1.2 实现后端代码 2.2 查询当前登录用户的…...
使用贝叶斯滤波器通过运动模型和嘈杂的墙壁传感器定位机器人研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Day 69-70:矩阵分解
代码: package dl;import java.io.*; import java.util.Random;/** Matrix factorization for recommender systems.*/public class MatrixFactorization {/*** Used to generate random numbers.*/Random rand new Random();/*** Number of users.*/int numUsers…...
数据结构:树的存储结构
学习树之前,我们已经了解了二叉树的顺序存储和链式存储,哪么我们如何来存储普通型的树结构的数据?如下图1: 如图1所示,这是一颗普通的树,我们要如何来存储呢?通常,存储这种树结构的数…...
Vue前端渲染blob二进制对象图片的方法
近期做开发,联调接口。接口返回的是一张图片,是对二进制图片处理并渲染,特此记录一下。 本文章是转载文章,原文章:Vue前端处理blob二进制对象图片的方法 接口response是下图 显然,获取到的是一堆乱码&…...
Java的标记接口(Marker Interface)
Java中的标记接口(Marker Interface)是一个空接口,接口内什么也没有定义。它标识了一种能力,标识继承自该接口的接口、实现了此接口的类具有某种能力。 例如,jdk的com.sun.org.apache.xalan.internal.xsltc.trax.Temp…...
Kafka基础架构与核心概念
Kafka简介 Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。架构特点是分区、多副本、多生产者、多订阅者,性能特点主要是…...
观察者模式与观察者模式实例EventBus
什么是观察者模式 顾名思义,观察者模式就是在多个对象之间,定义一个一对多的依赖,当一个对象状态改变时,所有依赖这个对象的对象都会自动收到通知。 观察者模式也称为发布订阅模式(Publish-Subscribe Design Pattern)࿰…...
科普 | OSI模型
本文简要地介绍 OSI 模型 1’ 2’ 3。 更新:2023 / 7 / 23 科普 | OSI模型 术语节点链路协议网络拓扑 概念作用结构应用层表示层会话层传输层网络层数据链路层物理层 数据如何流动OSI 和TCP/IP 的对应关系和协议参考链接 术语 节点 节点( Node &#…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
