VR头显如何低延迟播放8K的RTSP|RTMP流

news2024/11/13 11:27:58

技术背景

我们在做Unity平台RTSP、RTMP播放器的时候,有公司提出来这样的技术需求,希望在头显播放全景的8K RTSP|RTMP直播流,8K的数据,对头显和播放器,都提出了新的要求,我们从几个方面,探讨下VR头显设备如何播放8K的RTSP|RTMP流数据:

一、播放器支持

  1. 兼容性:首先,RTSP|RTMP播放器需要支持8K分辨率的视频流。这意味着播放器必须能够解码8K视频,并在支持8K分辨率的显示设备上播放,这个不必多说,我们已经支持。
  2. 解码能力:播放器需要具备强大的解码能力,以处理8K视频流中的大量数据。这通常要求播放器使用高效的解码算法,并充分利用硬件加速功能(如GPU加速),这就需要头显支持8K的硬解码。

二、网络要求

  1. 带宽:8K视频流需要极高的网络带宽来支持实时传输。确保网络带宽足够大,以避免播放过程中出现卡顿、延迟或缓冲等问题,如果是内网环境下,基本不要纠结带宽问题。
  2. 稳定性:网络连接的稳定性也非常重要。不稳定的网络连接可能导致视频流中断或质量下降。

三、硬件要求

  1. 处理器与内存:VR头显播放8K的视频流,对VR头显的性能,提了很高的要求,比如说quest3,就是不错的选择。

四、播放步骤

  1. 选择RTSP播放器:我们的做法,是用大牛直播SDK的原生的RTSP|RTMP播放器,硬解码模式,回调解码后的YUV或RGB数据到unity,需要注意的是,由于8K的RTSP|RTMP流,数据量非常大,特别是解码后的数据,条件允许的情况下,需要尽可能少的减少拷贝。

技术实现

本文以大牛直播SDK的Android平台Unity3D RTSP|RTMP播放模块为例:

开始播放:

/*
 * SmartPlayerAndroidMono.cs
 * Author: daniusdk.com
 * QQ:89030985
 */
public void Play()
{
	if (is_running)
	{
		Debug.Log("已经在播放。。");   
		return;
	}

	//获取输入框的url
	string url = input_url_.text.Trim();

	if (!url.StartsWith("rtmp://") && !url.StartsWith("rtsp://"))
	{
		videoUrl = "rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream";
	}
	else
	{
		videoUrl = url;
	}

	OpenPlayer();

	if ( player_handle_ == 0 )
		return;

	NT_U3D_Set_Game_Object(player_handle_, game_object_);

	/* ++ 播放前参数配置可加在此处 ++ */
	int is_using_tcp = 0;        //TCP/UDP模式设置
	NT_U3D_SetRTSPTcpMode(player_handle_, is_using_tcp);

	int is_report = 0;
	int report_interval = 1;
	NT_U3D_SetReportDownloadSpeed(player_handle_, is_report, report_interval);  //下载速度回调

	NT_U3D_SetBuffer(player_handle_, play_buffer_time_);                        //设置buffer time

	NT_U3D_SetPlayerLowLatencyMode(player_handle_, is_low_latency_ ? 1 : 0);    //设置是否启用低延迟模式

	NT_U3D_SetMute(player_handle_, is_mute_ ? 1 : 0);                           //是否启动播放的时候静音

	NT_U3D_SetAudioVolume(player_handle_, cur_audio_volume_);                   //设置播放音量

	NT_U3D_SetVideoDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.264软硬解模式

	NT_U3D_SetVideoHevcDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.265软硬解模式

	int is_output = 1;
	int disable_use_image_planes = 0;
	bool is_supports_texture_format = SystemInfo.SupportsTextureFormat(TextureFormat.RG16);
	Debug.Log("is_supports_texture_format: " + is_supports_texture_format);
	int is_supported_multiple_format = is_supports_texture_format? 1:0;
	int max_images = 3;
	int buffer_pool_max_size = 0;
	NT_U3D_SetImageReaderOutput(player_handle_, is_output, disable_use_image_planes, is_supported_multiple_format, max_images, buffer_pool_max_size);  //硬解码image reader

	int is_fast_startup = 1;
	NT_U3D_SetFastStartup(player_handle_, is_fast_startup);                     //设置快速启动模式

	int rtsp_timeout = 10;
	NT_U3D_SetRTSPTimeout(player_handle_, rtsp_timeout);                        //设置RTSP超时时间

	int is_auto_switch_tcp_udp = 1;
	NT_U3D_SetRTSPAutoSwitchTcpUdp(player_handle_, is_auto_switch_tcp_udp);    //设置TCP/UDP模式自动切换

	int is_audiotrack = 1;
	NT_U3D_SetAudioOutputType(player_handle_, is_audiotrack);                   //设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式

	NT_U3D_SetUrl(player_handle_, videoUrl);
	/* -- 播放前参数配置可加在此处 -- */

	int flag = NT_U3D_StartPlay(player_handle_);

	if (flag  == DANIULIVE_RETURN_OK)
	{
		is_need_get_frame_ = true;
		Debug.Log("播放成功");
	}
	else
	{
		is_need_get_frame_ = false;
		Debug.LogError("播放失败");
	}

	is_running = true;  
}

对应的OpenPlayer()实现如下:

private void OpenPlayer()
{
	if ( java_obj_cur_activity_ == null )
	{
		Debug.LogError("getApplicationContext is null");
		return;
	}

	player_handle_ = NT_U3D_Open();

	if (player_handle_ != 0)
		Debug.Log("open success");
	else
		Debug.LogError("open fail");
}

关闭Player:

private void ClosePlayer()
{
	is_need_get_frame_ = false;
	is_need_init_texture_ = false;

	int flag = NT_U3D_StopPlay(player_handle_);
	if (flag == DANIULIVE_RETURN_OK)
	{
		Debug.Log("停止成功");
	}
	else
	{
		Debug.LogError("停止失败");
	}

	flag = NT_U3D_Close(player_handle_);
	if (flag == DANIULIVE_RETURN_OK)
	{
		Debug.Log("关闭成功");
	}
	else
	{
		Debug.LogError("关闭失败");
	}

	player_handle_ = 0;

	NT_U3D_UnInit();

	is_running = false;
	video_format_ = VideoFrame.FORMAT_UNKNOWN;
	video_width_ = 0;
	video_height_ = 0;
}

Update刷新数据:

private void Update()
{
	if (!is_need_get_frame_)
		return;

	if (player_handle_ == 0)
		return;

	AndroidJavaObject u3d_video_frame_obj = NT_U3D_GetVideoFrame(player_handle_);

	if (u3d_video_frame_obj == null)
	{
		return;
	}

	VideoFrame converted_video_frame = ConvertToVideoFrame(u3d_video_frame_obj);

	if (converted_video_frame == null)
	{
		u3d_video_frame_obj.Call("release");

	   u3d_video_frame_obj = null;
	   return;
	}

	if (!is_need_init_texture_)
	{
		if (converted_video_frame.format_ != video_format_)
		{
			is_need_init_texture_ = true;

		}
		else if (converted_video_frame.width_ != video_width_
			|| converted_video_frame.height_ != video_height_
			|| converted_video_frame.stride0_ != y_row_bytes_
			|| converted_video_frame.stride1_ != u_row_bytes_
			|| converted_video_frame.stride2_ != v_row_bytes_)
		{
			is_need_init_texture_ = true;
		}
	}

	if (is_need_init_texture_)
	{
		if (InitYUVTexture(converted_video_frame))
		{
			is_need_init_texture_ = false;
		}
	}

	UpdateYUVTexture(converted_video_frame);

	converted_video_frame.java_frame_obj_ = null;
	converted_video_frame = null;
	u3d_video_frame_obj.Call("release");
	u3d_video_frame_obj = null;
}

总结

VR头显如果需要播放8K的RTSP或RTSP流,对硬件和网络的要求非常高,因此在实际应用中可能会遇到一些挑战。通过实际测试,在quest3头显,配合我们的RTSP|RTMP播放器,在unity下,可以实现毫秒级延迟的8K视频数据播放,以满足平衡操控等对实时性要求非常高的使用场景,感兴趣的开发者,可以单独跟我探讨。

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

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

相关文章

Lingo学习(一)——基本界面、解方程、变量

一、Lingo基本界面 【步骤】 1.双击打开Lingo 2.弹出一个对话框,点击Cancel左边的Never Register即可,其余内容用不到。 3:界面自动弹出名为“Lingo Model – Lingo 1”的窗口,用于书写代码。 4:以解方程的题目:x12为例,写完代码后,点击“红色的靶心”运行程序。 5:首先Lin…

昇思学习打卡-13-LLM原理与实践/解码原理--以MindNLP为例

文章目录 搜索方法集束搜索(beam search)贪心搜索(greedy search) 采样池处理结果 一个文本序列的概率分布可以分解为每个词基于其上文的条件概率的乘积 搜索方法 集束搜索(beam search) Beam search通过在每个时间步保留最可能的 num_beams 个词,并从中最终选择出…

代码随想录-暑假算法第一天(数组篇)

代码随想录-暑假算法第一天(数组篇) 1. 二分查找 力扣题目链接(opens new window) 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否…

超酷的3D立体文字?分享 1 段优质 CSS 代码片段!

本内容首发于工粽号:程序员大澈,每日分享一段优质代码片段,欢迎关注和投稿! 大家好,我是大澈! 本文约 500 字,整篇阅读约需 1 分钟。 今天分享一段优质 CSS 代码片段,实现了超酷的3…

SPI协议与读写串行 FLASH

SPI 协议简介 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、…

轻松选型,高效开发——业务开发集市助您一臂之力

在当今这个日新月异的时代,高效开发已成为企业保持竞争力的关键。为了满足市场对高效、灵活开发工具的迫切需求,OceanMind海睿思推出业务开发集市。这款开发工具汇集了众多丰富的、高度可定制的控件和组件,以及灵活的模板选型功能&#xff0c…

OpenGL笔记二之glad加载opengl函数以及opengl-API(函数)初体验

OpenGL笔记二之glad加载opengl函数以及opengl-API(函数)初体验 总结自bilibili赵新政老师的教程 code review! 文章目录 OpenGL笔记二之glad加载opengl函数以及opengl-API(函数)初体验1.运行2.重点3.目录结构4.main.cpp5.CMakeList.txt 1.运行 2.重点 3.目录结构 01_GLFW_WI…

户用光伏项目开发流程

1、收集业主信息 管理业主基本信息,包括但不限于联系方式、地址、房屋信息等。 2、业主开卡 每户都需要办理银行卡,用于电费结算和划转。 3、合同签约 业主开卡完成之后,平台方发起签约(支持线上签约)&#xff0c…

运行前端项目提示 run `npm fund` for details,如何解决?

经常出现在前端的一个小坑,分享一下技巧。 运行npm install命令终端提示: 107 packages are looking for funding run npm fund for details 解决方案: npm install --no-fund

实践致知第15享:如何取消文件的格式后缀?

一、背景需求 小姑电话说:我新建了一个excel表格,修改名字的时候总是容易删掉后面的字母(如下图所示),然后就打不开这个文件了! 二、解决方案 文件后面的字母是文件的扩展名,如果删掉之后&…

基础弱口令暴力破解

一、实验介绍 本实验中我们针对网站中的登录页面进行暴力破解,通过使用 Burpsuite 工具对网页进行暴力破解,体会学习暴力破解的基本过程,以及学习如何使用Burpsuite 工具。 二、实验目的 BurpSuite工具抓包。暴力破解基本流程。 三、实验…

白杨SEO:小红书追百度,知乎已落伍,从APP日活看,搞精准流量去哪与怎么做?

前言:这是白杨SEO公众号原创第540篇。为什么分享这个?看到啸哥在分享国内APP日活,恰好白杨SEO主要就是实战分享搞精准流量,所以希望大家也了解目前在哪些渠道去搞流量更好。如果有用,记得收藏或帮忙分享更多需要的人。…

微信小程序毕业设计-学习资料库系统项目开发实战(附源码+论文)

大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:微信小程序毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计…

粉笔推出国内首个职教行业大模型,助力学员高效学习

7月12日,粉笔正式推出其自主研发的首个专注于职教行业的垂域大模型,并将于8月1日上线粉笔AI老师 “粉笔头”,为学员提供个性化、智能化的辅导服务。 在垂域大模型的基础上,粉笔结合10年来的教研积累、独有数据搭建RAG系统&#x…

乘积末尾的0

区间删除 解题思路: 乘积末尾0的个数取决于2和5的因子的数量。因此我们要对所有的数进行统计2和5的因子个数。接下来使用滑动窗口算法: 构建一个滑动窗口,要求保证删除窗口内的元素后,保证剩余元素的乘积末尾0的个数>k。因此只…

从Centos7升级到Rocky linux 9后,网卡连接显示‘Wired connection 1‘问题解决方法

问题描述 从Centos7升级到Rocky9后, 发现网卡eth0的IP不正确。通过nmcli查看网卡连接,找不到name为eth0的连接,只显示’Wired connection 1’ 查看/etc/NetworkManager/system-connections/,发现找不到网卡配置文件。 原因分析 centos7使…

[web]-代码审计-就这么直接

打开页面就是一个输入框&#xff0c;非sql注入 查看页面源代码&#xff0c;发现有一个hint.php,访问后看到源码&#xff0c;但是有随机种子&#xff0c;根据时间不同随机值是变化的。time()输出时间戳&#xff0c;如果在短时间内访问&#xff0c;就会固定这个种子。 <?php…

stm32——外部中断EXTI

上回书说到定时器的级联&#xff0c;今天来谈谈外部中断EXTI。我使用的是STM32F103C8T6的学习板。仅供大家参考。 什么是中断呢&#xff1f;中断是指计算机在执行程序的过程中&#xff0c;当出现某些异常情况或特殊事件&#xff08;例如外部设备请求、定时时间到达、程序错误等…

流程图怎么做?有三种制作方法

流程图怎么做&#xff1f;在日常生活和工作中&#xff0c;流程图作为一种直观展示步骤、流程或决策路径的工具&#xff0c;扮演着不可或缺的角色。它不仅能够帮助我们理清思路、规划任务&#xff0c;还能促进团队协作与沟通。那么&#xff0c;如何高效地绘制流程图呢&#xff1…

关于web、DNS、firewalld、SELinux综合实验

思路&#xff1a;最好先做DNS&#xff0c;然后再做web服务器 主&#xff1a; 1、写配置文件、/etc/hosts文件等&#xff0c;创建目录&#xff0c;写index.html文件等 [rootlocalhost ~]# cd /etc/httpd/conf.d [rootlocalhost conf.d]# cat vhost.conf <directory /dado…