Unity3D下如何采集camera场景数据并推送RTMP服务?

2023-11-07

Unity3D使用场景

Unity3D是非常流行的游戏开发引擎,可以创建各种类型的3D和2D游戏或其他互动应用程序。常见使用场景如下:

  1. 游戏开发:Unity3D是一个广泛用于游戏开发的环境,适用于创建各种类型的游戏,包括动作游戏、角色扮演游戏、策略游戏、冒险游戏等。
  2. 虚拟现实:Unity3D也常用于虚拟现实(VR)开发,它提供了对VR设备的支持,如Oculus Rift和HTC Vive。
  3. 交互式演示:Unity3D可以用于创建各种类型的交互式演示,如产品原型、建筑和设计模拟器、教育应用程序等。
  4. 实时渲染:Unity3D的实时渲染功能可以用于创建电影级的特效和动画,以及用于视觉预览和产品渲染。
  5. 跨平台开发:Unity3D支持多个平台,包括PC、Mac、Linux、Android、iOS、Windows等,这使得开发者可以更容易地将他们的应用程序和游戏移植到不同的平台。

无论你是在哪个领域使用Unity3D,都需要了解其基本的工具和功能,包括场景编辑器、游戏对象、组件、脚本等。同时,还需要掌握一些基本的编程语言,如C#,以编写游戏逻辑和控制流程。

如何获取Camera场景数据

Unity3D获取摄像机数据通常用RenderTexture和RenderTexture.GetPixel方法来获取数据,把捕获屏幕的图像,存储在一个Texture2D实例中,用这个实例获取RGB数据。需要注意的是,需要为输出纹理创建一个新的纹理对象,否则可能会在屏幕上看到一片空白。示例代码如下:

using UnityEngine;  
  
public class GetCameraData : MonoBehaviour  
{  
    public Texture2D outputTexture; // 输出纹理,用于存储RGB数据  
    public RenderTexture renderTexture; // RenderTexture实例,用于捕获屏幕图像  
  
    void Start()  
    {  
        // 创建一个RenderTexture实例  
        renderTexture = new RenderTexture(Screen.width, Screen.height, 24);  
        // 获取当前摄像机  
        Camera camera = GetComponent<Camera>();  
        // 将当前摄像机的屏幕输出设置为刚刚创建的RenderTexture实例  
        camera.targetTexture = renderTexture;  
        // 创建一个空的Texture2D实例,用于存储从RenderTexture读取的RGB数据  
        outputTexture = new Texture2D(Screen.width, Screen.height);  
    }  
  
    void Update()  
    {  
        // 从RenderTexture中读取RGB数据,并存储到outputTexture中  
        RenderTexture.active = renderTexture;  
        outputTexture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);  
        outputTexture.Apply();  
    }  
}

如何实现RTMP推送服务

本文以大牛直播SDK开发的Unity3D下Android平台的RTMP推送camera场景的demo为例,结合Unity和原生模块交互,大概介绍下核心实现逻辑。

开始推送RTMP服务:

    public bool StartRtmpPusher()
    {
        if (is_pushing_rtmp_)
        {
            Debug.Log("已推送..");   
            return false;
        }


        if(!is_rtsp_publisher_running_)
        {
            InitAndSetConfig();
        }

        if (pusher_handle_ == 0) {
             Debug.LogError("StartRtmpPusher, publisherHandle is null..");
            return false;
        }

        NT_PB_U3D_SetPushUrl(pusher_handle_, rtmp_push_url_);

        int is_suc = NT_PB_U3D_StartPublisher(pusher_handle_);

        if (is_suc  == DANIULIVE_RETURN_OK)
        {
            Debug.Log("StartPublisher success..");          
            is_pushing_rtmp_ = true;
        }
        else
        {
            Debug.LogError("StartPublisher failed..");
            return false;
        }

        return true;
    }

InitAndSetConfig()完成常规参数设置,比如软硬编码、帧率、码率等参数设置,如果需要采集audio,还可以把麦克风采集到的audio和audioclip获取到的audio数据mix后输出:

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

        int audio_opt = 1;
        int video_opt = 3;

        video_width_ = camera_.pixelWidth;
        video_height_ = camera_.pixelHeight;

        pusher_handle_ = NT_PB_U3D_Open(audio_opt, video_opt, video_width_, video_height_);

        if (pusher_handle_ != 0){
            Debug.Log("NT_PB_U3D_Open success");
            NT_PB_U3D_Set_Game_Object(pusher_handle_, game_object_);
        }
        else
        {
            Debug.LogError("NT_PB_U3D_Open failed!");
            return;
        }

        int fps = 30;
        int gop = fps * 2;

        if(video_encoder_type_ == (int)PB_VIDEO_ENCODER_TYPE.VIDEO_ENCODER_HARDWARE_AVC)
        {
            int h264HWKbps = setHardwareEncoderKbps(true, video_width_, video_height_);
            h264HWKbps = h264HWKbps * fps / 25;

            Debug.Log("h264HWKbps: " + h264HWKbps);

            int isSupportH264HWEncoder = NT_PB_U3D_SetVideoHWEncoder(pusher_handle_, h264HWKbps);

            if (isSupportH264HWEncoder == 0) {
                NT_PB_U3D_SetNativeMediaNDK(pusher_handle_, 0);
                NT_PB_U3D_SetVideoHWEncoderBitrateMode(pusher_handle_, 1); // 0:CQ, 1:VBR, 2:CBR
                NT_PB_U3D_SetVideoHWEncoderQuality(pusher_handle_, 39);
                NT_PB_U3D_SetAVCHWEncoderProfile(pusher_handle_, 0x08); // 0x01: Baseline, 0x02: Main, 0x08: High

                // NT_PB_U3D_SetAVCHWEncoderLevel(pusher_handle_, 0x200); // Level 3.1
                // NT_PB_U3D_SetAVCHWEncoderLevel(pusher_handle_, 0x400); // Level 3.2
                // NT_PB_U3D_SetAVCHWEncoderLevel(pusher_handle_, 0x800); // Level 4
                NT_PB_U3D_SetAVCHWEncoderLevel(pusher_handle_, 0x1000); // Level 4.1 多数情况下,这个够用了
                //NT_PB_U3D_SetAVCHWEncoderLevel(pusher_handle_, 0x2000); // Level 4.2

                // NT_PB_U3D_SetVideoHWEncoderMaxBitrate(pusher_handle_, ((long)h264HWKbps)*1300);

                Debug.Log("Great, it supports h.264 hardware encoder!");
            }
        }
        else if(video_encoder_type_ == (int)PB_VIDEO_ENCODER_TYPE.VIDEO_ENCODER_HARDWARE_HEVC)
        {
            int hevcHWKbps = setHardwareEncoderKbps(false, video_width_, video_height_);
            hevcHWKbps = hevcHWKbps*fps/25;

            Debug.Log("hevcHWKbps: " + hevcHWKbps);

            int isSupportHevcHWEncoder = NT_PB_U3D_SetVideoHevcHWEncoder(pusher_handle_, hevcHWKbps);

            if (isSupportHevcHWEncoder == 0) {
                NT_PB_U3D_SetNativeMediaNDK(pusher_handle_, 0);
                NT_PB_U3D_SetVideoHWEncoderBitrateMode(pusher_handle_, 0); // 0:CQ, 1:VBR, 2:CBR
                NT_PB_U3D_SetVideoHWEncoderQuality(pusher_handle_, 39);

                // NT_PB_U3D_SetVideoHWEncoderMaxBitrate(pusher_handle_, ((long)hevcHWKbps)*1200);

                Debug.Log("Great, it supports hevc hardware encoder!");
            }
        }
        else 
        {
            if (is_sw_vbr_mode_) //H.264 software encoder
            {
                int is_enable_vbr = 1;
                int video_quality = CalVideoQuality(video_width_, video_height_, true);
                int vbr_max_bitrate = CalVbrMaxKBitRate(video_width_, video_height_);
                vbr_max_bitrate = vbr_max_bitrate * fps / 25;

                NT_PB_U3D_SetSwVBRMode(pusher_handle_, is_enable_vbr, video_quality, vbr_max_bitrate);
                //NT_PB_U3D_SetSWVideoEncoderSpeed(pusher_handle_, 2);
            }
        }

        NT_PB_U3D_SetAudioCodecType(pusher_handle_, 1);

        NT_PB_U3D_SetFPS(pusher_handle_, fps);

        NT_PB_U3D_SetGopInterval(pusher_handle_, gop);

        if (audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER
            || audio_push_type_ == (int)PB_AUDIO_OPTION.AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER)
        {
            NT_PB_U3D_SetAudioMix(pusher_handle_, 1);
        }
        else
        {
            NT_PB_U3D_SetAudioMix(pusher_handle_, 0);
        }
    }

投递video数据的逻辑实现如下:

    void PostVideoData() {
        if(pusher_handle_ == 0)
            return;

        if(!is_pushing_rtmp_ && !is_rtsp_publisher_running_)
            return;

        if (textures_poll_ == null)
           return;

        int w = camera_.pixelWidth;
        int h = camera_.pixelHeight;

       if (w != video_width_ || h != video_height_) {

           Debug.Log("PostVideoData resolution changed++ width: " + w + " height: " + h);
           if(render_texture_ != null) {
               render_texture_.Release();
               render_texture_ = null;
           }

           video_width_  = w;
           video_height_ = h;
       }

       if (null == render_texture_ ) {
           render_texture_ = new RenderTexture(video_width_, video_height_, 16);
           render_texture_.Create();
       }

       Texture2D image_texture = textures_poll_.get(video_width_, video_height_);
        if (null == image_texture)
           return;

         ...

        image_texture.ReadPixels(new Rect(0, 0, video_width_, video_height_), 0, 0, false);

         ...

        post_image_worker_.post(image_texture, is_vertical_flip_, is_horizontal_flip_, scale_width_, scale_height_);
    }

如果需要停止RTMP推送:

    private void StopRtmpPusher()
    {
        if(!is_pushing_rtmp_)
            return;

        NT_PB_U3D_StopPublisher(pusher_handle_);

        if(!is_rtsp_publisher_running_)
        {
            NT_PB_U3D_Close(pusher_handle_);
            pusher_handle_ = 0;

            NT_PB_U3D_UnInit();
        }

        is_pushing_rtmp_ = false;
    }

技术总结

Unity3D下采集camera场景并推送RTMP具有重要的意义,可以为实时监控、在线直播、视频教程制作、增强现实和虚拟现实应用以及数据记录和分析等领域提供有力的支持。比如,采集camera场景可以用于增强现实和虚拟现实应用。在AR中,可以通过采集实际场景的画面,将虚拟元素与现实场景进行融合,增强沉浸感和互动性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity3D下如何采集camera场景数据并推送RTMP服务? 的相关文章

  • Epoll图解

    图解 Epoll怎么实现的 51CTO COM epoll详解 Ineffable 的博客 CSDN博客 epoll详解
  • Nginx健康检查

    0 背景 服务治理的一个重要任务是感知服务节点变更 完成服务自动注册及异常节点的自动摘除 这就需要服务治理平台能够 及时 准确的感知service节点的健康状况 1 方案概述 Nginx 提供了三种HTTP服务健康检查方案供用户选择 1 T
  • 三面阿里被挂,竟获内推名额,历经 5 面拿下口碑 offer...

    每一个互联网人心中都有一个大厂梦 百度 阿里巴巴 腾讯是很多互联网人梦寐以求的地方 而我也不例外 但是 BAT 等一线互联网大厂并不是想进就能够进的 它对人才的技术能力和学历都是有一定要求的 所以除了学历以外 我们的技术和能力都要过硬才行
  • win10微信打电话对方听不到你的声音,你能听到对方声音

    1 隐私权限 开始 设置 隐私 麦克风 更改 开启权限 2 禁用占用问题 右键电脑右下角小喇叭 声音 声音控制面板 录制 分别右键下面的几个选项 启用麦克风 立体声混音等 然后分别双击下面的麦克风 立体声等选项 高级 独占前面的v取消 确定
  • 拯救小tim【最短路】

    题目链接 这里有一个坑点 譬如说 我们从S出发的时间 不是刚好卡着第一个的 起始点 没准出发的第一步 没有卡起始点 而是在后面的到达其他点的时候卡了起始点这样的情况 所以我们应该从0 max BegTim的来枚举起点时间 然后跑Dijkst
  • javamail发送接收的简单demo

    目录结构 首先引入文件
  • 23考研重大软院数一英一391分经验帖

    今年这情况之后 所有前人的经验帖作废 前言 本校本专业生一战上岸 属于考研界难度最低的一档 今年有个初试439的怪物 属于是蚌了 第二名也有419 第三名就断档了 我初试第五 政治78 英一75 数一115 专业课123 总分391分 可以
  • [个人笔记] vCenter设置时区和NTP同步

    VMware虚拟化 运维篇 第三章 vCenter设置时区和NTP同步 VMware虚拟化 运维篇 系列文章回顾 vCenter设置时区和NTP同步 附加 ESXi设置alias 参考链接 系列文章回顾 第一章 vCenter给虚机添加RD
  • 鼓励参与计算机考试宣传标语,考试宣传标语

    考试标语 1 遵守考场纪律 维护知识尊严 2 提倡诚信做人 纯洁校园风气 3 考前不慌不乱 考时沉着应对 考后杜绝议论 4 用心看卷 专心答题 细心复查 5 我自信 我成功 6 与诚信携手同行与舞弊挥手作别 7 怀轻松心情进考场 带胜利喜悦
  • 安全https,dns笔记整理

    一 https HTTP 是用于从万维网 WWW World Wide Web 服务器传输超文本到本地浏览器的传送协议 他是一种应用层协议 是基于 TCP IP 通信协议来传递数据的 但他不安全 以明文传递的方式 容易被他人盗取篡改信息 H
  • java 提取存在逗号和小数点的字符串中的数字

    可以使用正则表达式来处理 以下是一个示例代码 可以提取字符串中可能包含逗号和小数点的数字 import java util regex Matcher import java util regex Pattern public class
  • Arduino和Python实时监督控制和数据采集系统(SCADA)

    本文 将向您展示如何设置环境温度信号 该信号将通过计算机上的实时仪表板记录和可视化数据 硬件设计 首先 我们将使用Arduino Uno开发板从红外温度计读取温度值 如上所示连接红外测温仪后 继续将以下程序上传到Arduino 要验证Ard
  • 如何在windows下切换node版本

    解决办法 1 用到某个版本对node重新卸载 安装对应的版本 2 使用nvm 很明显 第一种方法虽然也能解决node版本问题 但是太多麻烦 接下来介绍下nvm的安装使用 第一步 下载nvm并安装 推荐使用nvm setup zip nvm
  • ROS配置LTE第二链路实现故障自动切换

    本文几乎没有操作部分 主要是讲原理 设备是RB962 通过PPPoE方式上网 华为5577移动路由器配合电信日租卡作为第二链路备用 计划实现宽带正常时通过PPPoE线路上网 异常时通过LTE线路上网 PPPoE线路恢复正常时 流量回到PPP
  • 第37.2节 框选-框选场景中的物体

    目录 本节内容 实现要点 点选 性能 绘制球 本节内容 结合上一节 我们把框选这个功能给完善了 如下 白色的是我点击左CTRL 用鼠标左键在场景中拉的框 拉框的教程在第37 1节 框选 绘制框选框 拉完框后能够将场景中选择的物体置红 本节代

随机推荐

  • 基于Qt的OpenGL编程(3.x以上GLSL可编程管线版)---(十二)光照贴图

    Vries的原教程地址如下 https learnopengl cn github io 02 20Lighting 04 20Lighting 20maps 关于OpenGL函数的详细解析及OpenGL关于满反射贴图与镜面反射贴图的知识点
  • 反射的使用

    反射 一 反射基本概念 1 反射是什么 2 Class类实例对象是什么 Field类实例对象和Method类实例对象是什么 3 jVM类加载机制 很难又很重要 待补充 二 反射的作用 三 Class详解 Class的使用场景 1 通过对象获
  • C语言之函数必备练习题

    笔者来自于一个普通二本 非科班出身 对于未来的打算博多 无法完事皆满意 所以 尽自己可能去实现 所以 打算转码中 C语言XX必备练习题 凝聚了笔者精华所有 请看到的读者认真思考 在进行 话不多说 直接上题 笔者的一贯要求 速度 1 作业标题
  • Python爬虫从入门到精通:(9)数据解析_xpath解析2_爬取4K高清动漫图片_Python涛哥

    使用xpath爬取4K高清动漫图片名称和图片数据 爬取当前页 创建文件夹 存储图片 dirName GirlsLib if not os path exists dirName os mkdir dirName headers User A
  • 购物商城---freemarker在项目中的应用

    一 1 通过模板 数据 生成静态化页面 2 缺点 数据不实时 3 适用于于数据长时间不更新的情况 二 在项目中搭建freemarker freemarker xml
  • pandas对dataframe内部数据的增删改查操作整理汇总

    文章目录 一 增加数据 1 增加行数据 2 增加列数据 二 删除数据 删除前 后 N 行 三 修改数据 四 查询 索引数据 五 合并Dataframe 1 按行合并 根据行index合并 自定义合并规则 2 按列合并 根据列名称合并 六 拆
  • 【福利】光荣之路公开课视频下载大全

    最新视频 JAVA编程系列 六哥 第七讲 日志 异常和反射 6月6日 http yun baidu com s 1mhA7Sbm Python编程系列 第十五讲 网络编程和pyh应用举例 3月14日 http yun baidu com s
  • Linux解压命令

    Linux解压命令 解压到当前文件下 unzip test zip 3D打印梦想库了解一下
  • 系统稳定性方法论 - 提感知、快响应、做复盘

    之前在 lt 系统稳定性方法论 gt 中提到了稳定性建设的四大抓手 在 lt 降发生 gt 之后 今天来说一说其余的三点 提感知 快响应 做复盘 提感知 何为 提感知 提感知指的是 对于已经出现问题 能够及时且精准的进行告警 提升对异常的感
  • 关于scrapy爬虫的注意事项

    1 图片下载的设置 class ClawernameSpider scrapy Spider 定制化设置 custom settings LOG LEVEL DEBUG Log等级 默认是最低级别debug ROBOTSTXT OBEY F
  • XMPP客户端库Smack 4.1.4版官方开发文档之六

    一 处理出入的字节 smacke 类库 提供了处理传入的字节 主要依靠两个类 org jivesoftware smack PacketCollector 和 org jivesoftware smack PacketListener or
  • lpad用法 oracle,oracle中lpad函数的用法详解

    oracle中lpad函数的用法详解 oracle中lpad的用法 pad翻译 填充 lpad函数 在字符串的左侧添加指定字符串 用法 www jb51 net lpad String 截取长度 添加的字符串 说是添加字符串也不准确 比较准
  • DCMTK读取dcm图片+opencv显示图片

    读取DCM格式图片中的一些基本信息 DcmFileFormat是最基本的文件对象 OFCondition是每一次操作的返回值 用来判断操作是否成功 所有的数据都存在DcmDataSet对象中 用getDataSet方法得到 void Loa
  • webpack(4版本)使用

    webpack简介 webpack 是一种前端资源构建工具 一个静态模块打包器 module bundler 在 webpack 看来 前 端的所有资源文件 js json css img less 都会作为模块处理 它将根据模块的依赖关系
  • 利用神经网络实现股票预测

    神经网络 NeuralNetworks 是一种用训练数据拟合目标函数的黑箱模型 只要数据量足够大 它可以拟合出输入到输出之间的任意函数关系 本篇教程我们将使用神经网络进行股市的预测 利用数据样本学习 得到相关因素预测股票走势 01 问题描述
  • 区块链+跨境支付有哪些优势?

    全球互联网的高速发展也带动了跨境电商的快速发展 跨境支付也瞬间成为第三方支付领域的一个风口 而将区块链技术应用在跨境支付领域也逐渐成为市场的热点需求 传统的跨境支付方式中间环节繁杂 费时又费力 而且跨境电商卖家在跨境支付环节 存在境外银行账
  • ModuleNotFoundError: No module named ‘fused_layer_norm_cuda‘

    No module named fused layer norm cuda Issue 161 NVIDIA apex GitHub 按照以下方式安装 可解决问题 apex 安装步骤 1 git clone https github com
  • android和iOS平台的崩溃捕获和收集

    通过崩溃捕获和收集 可以收集到已发布应用 游戏 的异常 以便开发人员发现和修改bug 对于提高软件质量有着极大的帮助 本文介绍了iOS和android平台下崩溃捕获和收集的原理及步骤 不过如果是个人开发应用或者没有特殊限制的话 就不用往下看
  • pycharm preparing workspace 项目打不开怎么办?

    屏幕一直显示 preparing workspace 无法打开项目时 找出保存最近项目信息的xml文件 Library Preferences Pycharm2019 2 options recentProjectsDirectories
  • Unity3D下如何采集camera场景数据并推送RTMP服务?

    Unity3D使用场景 Unity3D是非常流行的游戏开发引擎 可以创建各种类型的3D和2D游戏或其他互动应用程序 常见使用场景如下 游戏开发 Unity3D是一个广泛用于游戏开发的环境 适用于创建各种类型的游戏 包括动作游戏 角色扮演游戏