使用MTVerseXR SDK实现VR串流

news2024/10/4 19:46:29

1、概述​

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

2、环境准备​

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

2.1 MTVerseXR 服务器驱动​

运行平台要求

  • 系统:x86 CPU + Windows10

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

  • 软件:SteamVR

  • 基于OpenXR的Windows VR应用

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-2
   MySetupXRSession
 
   MySetupDeviceDesc(&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);
    // 初始化MTXRClient
    MTXRClient.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 RenderDoc
  static 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.coloradmin.cn/o/2188592.html

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

相关文章

芯片公司产品简介

台湾新唐科技:ARM内核-nuvoTon stc&#xff08;宏晶&#xff09;&#xff1a;STC 8051 系列、STC32 8051系列、STC32 ARM系列 兆易&#xff1a;Arm Cortex-M内核和RISC-V内核 意法&#xff1a; 乐鑫科技&#xff1a; MCU厂家 GPU厂家 MCU与MPU 引用链接&#xff1a; http://…

AutoCAD学习

AutoCAD学习 最基本操作 命令用途说明空格键确认键也可以是重复刚才的命令回车键也是确认键鼠标右键也可以选择确认LINE、L直线命令绘制直线DLI线性尺寸标注DIMLINEAR鼠标滚轮滚动放大缩小视图界面鼠标中键按住移动视图DAL对齐线性标注DIMALIGNED F8 正交模式ORTHOMODE Tab 切换…

漆包线称重系统/自动称重/项目合作

万界星空科技漆包线行业称重系统实现自动称重的方式主要依赖于现代数字电子称重技术、计算机网络技术以及相关的软件系统的集成。以下是对该系统如何实现自动称重的详细解释&#xff1a; 一、硬件基础 称重设备&#xff1a; 系统采用高精度的电子秤作为称重设备&#xff0c;这…

TOGAF框架中的最佳实践与实施技巧:推动企业数字化转型的实战指南

企业的数字化转型已经成为全球商业界的一大趋势&#xff0c;而实现这一转型不仅需要技术的创新&#xff0c;还需要清晰的架构规划和实施策略。TOGAF&#xff08;The Open Group Architecture Framework&#xff09;作为全球广泛应用的企业架构标准&#xff0c;为企业提供了行之…

Pikachu-File Inclusion- 本地文件包含

前端每次挑选篮球明星&#xff0c;都会通过get请求&#xff0c;传了文件名&#xff0c;把页面展示出来&#xff0c;由于文件名时前端传给后台;并且查看源码&#xff0c;没有对参数做限制&#xff1b; 尝试直接从前端修改filename 参数&#xff1b; filename../../../../../../…

24-10-3-读书笔记(二十三)-《一个孤独漫步者的遐想》上([法] 让·雅克·卢梭 [译]陈阳)

文章目录 《一个孤独漫步者的遐想》上&#xff08;[法] 让雅克卢梭 [译]陈阳&#xff09;卢梭生平大事年表总结 《一个孤独漫步者的遐想》上&#xff08;[法] 让雅克卢梭 [译]陈阳&#xff09; 十月第三篇&#xff0c;看书看个爽&#xff0c;今天是法国哲学家卢梭晚年的著作《一…

【C++】—— vector模拟实现

vector 接口预览 namespace HL {template<class T>class vector{//迭代器iteratortypedef T* iterator;typedef const T* const_iterator;public://默认成员函数vector();vector(size_t n, const T& val T());vector(int n, const T& val T());vector(const v…

【学习资源】人在环路的机器学习

说明&#xff1a;本文图片和内容来源 Human-in-the-Loop Machine Learning Human-in-the-Loop Machine Learning Active learning and annotation for human-centered AI by Robert (Munro) Monarch, June 2021 介绍Human-in-the-Loop的目标&#xff0c;学习过程&#xff0c…

Redis:通用命令 数据类型

Redis&#xff1a;通用命令 & 数据类型 通用命令SETGETKEYSEXISTSDELEXPIRETTLTYPEFLUSHALL 数据类型 Redis的客户端提供了很多命令用于操控Redis&#xff0c;在Redis中&#xff0c;key的类型都是字符串&#xff0c;而value有多种类型&#xff0c;每种类型都有自己的操作命…

DMA 正点原子版

就是介绍一下dma&#xff0c;只能内存到外设&#xff0c;外设到内存&#xff0c;内存到内存&#xff0c;不能外设到外设这样进行数据传输 这个是 可以看这个表来查&#xff0c;哪个dma的哪个通道用来传输什么数据&#xff0c;这个是芯片固定好的&#xff0c;只能看表查&#xf…

Geogebra基础篇002—关于Geogebra软件的介绍及与MatLab的区别

为什么要学Geogebra&#xff1f; 因为和MatLab的科学计算相比&#xff0c;GeoGebra重点突出教学展示&#xff0c;对于教师、学生人群来讲再合适不过了&#xff0c;尤其是可以融入到PPT里边呈现交互式动画&#xff0c;想想听众的表情&#xff01;这不就弥补了看到PPT播放数学公…

Python+Matplotlib-高等数学上-P7-例如部分可视化

import numpy as np import matplotlib.pyplot as plt# 设置中文字体&#xff0c;确保中文显示正确 plt.rcParams[font.sans-serif] [SimHei] # 用黑体显示中文 plt.rcParams[axes.unicode_minus] False # 正常显示负号# 设置图形和子图 fig, (ax1, ax2) plt.subplots(2, …

Qt 5开发步骤及实例

目录 界面设计编写相应的计算圆面积代码 界面设计 创建桌面应用程序 得到这样一个树形视图 双击界面文件中的dialog.ui 直接双击控件label改名&#xff0c;然后修改最后一个label的属性 修改这个标签的样式&#xff0c;把frameshape改成Panel&#xff0c;frameshadow改…

LC刷题专题:二叉树;迭代;递归(897、1372、208)

文章目录 897.递增顺序搜索树1372. 二叉树中的最长交错路径208. 实现 Trie (前缀树) 897.递增顺序搜索树 https://leetcode.cn/problems/increasing-order-search-tree/description/ 这道题目本身就是一个简单题&#xff1a;非常容易实现&#xff1a;只需要在递归或者迭代中序…

【Java的SPI机制】Java SPI机制:实现灵活的服务扩展

在Java开发中&#xff0c;SPI&#xff08;Service Provider Interface&#xff0c;服务提供者接口&#xff09;机制是一种重要的设计模式&#xff0c;它允许在运行时动态地插入或更换组件实现&#xff0c;从而实现框架或库的扩展点。本文将深入浅出地介绍Java SPI机制&#xff…

【Godot4.3】复合路径类myPath

概述 之前编写过一个基于指令绘图的类交myPoint&#xff0c;但是只涉及折线段生成。这次我基于SVG的<path>标签路径指令的启发&#xff0c;实现了一个能够获得连续绘制的直线段、圆弧和贝塞尔复合路径的类型myPath。 可以使用绘图指令方法或字符串形式的绘图指令解析来…

hbuilderx+uniapp+Android宠物用品商城领养服务系统的设计与实现 微信小程序沙箱支付

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 顾客 领养…

OpenCAEPoro优化(1)

核心目的&#xff1a;减少运行时的 object time 方法一&#xff1a;改变运行的进程数 进入OpenCAEPoro目录下运行下述代码&#xff08;进程数为4&#xff09; mpirun -np 4 ./testOpenCAEPoro ./data/case1/case1.data verbose1结果如下 可以看到&#xff0c;object time是…

日常工作记录:服务器被攻击导致chattr: command not found

在深夜的寂静中&#xff0c;公司的服务器突然遭遇了一场突如其来的攻击。特别是nginx配置文件无法修改&#xff0c;仿佛预示着不祥的预兆&#xff0c;面对这突如其来的灾难&#xff0c;技术人员迅速响应。 这时候需要chattr&#xff0c;但是执行的chattr -i xxx的时候&#xf…

神经网络激活函数之前的加权求和 | 矩阵相乘运算法则(清晰版)

1. 神经网络中进行加权求和为什么要将w矩阵进行转置&#xff1f; 下面以一个简单的神经网络作为举例&#xff1a; 我们要将输入特征与W进行加权求和&#xff0c;想要的是下面这种结果&#xff1a; 但是根据矩阵相乘的运算法则&#xff1a; 矩阵A的列数&#xff08;column&am…