Unity实现camera数据注入RMP推送或轻量级RTSP服务模块

news2024/11/25 7:20:17

技术背景

随着技术的不断进步和应用的不断深化,Unity3D VR应用的前景非常广阔,它广泛应用于教育、医疗、军事、工业设计、虚拟数字人等多个领域。

  1. 教育领域:Unity3D VR技术可以用来创建虚拟现实教室,让学生能够身临其境地体验课程内容,提高学习效果和兴趣;
  2. 医疗领域:Unity3D VR技术可以用来创建虚拟手术室,让医生能够在真实手术之前进行模拟操作,提高手术技能和安全性;
  3. 军事领域:Unity3D VR技术可以用来创建虚拟战场环境,进行军事训练和战术演练,提高士兵的战斗能力和应变能力;
  4. 工业设计领域:Unity3D VR技术可以用来创建虚拟现实工作环境,让设计师能够在真实产品推出之前进行虚拟测试和修改,提高产品设计和制造的效率和质量。
  5. 虚拟数字人是指使用虚拟现实技术创建的数字人物,具有人的外貌、动作、语言和思维等特征。VR虚拟数字人可以用来进行虚拟互动、虚拟演讲、虚拟展览、虚拟客服等多种应用场景。例如,在虚拟展览中,VR虚拟数字人可以作为虚拟讲解员,为参观者介绍展品,提供全方位的互动体验。在虚拟客服中,VR虚拟数字人可以作为企业形象代表,与消费者进行互动交流,提高客户满意度和品牌形象。

技术实现

从技术的角度,分析如何在unity环境下,采集到camera数据,然后编码打包推RTMP或启动轻量级RTSP服务。我们老早实现了Unity环境下的RTMP低延迟推送,原生环境下,比如windows下,可轻松实现50帧+的编码和RTMP推送(需要播放端也有高帧率播放的能力)。

数据源是高帧率的基础,比如,我们在跟外部公司合作的时候,比如无人机在一些工业场景下的智能躲避等,帧率要求非常高,这时候,如果单独还好,多路的话,ReadPixel()读取数据耗时还是非常大的。读取到的数据,特别是高分辨率高帧率的,编码一般建议硬编码,帧率的控制,需要有个好的算法机制,确保比如我可以采集到60帧,但是我实际值需要编码45帧,如何drop数据,达到流畅无卡顿感。

此外,除了视频数据外,音频可以采集麦克风、Unity内部音频、麦克风+unity内部音频混音或Unity下2路内部音频混音。Unity内部audio数据采集,可以使用AudioClip,编码格式建议AAC。

以Windows平台为例,Frame的构建,可以参考一下设计:

/*
* 构建FrameTexture
* Author: daniusdk.com
*/
public class FrameTexture
{
  public FrameTexture(Texture2D texture, IntPtr video_buffer, int video_buffer_size,
                      int video_width, int video_height, int is_vertical_flip, int is_horizontal_flip, int scale_width, int scale_height, bool is_alpha)
  {
    texture_ = texture;
    video_buffer_ = video_buffer;
    video_buffer_size_ = video_buffer_size;
    video_width_ = video_width;
    video_height_ = video_height;
    is_vertical_flip_ = is_vertical_flip;
    is_horizontal_flip_ = is_horizontal_flip;
    scale_width_ = scale_width;
    scale_height_ = scale_height;
    is_alpha_ = is_alpha;
  }

  public Texture2D texture_;
  public IntPtr video_buffer_;
  public int video_buffer_size_;
  public int video_width_;
  public int video_height_;
  public int is_vertical_flip_;
  public int is_horizontal_flip_;
  public int scale_width_;
  public int scale_height_;
  public bool is_alpha_;
}

PostImageWorker类,实现数据投递到原始模块:

private class PostImageWorker
{
  public PostImageWorker(TexturesPool pool, nt_publisher_wrapper handle)
  {
    pool_ = pool;
    handle_ = handle;
  }

  public void run()
  {
    if (null == pool_ || null == handle_)
      return;

    while (!is_exit_)
    {
      event_.WaitOne(100);

      if (is_exit_)
        break;

      while (sendImage()) ;
    }

    Debug.Log("PostImageWorker.run out...");

    FrameTexture frame;
    while (frames_.TryDequeue(out frame))
    {
      if (frame != null && frame.texture_)
      {
        pool_.add(frame.texture_);
        frame.texture_ = null;
      }
    }

    frame = null;
  }

  private bool sendImage()
  {
    FrameTexture frame;
    if (frames_.TryDequeue(out frame))
    {
      if (frame != null && frame.texture_ != null)
      {
        if (frame.video_buffer_ != IntPtr.Zero)
        {
          handle_.OnPostRGBXData(0, frame.video_buffer_, video_buffer_size_, frame.video_width_ * 4, frame.video_width_, -frame.video_height_, frame.is_alpha_);
        }

        pool_.add(frame.texture_);
        frame.texture_ = null;
      }

      frame = null;
      return true;
    }

    frame = null;
    return false;
  }

Windows平台,构建个承载的图层:

NT_PB_ExternalVideoFrameLayerConfig external_layer_c1 = new NT_PB_ExternalVideoFrameLayerConfig();

external_layer_c1.base_.type_ = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
external_layer_c1.base_.index_ = 0;
external_layer_c1.base_.enable_ = 1;
external_layer_c1.base_.region_.x_ = 0;
external_layer_c1.base_.region_.y_ = 0;
external_layer_c1.base_.region_.width_ = video_width_;
external_layer_c1.base_.region_.height_ = video_height_;

external_layer_c1.base_.offset_ = Marshal.OffsetOf(external_layer_c1.GetType(), "base_").ToInt32();
external_layer_c1.base_.cb_size_ = (uint)Marshal.SizeOf(external_layer_c1);

IntPtr external_layer_conf = Marshal.AllocHGlobal(Marshal.SizeOf(external_layer_c1));

Marshal.StructureToPtr(external_layer_c1, external_layer_conf, true);

UInt32 external_r = NTSmartPublisherSDK.NT_PB_AddLayerConfig(publisher_handle_, 0,
                                                             external_layer_conf, (int)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME,
                                                             0, IntPtr.Zero);

Marshal.FreeHGlobal(external_layer_conf);

然后通过NT_PB_PostLayerImage()给图层投递数据即可:

/*
         * 给index层投递Image数据,目前主要是用来把rgb和yuv视频数据传给相关层
         * reserve: 保留字段,请传0
         * index: 层索引
         * image: 图像
         * flag: 请传0
         * pReserve: 保留字段,请传0
         * 
         * 成功返回 NT_ERC_OK
     */
[DllImport("SmartPublisherSDK", EntryPoint = "NT_PB_PostLayerImage", CallingConvention = CallingConvention.StdCall)]
        public static extern UInt32 NT_PB_PostLayerImage(IntPtr handle, Int32 reserve,
                                                         Int32 index, IntPtr image,
                                                         UInt32 flag, IntPtr pReserve);

如果需要预览推送的数据:

//预览数据回调
public void SDKVideoPreviewImageCallBack(IntPtr handle, IntPtr user_data, IntPtr image)
{
  NT_PB_Image pb_image = (NT_PB_Image)Marshal.PtrToStructure(image, typeof(NT_PB_Image));

  NT_VideoFrame pVideoFrame = new NT_VideoFrame();

  pVideoFrame.width_ = pb_image.width_;
  pVideoFrame.height_ = pb_image.height_;

  pVideoFrame.stride_ = pb_image.stride_[0];

  Int32 argb_size = pb_image.stride_[0] * pb_image.height_;

  pVideoFrame.plane_data_ = new byte[argb_size];

  if (argb_size > 0)
  {
    Marshal.Copy(pb_image.plane_[0],pVideoFrame.plane_data_,0, argb_size);
  }

  {
    cur_image_ = pVideoFrame;
  }
}  

总结

Unity下的“多端同屏”云渲染以及相关可视化平台解决方案,成为助力了工业领域数字化转型。除上述场景外,还需要考虑多实例多camera模式,实现高效率低延迟和低资源占有的互动体验。

 

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

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

相关文章

微信小程序 事件和语法

属性列表 target&#xff1a;触发事件的对象currentTarget&#xff1a;绑定事件的对象&#xff0c;可能不是由此对象触发而是触发对象冒泡导致触发此事件 微信小程序 事件绑定 <button type"primary" bindtap"btnTapHandler">按钮</button> …

pytorch学习-线性神经网络——softmax回归+损失函数+图片分类数据集

1.softmax回归 Softmax回归&#xff08;Softmax Regression&#xff09;是一种常见的多分类模型&#xff0c;可以用于将输入变量映射到多个类别的概率分布中。softmax回归是机器学习中非常重要并且经典的模型&#xff0c;虽然叫回归&#xff0c;实际上是一个分类问题 1.1分类与…

ORCA优化器浅析——Exploration and Implementation Apply CXform Phase

GPORCA通过规则对表达式做转换&#xff0c;规则分为两类&#xff1a;Exploration&#xff0c;是对逻辑表达式做等价变换的&#xff1b;Implementation&#xff0c;是把逻辑操作符转换为物理操作符。Except the types of operator generated during Exploration (Logical > L…

手机python怎么用海龟画图,python怎么在手机上编程

大家好&#xff0c;给大家分享一下手机python怎么用海龟画图&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 1、如何python手机版创造Al&#xff1f; 如果您想在手机上使用Python来创建AI&#xff08;人工智能&#xff09;程序&#xff0…

服务器推送、在线游戏和电子邮件背后的网络协议

之前也聊了不少网络协议这块内容&#xff0c;现在我们将深入探讨关键的网络协议及其在不同应用中的作用。重点在于理解这些协议如何塑造我们在互联网上的通信和互动方式。我们将深入研究以下领域&#xff1a; WebSocket 在之前的讨论中&#xff0c;我们研究了HTTP及其在客户端和…

帮助中心客户服务中的作用与应用

随着互联网的快速发展&#xff0c;越来越多的企业开始寻求更加高效、便捷的客户服务方式&#xff0c;帮助中心的自助服务作为一种新型的客户服务模式&#xff0c;受到了广泛关注。本文将针对帮助中心的自助服务在客户服务中的作用与应用进行深入探讨。 帮助中心在客户服务中的…

scrcpy2.0+实时将手机画面显示在屏幕上并用鼠标模拟点击2023.7.26

想要用AI代打手游&#xff0c;除了模拟器登录&#xff0c;也可以直接使用第三方工具Scrcpy&#xff0c;来自github&#xff0c;它是一个开源的屏幕镜像工具&#xff0c;可以在电脑上显示Android设备的画面&#xff0c;并支持使用鼠标进行交互。 目录 1. 下载安装2. scrcpy的高级…

文本预处理——文本数据分析

目录 文本数据分析中文酒店评价语料获得训练集和验证集的标签数量分布获取训练集和验证集的句子长度分布获取训练集和验证集的正负样本长度散点分布获得训练集和验证集不同词汇总数统计获得训练集上正负的样本的高频形容词词云获得验证集上正负的样本的形容词词云 文本数据分析…

Golang指针详解

要搞明白Go语言中的指针需要先知道3个概念&#xff1a;指针地址、指针类型和指针取值。 指针介绍 我们知道变量是用来存储数据的&#xff0c;变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量 a : 10 ,这个时候可以直接通过 a 这个变量来读取内存…

IPv6 over IPv4

IPv6 over IPv4隧道简介 IPv6 over IPv4隧道可实现IPv6网络孤岛之间通过IPv4网络互连。由于IPv4地址的枯竭和IPv6的先进性&#xff0c;IPv4过渡为IPv6势在必行。因为IPv6与IPv4的不兼容性&#xff0c;所以需要对原有的IPv4设备进行替换。但是如果贸然将IPv4设备大量替换所需成…

明晚直播:可重构计算芯片的AI创新应用分享!

大模型技术的不断升级及应用落地&#xff0c;正在推动人工智能技术发展进入新的阶段&#xff0c;而智能化快速增长和发展的市场对芯片提出了更高的要求&#xff1a;高算力、高性能、灵活性、安全性。可重构计算区别于传统CPU、GPU&#xff0c;以指令驱动的串行执行方式&#xf…

SpringBoot 集成 Elasticsearch

一、版本 spring-boot版本&#xff1a;2.3.7.RELEASEElasticsearch7.8.0版本说明详见 二、Elasticsearch 下载和安装 Elasticsearch 下载 kibana下载 ik分词器下载 配置IK分词器 2.1 解压&#xff0c;在elasticsearch-7.8.0\plugins 路径下新建ik目录 2.2 将ik分词器解压放…

【VB6|第21期】检查SqlServer数据库置疑损坏的小工具(含源码)

日期&#xff1a;2023年7月25日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

Java反射类private私有变量Map并赋值

Java反射类private私有变量Map并赋值 import java.util.LinkedHashMap; import java.util.Map;public class MyObj {private String KEY "NAME";//目标是通过反射在外部访问cacheprivate Map<String, String> cache new LinkedHashMap<>();public MyOb…

mac 删除自带的ABC输入法保留一个搜狗输入法,搜狗配置一下可以减少很多的敲击键盘和鼠标点击次数

0. 背景 对于开发者来说&#xff0c;经常被中英文切换输入法所困扰&#xff0c;我这边有一个方法&#xff0c;删除mac默认的ABC输入法 仅仅保留搜狗一个输入法&#xff0c;配置一下搜狗输入&#xff1a;哪些指定为英文输入&#xff0c;哪些指定为中文输入&#xff08;符号也可…

七、Kafka源码分析之网络通信

1、生产者网络设计 架构设计图 2、生产者消息缓存机制 1、RecordAccumulator 将消息缓存到RecordAccumulator收集器中, 最后判断是否要发送。这个加入消息收集器&#xff0c;首先得从 Deque 里找到自己的目标分区&#xff0c;如果没有就新建一个批量消息 Deque 加进入 2、消…

限幅器(信捷PLC C语言FC功能函数)

关于限幅器的算法介绍,请参考下面博客文章,这里不再赘述,受水平和能力所限文中难免出现错误和不足之处,欢迎大家批评指正。 限幅器算法介绍 PLC信号处理系列之限幅器(Saturation)_RXXW_Dor的博客-CSDN博客TITLE=限幅器VAR_INPUTrX:REAL;// 输出值// 上限到达 FALSE: Up…

RT thread 之 Nand flash 读写过程分析

文章目录 前言&#xff1a;什么是Nand Flash&#xff1f;1、Nand Flash 读取步骤2、从主存读到Cache2.1 在标准spi接口下读取过程2.2 测试时序&#xff08;SPI频率30MHz&#xff09; 3.从Cache读取数据3.1在标准spi接口读取过程测试时序 前言&#xff1a;什么是Nand Flash&…

服务器数据恢复-误操作导致存储VDisk丢失的数据恢复案例

服务器数据恢复环境&#xff1a; IBM某型号存储&#xff1b; Solaris操作系统&#xff0c;部署Oracle数据库。 服务器故障&#xff1a; 重建MDisk导致对应的存储池中的VDisk丢失&#xff0c;导致Solaris操作系统中的Oracle数据库无法使用。 服务器数据恢复过程&#xff1a; 1、…

【如何训练一个中英翻译模型】LSTM机器翻译模型部署之ncnn(python)(五)

系列文章 【如何训练一个中英翻译模型】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型训练与保存&#xff08;二&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型部署&#xff08;三&#xff09; 【如何训练…