unity 接收拼接数据进行纹理替换且保存相机纹理到rtsp server(一)

1 rtsp 协议后编码解码

rtsp协议的问题就是,拼接完成后,还需要编码,而unity里面再需要解码,需要的过程多了一步编码再解码,大大加重了

2 rtsp 协议后轻量编码

rtsp协议使用mjpeg进行图片传输。why?这样做的好处是解码端进行像素处理以后不用再进行h264和h265编码,而unity端也不用再解码一次,这样增加了程序运行效率

3 server

1 rtsp server
2 websocket server

c# c++ 都可以做websocket server,
使用c++ 做一个server
server 既能接收推流,又能接收流

4 unity client

在这里插入图片描述
unity 上建立一个两个plane,调整位置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
//using NetM
public class test : MonoBehaviour
{// Start is called before the first frame updateprivate string msg = null;string[] file1Lines;void Start(){string path = Application.dataPath + "/rtsp.txt";path = path.Replace("/", "\\");//File静态类方式if (File.Exists(path)){Debug.Log("FileExists");file1Lines = File.ReadAllLines(path);foreach (string line in file1Lines){Debug.Log(line);}}else{Debug.Log("FileNotExists");File.CreateText(path);}//Texture2D m_resultTure = new Texture2D((int)widthSize, (int)heighSize, TextureFormat.RGB24, false);//Texture2D m_resultTure = GetComponent(texture1);//texture rigidbody2D = GetComponent(texture1);//rawImage = GetComponent<RawImage>();//rawImage.texture = texture;//NetManager.M_Instance.Connect("ws://127.0.0.1:8888");   //本机地址}private void OnGUI(){//绘制输入框,以及获取输入框中的内容//PS:第二参数必须是msg,否则在我们输入后,虽然msg可以获得到输入内容,但马上就被第二参数在下一帧重新覆盖。msg = GUI.TextField(new Rect(10, 10, 100, 20), msg);//绘制按钮,以及按下发送信息按钮,发送信息if (GUI.Button(new Rect(120, 10, 80, 20), "发送信息") && msg != null){NetManager.M_Instance.Send(msg);}//绘制按钮,以及按下断开连接按钮,发送断开连接请求if (GUI.Button(new Rect(210, 10, 80, 20), "断开连接")){Debug.Log("向服务器请求断开连接......");NetManager.M_Instance.CloseClientWebSocket();}}void Update(){}
}

4.1 websocket client

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.WebSockets;
using System.Threading;
using System.Text;public class NetManager
{#region 实现单例的代码//变量private volatile static NetManager m_instance;          private static object m_locker = new object();          //属性public static NetManager M_Instance{get{//线程锁。防止同时判断为null时同时创建对象lock (m_locker){//如果不存在对象则创建对象if (m_instance == null){m_instance = new NetManager();}}return m_instance;}}#endregion//私有化构造private NetManager() { }//客户端webSocketprivate ClientWebSocket m_clientWebSocket;//处理接收数据的线程private Thread m_dataReceiveThread;//线程持续执行的标识符private bool m_isDoThread;private bool m_isConnected = false;/// <summary>/// ClientWebSocket,与服务器建立连接。/// </summary>/// <param name="uriStr"></param>public void Connect(string uriStr){try{//创建ClientWebSocketm_clientWebSocket = new ClientWebSocket();//初始化标识符m_isDoThread = true;//创建线程m_dataReceiveThread = new Thread(ReceiveData);  //创建数据接收线程  m_dataReceiveThread.IsBackground = true;        //设置为后台可以运行,主线程关闭时,此线程也会关闭(实际在Unity中并没什么用,还是要手动关闭)//设置请求头部//m_clientWebSocket.Options.SetRequestHeader("headerName", "hearValue");//开始连接var task = m_clientWebSocket.ConnectAsync(new Uri(uriStr), CancellationToken.None);task.Wait();    //等待//启动数据接收线程m_dataReceiveThread.Start(m_clientWebSocket);//输出提示if (m_clientWebSocket.State == WebSocketState.Open){Debug.Log("连接服务器完毕。");m_isConnected = true;}}catch (WebSocketException ex){Debug.LogError("连接出错:" + ex.Message);Debug.LogError("WebSokcet状态:" + m_clientWebSocket.State);CloseClientWebSocket();}}/// <summary>/// 持续接收服务器的信息。/// </summary>/// <param name="socket"></param>private void ReceiveData(object socket){//类型转换ClientWebSocket socketClient = (ClientWebSocket)socket;//持续接收信息while (m_isDoThread){//接收数据string data = Receive(socketClient);//数据处理(可以和服务器一样使用事件(委托)来处理)if (data != null){Debug.Log("接收的服务器消息:" + data);}//组装纹理//Texture2D tex2D = new Texture2D(100, 100);//tex2D.LoadImage(bytes);}Debug.Log("接收信息线程结束。");}/// <summary>/// 接收服务器信息。/// </summary>/// <param name="socket"></param>/// <returns></returns>private byte[] Receive(ClientWebSocket socket){try{//接收消息时,对WebSocketState是有要求的,所以加上if判断(如果不是这两种状态,会报出异常)if (socket != null && (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent)){byte[] arrry = new byte[1024];  //注意长度,如果服务器发送消息过长,这也需要跟着调整ArraySegment<byte> buffer = new ArraySegment<byte>(arrry);  //实例化一个ArraySegment结构体//接收数据var task = socket.ReceiveAsync(buffer, CancellationToken.None);task.Wait();//等待//仅作状态展示。在客户端发送关闭消息后,服务器会回复确认信息,在收到确认信息后状态便是CloseReceived,这里打印输出。Debug.Log("socekt当前状态:" + socket.State);//如果是结束消息确认,则返回null,不再解析信息if (socket.State == WebSocketState.CloseReceived || task.Result.MessageType == WebSocketMessageType.Close){return null;}//如果是字符串//return Encoding.UTF8.GetString(buffer.Array, 0, task.Result.Count);return buffer.Array;}else{return null;}}catch (WebSocketException ex){Debug.LogError("接收服务器信息错误:" + ex.Message);CloseClientWebSocket();return null;}}/// <summary>/// 发送消息/// </summary>/// <param name="content"></param>public void Send(string content){try{//发送消息时,对WebSocketState是有要求的,加上if判断(如果不是这两种状态,会报出异常)if (m_clientWebSocket != null && (m_clientWebSocket.State == WebSocketState.Open || m_clientWebSocket.State == WebSocketState.CloseReceived)){ArraySegment<byte> array = new ArraySegment<byte>(Encoding.UTF8.GetBytes(content)); //创建内容的字节编码数组并实例化一个ArraySegment结构体var task = m_clientWebSocket.SendAsync(array, WebSocketMessageType.Binary, true, CancellationToken.None);  //发送task.Wait();  //等待Debug.Log("发送了一个消息到服务器。");}}catch (WebSocketException ex){Debug.LogError("向服务器发送信息错误:" + ex.Message);CloseClientWebSocket();}}/// <summary>/// 关闭ClientWebSocket。/// </summary>public void CloseClientWebSocket(){//关闭Socketif (m_clientWebSocket != null && m_clientWebSocket.State == WebSocketState.Open){var task = m_clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);Debug.Log("socekt状态:" + m_clientWebSocket.State);Debug.Log("close");}//关闭线程if (m_dataReceiveThread != null && m_dataReceiveThread.IsAlive){m_isDoThread = false;   m_dataReceiveThread = null;}}
}

以上为websocket client, 接收到数据以后就可以替换纹理,接下去我们把里面的纹理图像保存出来,这样可以使用多个不同的camera 来保存不同角度和三维场景中不同的图像流。然后使用websocket 传输出去,形成一个rtsp server。
在这里插入图片描述

4.2 脚本start时候读取本地纹理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
public class canvasrtsp : MonoBehaviour
{// Start is called before the first frame update//public RawImage rawImage;//相机渲染的UI//public GameObject plane1;//相机渲染的GameObject//StartCoroutine(LoadTextureFromInternet("http://avatar.csdnimg.cn/1/E/6/2_u013012420.jpg"));//IEnumerator LoadTextureFromInternet(string path)//{//    UnityWebRequest request = new UnityWebRequest(path);//    //给request的downloadhandle赋值,new出来的UnityWebRequest不附加downloadhandle//    //数据,不赋值的话访问不到下载出来的数据//    DownloadHandlerTexture texture = new DownloadHandlerTexture(true);//    request.downloadHandler = texture;//    yield return request.Send();//    if (string.IsNullOrEmpty(request.error))//    {//        pic = texture.texture;//    }//    Image tempImage = GameObject.Find("Image").GetComponent<Image>();//    Sprite sp = Sprite.Create((Texture2D)pic, new Rect(0, 0, pic.width, pic.height), Vector2.zero);//    tempImage.sprite = sp;//    GameObject go = GameObject.Find("plane1");//    go.GetComponent<MeshRenderer>().material.mainTexture = pic;//}Texture2D Base64ToRGBA32(string imageData, int offset = 0){Texture2D tex2D = new Texture2D(2, 2, TextureFormat.RGB24, false);imageData = imageData.Substring(offset);byte[] data = Convert.FromBase64String(imageData);tex2D.LoadImage(data);return tex2D;}void Start(){string path1 = @"F:\pic\b.png";FileStream fileStream = new FileStream(path1, FileMode.Open, FileAccess.Read);fileStream.Seek(0, SeekOrigin.Begin);byte[] bye = new byte[fileStream.Length];fileStream.Read(bye, 0, bye.Length);fileStream.Close();//创建textureTexture2D texture2D = new Texture2D(240, 144);texture2D.LoadImage(bye);//plane1.gameObject.SetActive(true);GameObject go = GameObject.Find("Plane");//GameObject go1 = GameObject.FindWithTag("ppp");GameObject go1 = GameObject.Find("Cube");//Debug.Log(texture2D.width, texture2D.height);go.GetComponent<MeshRenderer>().material.mainTexture = texture2D;go1.GetComponent<MeshRenderer>().material.mainTexture = texture2D;//T image = GetComponent("image1");}// Update is called once per framevoid Update(){}
}

5 保存相机纹理

增加一个camera capture函数,这个函数将当前相机所见保存成图像

 Texture2D CaptureCamera(Camera camera, Rect rect){RenderTexture rt = new RenderTexture((int)rect.width, (int)rect.height, 0);//创建一个RenderTexture对象camera.targetTexture = rt;//临时设置相关相机的targetTexture为rt, 并手动渲染相关相机camera.Render();//ps: --- 如果这样加上第二个相机,可以实现只截图某几个指定的相机一起看到的图像。//ps: camera2.targetTexture = rt;//ps: camera2.Render();//ps: -------------------------------------------------------------------RenderTexture.active = rt;//激活这个rt, 并从中中读取像素。Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false);screenShot.ReadPixels(rect, 0, 0);//注:这个时候,它是从RenderTexture.active中读取像素screenShot.Apply();//重置相关参数,以使用camera继续在屏幕上显示camera.targetTexture = null;//ps: camera2.targetTexture = null;RenderTexture.active = null; //JC: added to avoid errorsGameObject.Destroy(rt);byte[] bytes = screenShot.EncodeToPNG();//最后将这些纹理数据,成一个png图片文件//string filename = Application.dataPath + "/Screenshot.png";string filename = "e:/qianbo2.png";System.IO.File.WriteAllBytes(filename, bytes);Debug.Log(string.Format("截屏了一张照片: {0}", filename));return screenShot;}

readpixel 方式

readpixel方式一定要注意等到 frameEnd 结束才能读取

WaitForEndOfFrame frameEnd = new WaitForEndOfFrame();IEnumerator CaptureScreenshot2(Rect rect){yield return frameEnd;Texture2D screenShot = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGB24, false);//先创建一个的空纹理,大小可根据实现需要来设置
#pragma warning disable UNT0017 // SetPixels invocation is slowscreenShot.ReadPixels(rect, 0, 0);//读取屏幕像素信息并存储为纹理数据,
#pragma warning restore UNT0017 // SetPixels invocation is slowscreenShot.Apply();byte[] bytes = screenShot.EncodeToJPG();//然后将这些纹理数据,成一个png图片文件string filename = "e:/qianbo1.JPG";System.IO.File.WriteAllBytes(filename, bytes);Debug.Log(string.Format("截屏了一张图片: {0}", filename));//最后,我返回这个Texture2d对象,这样我们直接,所这个截图图示在游戏中,当然这个根据自己的需求的。//yield return screenShot;}

update

我们使用update 来测试我们的函数,按下空格,按下S,按下F,分别调用函数

 void Update(){if (Input.GetKeyDown(KeyCode.Space)){int screenWidth = Screen.width;int screenHeight = Screen.height;RenderTexture rt = new RenderTexture(screenWidth, screenHeight, 24);Camera.main.targetTexture = rt;Camera.main.Render();RenderTexture.active = rt;//激活这个rt, 并从中中读取像素。Texture2D screenShot = new Texture2D(screenWidth, screenHeight, TextureFormat.RGB24, false);screenShot.ReadPixels(new Rect(0, 0, screenWidth, screenHeight), 0, 0);screenShot.Apply();byte[] bytes = screenShot.EncodeToJPG();//string path = Application.dataPath + "/../" + fileName + ".jpg";//string path = Application.dataPath + "/../" + fileName + ".jpg";string path = "e:/qianbo3.jpg" ;File.WriteAllBytes(path, bytes);Debug.Log(string.Format("space: {0}", path));}else if(Input.GetKeyDown(KeyCode.F)){Rect rect = new Rect(10, 10, 300, 200);CaptureScreenshot2(rect);//Debug.Log(string.Format("F: {0}", path));}else if (Input.GetKeyDown(KeyCode.S)){mainCamera = GameObject.Find("Main Camera");Rect rect = new Rect(0, 0, 300, 200);//获取主摄像机的Camera组件//mainCamera= GameObject.Find("MainCamera").GetComponent<Camera>();//Camera ca = Camera.main;CaptureCamera(Camera.main, rect);}}

以下为存储到磁盘的图像
在这里插入图片描述

rtsp 方式传输

我们将使用rtsp方式传送抓取到的纹理图像,使用mjpeg over rtsp, 将jpeg 传送到server,由server提供rtsp 服务,或者http服务,使用c++来写这个server,这个由下一次再讲了。

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

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

相关文章

vite + react + typescript + uni-app + node 开发一个生态系统

简介 使用 vite react typescript uni-app node 来开发一个简易的生态系统案例&#xff0c;包含 APP&#xff0c;H5&#xff0c;微信小程序&#xff0c;控制台&#xff0c;服务端 开发 admin 技术栈&#xff1a;vite react typescript初始化控制台项目选择自定义预设…

【C语言】指针的“最后一站”【进阶版】

欢迎各位看官^_^ 目录 1、字符指针 2、指针数组 3、数组指针 3.1数组指针的定义 3.2数组指针的使用 4、数组指针和指针数组的区别 5、数组参数&#xff0c;指针参数 5.1数组参数定义 5.2指针参数定义 5.3一维数组传参 5.4二维数组传参 5.5一级指针传参 5.6二级指…

《Docker 容器化的艺术:深入理解容器技术》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

javaweb04-vue基础

话不多说&#xff0c;参考官网地址Vue官网集成Vue应用。 一、Vue快速入门 &#xff08;1&#xff09;新建HTML页面&#xff0c;引入Vue.js 我这里用的是CDN方式 <script src"https://unpkg.com/vue3/dist/vue.global.js"></script> &#xff08;2&am…

windows10系统下Python3.11中安装Numpy库教程

Python3.11中安装Numpy库目录 项目场景&#xff1a;问题描述解决方案&#xff1a;①下载Numpy文件②把NumPy文件放到Python安装的Scripts文件夹里。③安装numpy④安装验证 项目场景&#xff1a; numpy是开源的数值计算扩展&#xff0c;用于数据分析、机器学习、科学计算的重要…

【Java 基础篇】Java TreeSet 详解:红黑树实现的有序集合

Java 集合框架提供了多种数据结构&#xff0c;用于存储和操作数据。其中&#xff0c;TreeSet 是一种特殊类型的集合&#xff0c;它通过红黑树&#xff08;Red-Black Tree&#xff09;数据结构实现了有序的、唯一元素存储。本篇博客将深入探讨 TreeSet&#xff0c;包括其概念、特…

小程序中使用分包

前言 小程序在未使用的分包的情况下仅支持大小为2M,如果图片等资源过多的情况下可以使用分包功能&#xff0c;使用分包的情况下单个分包大小不能超过2M,总大小不能超过20M&#xff0c;分包有两种情况&#xff1a;普通分包和独立分包&#xff0c;下面介绍的是普通分包。官方文档…

《向量数据库指南》——哪些需求推动了如Milvus Cloud等的向量数据库的更新和迭代?

这个问题需要深入讨论大模型与向量数据库之间的关系。从去年 ChatGPT 推出时这个问题就开始引发我们的思考。在当时,我们敏锐地意识到这将是一个机遇。然而,在国内,这个概念的认知需要更长的时间。我个人在去年四五月份的美国之行中注意到,数据库在美国已经是一个非常热门的…

算法通关村第十九关:青铜-动态规划是怎么回事

青铜挑战-动态规划是怎么回事 动态规划&#xff08;简称DP&#xff0c;Dynamic Programming&#xff09;&#xff1a;最热门、最重要的算法之一。面试中大量出现&#xff0c;整体偏难。 1. 热身&#xff1a;重复计算和记忆化搜索&#xff08;如何说一万次"我爱你"&…

【LeetCode-中等题】59. 螺旋矩阵 II

文章目录 题目方法一&#xff1a;二维数组缩圈填数字方法二&#xff1a; 题目 方法一&#xff1a;二维数组缩圈填数字 定义四个边界条件&#xff0c;每转一圈&#xff0c;把数值填进去&#xff0c;然后缩小一圈&#xff0c;直到不满足条件位置 结束循环条件可以是&#xff1a; …

QML android 采集手机传感器数据 并通过udp 发送

利用 qt 开发 安卓 app &#xff0c;采集手机传感器数据 并通过udp 发送 #ifndef UDPLINK_H #define UDPLINK_H#include <QObject> #include <QUdpSocket> #include <QHostAddress>class UdpLink : public QObject {Q_OBJECT public:explicit UdpLink(QObjec…

使用ExcelJS快速处理Node.js爬虫数据

什么是ExcelJS ExcelJS是一个用于处理Excel文件的JavaScript库。它可以让你使用JavaScript创建、读取和修改Excel文件。 以下是ExcelJS的一些主要特点&#xff1a; 支持xlsx、xlsm、xlsb、xls格式的Excel文件。可以创建和修改工作表、单元格、行和列。可以设置单元格样式、字…

leetcode:67. 二进制求和

题目&#xff1a; 函数原型&#xff1a; char * addBinary(char * a, char * b) 思路&#xff1a; 二进制相加&#xff0c;首先我们考虑先将字符串逆序。由此要写一个逆序函数reserve。字符串逆序后&#xff0c;从前往后相加&#xff0c;以较长的字符串的长度为标准长度n&#…

可视化大屏设计模板 | 主题皮肤(报表UI设计)

下载使用可视化大屏设计模板&#xff0c;减少重复性操作&#xff0c;提高报表制作效率的同时也确保了报表风格一致&#xff0c;凸显关键数据信息。 软件&#xff1a;奥威BI系统&#xff0c;又称奥威BI数据可视化工具 所属功能板块&#xff1a;主题皮肤上传下载&#xff08;数…

Python实现机器学习(下)— 数据预处理、模型训练和模型评估

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本门课程将介绍人工智能相关概念&#xff0c;重点讲解机器学习原理机器基本算法&#xff08;监督学习及非监督学习&#xff09;。使用python&#xff0c;结合sklearn、Pycharm进行编程&#xff0c;介绍iris&#xff08;鸢尾…

Excel学习 WPS版

Excel学习 1.界面基础1.1 方格移动快捷键1.2 自动适配文字长度1.3 跨栏置中1.4 多个单元格同宽度&#xff1a;1.5 下拉框选择1.6 打印预览1.7 绘制边框1.8 冻结一行多行表头1.9 分割视图 2.日期相关2.1 今日日期快捷键2.2 月份提取 3.数学公式3.1 自动增长3.2 排序3.3 筛选3.4 …

数据结构——排序算法——冒泡排序

冒泡排序1 void swap(vector<int> arr, int i, int j) {int temp arr[i];arr[i] arr[j];arr[j] temp;}void bubbleSort1(vector<int> arr) {for (int i 0; i < arr.size() - 1; i){for (int j 0; j < arr.size() - 1 - i; j){if (arr[j] > arr[j 1…

【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem

前言【Unity编辑器扩展】 | 顶部菜单栏扩展 MenuItem一、创建多级菜单二、创建可使用快捷键的菜单项三、调节菜单显示顺序和可选择性四、创建可被勾选的菜单项五、右键菜单扩展5.1 Hierarchy 右键菜单5.2 Project 右键菜单5.3 Inspector 组件右键菜单六、AddComponentMenu 特性…

人工智能:神经细胞模型到神经网络模型

人工智能领域中的重要流派之一是&#xff1a;从神经细胞模型&#xff08;Neural Cell Model&#xff09;到神经网络模型&#xff08;Neural Network Model&#xff09;。 一、神经细胞模型 第一个人工神经细胞模型是“MP”模型&#xff0c;它是由麦卡洛克、匹茨合作&#xff0…

Java-华为真题-预定酒店

需求&#xff1a; 放暑假了&#xff0c;小王决定到某旅游景点游玩&#xff0c;他在网上搜索到了各种价位的酒店&#xff08;长度为n的数组A&#xff09;&#xff0c;他的心理价位是x元&#xff0c;请帮他筛选出k个最接近x元的酒店&#xff08;n>k>0&#xff09;&#xff…