使用MTVerseXR SDK实现VR串流

server/2024/10/9 1:03:31/

1、概述​

MTVerseXR SDK 是摩尔线程GPU加速的虚拟现实(VR)流媒体平台,专门用于从远程服务器流式传输基于标准OpenXR的应用程序。MTVerseXR可以通过Wi-Fi和USB流式将VR内容从Windows服务器流式传输到XR客户端设备, 使相对性能低的VR客户端可以使用高性能图形服务器的渲染能力。

2、环境准备​

MTVerseXR SDK包括服务器驱动程序、客户端SDK和示例客户端应用程序。Windows上运行的服务端和VR头盔内的应用程序共同实现了Windows系统(边缘云)的SteamVR与VR头盔客户端的连接。

xr-服务器驱动">2.1 MTVerseXR 服务器驱动​

运行平台要求

  • 系统:x86 CPU + Windows10

  • 显卡:摩尔线程MTT S80, MTT S70, 驱动程序 v240.50或更新版本

  • 软件:SteamVR

  • 基于OpenXR的Windows VR应用

xr-客户端sdk">2.2 MTVerseXR 客户端SDK​

客户端SDK目前只支持Android系统。包含头文件、Android so库文件和资源文件,通过AAR的方式提供。

3、服务器驱动安装和启动​

  1. 安装SteamVR:
    • 下载并安装Steam
    • 在Steam中安装SteamVR
  2. 安装运行时库
    • 下载并安装vc_redist.x64.exe
  3. 安装Vulkan驱动:
    • 依次运行 VulkanRT-1.3.261.1-Installer.exe 和 VulkanSDK-1.3.261.1-Installer.exe 安装Vulkan
    • 链接如下:
      https://sdk.lunarg.com/sdk/download/1.3.261.1/windows/VulkanRT-1.3.261.1-Installer.exe
      https://sdk.lunarg.com/sdk/download/1.3.261.1/windows/VulkanSDK-1.3.261.1-Installer.exe
  4. 安装 MTVerseXR驱动:
    • 以管理员权限运行 MTVerseXRSetup-v1.0.0.exe 安装程序;
    • 按照安装向导的步骤进行安装,在最后一页,记得勾选“注册驱动”。如果未勾选注册驱动,可以通过以管理员权限运行安装目录下的 driver_install.bat 文件来完成注册;
    • 重启计算机
  5. 启动SteamVR
    • 确认MTVerseXR驱动启用, 查看SteamVR 【设置】-【启动/关闭】-【管理加载项】-【MTXR_VHMD】选项是开启状态(如果【设置】页面没有【启动/关闭】,点击【设置】页面的左下角【高级设置】下方的【显示】)

4、客户端开发指南​

基于Client SDK 开发App的C++部分大致框架如下:

int main() {MySetupGLES3-2MySetupXRSessionMySetupDeviceDesc(&ddesc);MySetupCallbacks(&callbacks);MySetupReceiverDesc(&rdesc, ddesc, callbacks)MTXRCreateReceiver();MTXRConnect();while (!*exiting*) {MyPlatformEventHandling();if (client_state < connected) {MyRenderConnectionProgress();}else if (client_state == connected) {UpdateTrackingState();MTXRFetchFrame()MTXRRenderFrame(framesFetched);MTXRReleaseFrame()}else {MyHandleDisconnect();exiting = true;}}MTXRDestroyReceiver()
}

请注意,此处的UpdateTrackingState是伪代码中更新位姿回调的占位符。在UpdateTrackingState中,可以处理头盔、手柄的位姿和手柄事件,保存最新的位姿信息,在回调中更新。

Java部分需要在Activity的onCreate中调用MTXRClient.init 初始化客户端SDK组件。
SDK同时提供了工具类MTXRServerDiscover和MTXRUSB。其中MTXRServerDiscover用于探测局域网中的MTVerseXR Server,MTXRUSB用于打开USB配件模式的文件描述句柄,具体参考示例代码。

4.1 引用SDK包​

在app的gradle中添加aar依赖。

手动或者使用gradle脚本解压aar文件后,在CMakeLists或者Android.mk中引用SDK的头文件和so文件。解压后头文件的路径为assets\include, so的路径为jni\arm64-v8a。

4.2 初始化​

Java部分的初始化:

  • 在App MainActivity的onCreate中调用 MTXRClient.init 完成。
protected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.xr);// 初始化MTXRClientMTXRClient.init(getBaseContext());// do super first, as that sets up some native things.super.onCreate(savedInstanceState);boolean usMic = false;enableMicrophone(usMic);……
}

C++部分初始化:

  1. OpenGLES 3.2上下文环境初始化(SDK目前仅支持OpenGLES环境下的显示,版本要求>=3.2);
  2. OpenXR Sessioin环境初始化(非必须通过OpenXR);
  3. 获取设备相关参数填充MTXRReceiverDesc的成员变量,包括:显示分辨率、刷新率、音频接收状态等;
  4. 设置MTXRClientCallbacks中需要的回调函数,包括GetTrackingState(必须实现以同步最新的位姿和手柄输入)和GetHapticCallback(手柄震动反馈);
  5. 调用MTXRCreateReceiver 创建Receiver对象,用于和服务器端通信的重要对象;
  6. 调用 MTXRConnect(Wifi连接)或者MTXRConnectUSB(USB连接) 连接服务器。
void XRScene::Start(const std::string& serverIp, int fd, bool enableMic) {m_desc.deviceDesc.posePollFreq = 0;m_desc.clientCallbacks.GetTrackingState = [](void *context, MTXRVRTrackingState *trackingState) {reinterpret_cast<XRScene*>(context)->GetTrackingState(trackingState);};m_desc.clientCallbacks.ReceiveMicrophoneData = NULL;m_desc.clientCallbacks.GetHapticCallback = [](void* context, MTXRHapticFeedback *haptic) {XRScene* that = reinterpret_cast<XRScene*>(context);if (NULL != that->m_haptic_callback && NULL != that->m_context) {that->m_haptic_callback(that->m_context, haptic);}};……m_desc.deviceDesc.receiveAudio = true;m_desc.clientContext = this;m_desc.requestedVersion = MTXR_VERSION_DWORD;m_desc.deviceDesc.sendAudio = enableMic;if (enableMic) {m_desc.clientCallbacks.ReceiveMicrophoneData = [](void* context, unsigned char* data, int size)->bool {return reinterpret_cast<XRScene*>(context)->ReceiveMicrophoneData(data, size);};}MTXRError ret = MTXRCreateReceiver(&m_desc, &m_receiver);if (MTXRError_Success == ret) {if (fd > 0) {MTXRConnectUSB(m_receiver, fd);} else {MTXRConnect(m_receiver, serverIp.c_str());}}
}

4.3 主循环​

MTVerseXR客户端App的主循环中需要根据当前的状态做出不同的行为。

  • Receiver未创建:显示一些交互UI,比如服务器的IP地址,让用户可以选择不同的连接方式。

  • 未连接到服务器:显示加载提示,告知用户正在连接服务器。

  • 成功连接到服务器:开始接收服务器传输的音视频数据。

  • 有可用的视频帧:判断当前是否有可用的视频帧,根据结果显示最新的视频或者缓存的视频帧。

  • 连接断开:用户主动断开连接后,应用需要清理相关资源。

渲染服务器视频流步骤:

  1. 获取可用视频帧:调用MTXRFetchFrame获取当前可用视频数据。
void XRScene::BeginFrame(int64_t predictedDisplayTime, const MTXRVRTrackingState& pose, const std::vector<XrView>& xrViews, bool showPerfUI) {{std::lock_guard<std::mutex> guard(m_poseLock);m_xrState = pose;}……m_xrFrame = std::make_shared<MTXRFramesFetched>();MTXRFetchFrame(m_receiver, m_xrFrame.get(), predictedDisplayTime, 0);
}

  1. 渲染视频帧:MTXRFetchFrame成功后,调用MTXRRenderFrame显示当前最新视频帧,否则跳过绘制或者显示之前的缓存。注意需要在激活GLES 3.2上下文环境的渲染线程中调用。
void XRScene::RenderFrame(const XrCompositionLayerProjectionView &layerView, int viewId) {MTXRPosef pose;pose.position = {layerView.pose.position.x, layerView.pose.position.y, layerView.pose.position.z};pose.orientation = {layerView.pose.orientation.x, layerView.pose.orientation.y, layerView.pose.orientation.z, layerView.pose.orientation.w};MTXRFovf fov = {layerView.fov.angleLeft, layerView.fov.angleRight, layerView.fov.angleUp, layerView.fov.angleDown};MTXRRenderFrame(m_receiver, m_xrFrame.get(), viewId == 0 ? MTXRFrameMask_Left : MTXRFrameMask_Right, &pose, &fov);
}void RenderView(int viewID,XrTime predictedDisplayTime,std::shared_ptr<XRScene> xrScene,const XrCompositionLayerProjectionView &layerView,const XrSwapchainImageBaseHeader *swapchainImage,int64_t swapchainFormat) override {glBindFramebuffer(GL_FRAMEBUFFER, m_swapchainFramebuffer);const XrSwapchainImageOpenGLESKHR* swapchainImageGLES = reinterpret_cast<const XrSwapchainImageOpenGLESKHR *>(swapchainImage);const uint32_t colorTexture = swapchainImageGLES->image;glViewport(static_cast<GLint>(layerView.subImage.imageRect.offset.x),static_cast<GLint>(layerView.subImage.imageRect.offset.y),static_cast<GLsizei>(layerView.subImage.imageRect.extent.width),static_cast<GLsizei>(layerView.subImage.imageRect.extent.height));glFrontFace(GL_CW);glCullFace(GL_BACK);glDisable(GL_CULL_FACE);glEnable(GL_DEPTH_TEST);const uint32_t depthTexture = GetDepthTexture(colorTexture);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);// Clear swapchain and depth buffer.glClearColor(BackgroundColor[0], BackgroundColor[1], BackgroundColor[2], BackgroundColor[3]);glClearDepthf(1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);if (xrScene) {xrScene->RenderFrame(layerView, viewID);}glBindFramebuffer(GL_FRAMEBUFFER, 0);// Swap our window every other eye for RenderDocstatic int everyOther = 0;if ((everyOther++ & 1) != 0) {ksGpuWindow_SwapBuffers(&window);}
}

  1. 释放视频帧:绘制完成后,需要调用MTXRReleaseFrame通知MTVerseXR SDK内部释放和回收视频资源。
void XRScene::EndFrame() {MTXRReleaseFrame(m_receiver, m_xrFrame.get());m_xrFrame = nullptr;
}

4.4 资源清理​

退出客户端应用前,需要调用MTXRDestroyReceiver释放MTVerseXR的相关资源。

note

相关安装包、源码可以通过开发者社区下载:MTVerseXR SDK | 摩尔线程开发者)


http://www.ppmy.cn/server/129013.html

相关文章

SQL第12课——联结表

三点&#xff1a;什么是联结&#xff1f;为什么使用联结&#xff1f;如何编写使用联结的select语句 12.1 联结 SQL最强大的功能之一就是能在数据查询的执行中联结&#xff08;join)表。联结是利用SQL的select能执行的最重要的操作。 在使用联结前&#xff0c;需要了解关系表…

银河麒麟,apt 安装软件报错640Unknown Status

今天把银行麒麟的机器恢复出厂了&#xff0c;然后apt install 安装极其不稳定&#xff0c;故障现象如下图所示&#xff1a; 错误提示里面有&#xff1a; 640 Unknown Status [IP: 106.116.184.122 80] E: 无法下载 http://archive.kylinos.cn/kylin/KYLIN-ALL/pool/universe/f…

序列化与反序列化基础及反序列化漏洞(附案例)

参考文章&#xff1a; [web安全原理]PHP反序列化漏洞 - 笑花大王 - 博客园 (cnblogs.com) 一、概念 为了能有效的存储数据而不丢失数据的类型和内容&#xff0c;经常需要通过序列化对数据进行处理&#xff0c;将数据进行序列化后&#xff0c;会生成一个字符串&#xff0c;字符…

Go语言实现长连接并发框架 - 消息

文章目录 前言接口结构体接口实现项目地址最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;上篇博客实现了对连接的封装&#xff0c;那我们对连接的读写操作涉及数据格式的定义&#xff0c;我们采用统一的数据格式。使用我们这种数据格式的数据&#xff0c;我们将其称之为…

17 链表——21. 合并两个有序链表 ★

17 链表 21. 合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 算法设计: 合并两个有序链表,并保持有序性,可以采用迭代法和递归法两种…

学生家长必备,中小学课本教材电子书批量下载工具

今天再整理更新几个最新的电子书教材网站和下载工具 苏生不惑备用号&#xff0c;分享各种黑科技软件资源和技巧&#xff0c;带你玩转互联网。 首先还是国家中小学智慧教育平台这个网站 https://basic.smartedu.cn/elecEdu 还有在线电子教材网站 http://www.haoduoyun.cc/ …

专栏十九:单细胞大数据时代使用scvi和scanpy整合数据

慢更ing,主要是记录自己在分析中的一些困惑 一、基础知识和解惑 放在最前面,是因为scvi整合不像harmony,傻瓜式操作,很多地方还是要注意一下的。 1.如何正确的寻找HVGs 一般我们使用的函数就是scanpy.pp.highly_variable_genes,里面的参数较为复杂。 Expects logarit…

Windows系统编程(三)线程并发

进程与线程 进程&#xff1a;直观的说就是任务管理器中各种正在运行的程序。对于操作系统来说&#xff0c;进程仅仅是一个数据结构&#xff0c;并不会真实的执行代码 线程&#xff1a;通常被称作但并不真的是轻量级进程或实际工作中的进程&#xff0c;它会真实的执行代码。每…