我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Unity Sockect实现画面实时传输案例原理解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Unity Sockect实现画面实时传输案例原理解析

前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。

提示:以下是本篇文章正文内容,下面案例可供参考

一、Socket通信原理

Socket是比较常用的一种通信方式。有关介绍可以点击查看Socket通信原理

二、画面传输设计

 1.逻辑设计图

逻辑图

2.Unity服务端

首先创建一个Unity工程,然后新建Server场景,用于接受数据,展示画面。
然后再场景中创建一个RawImage并设置为全屏。

如图:

在这里插入图片描述

然后创建一个脚本,命名为UnityServer,再创建一个UnityServer.cs
在Start函数中创建Socket服务器,并开启一个线程用于接受数据。
这里要注意一点,不能在接受数据线程中处理数据,需要在主线程中进行处理。
因为Unity主线程里面的资源不允许其他线程进行访问。
在Update函数中处理数据,并展示图片。

UnityServer .cs代码如下:


using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public class UnityServer : MonoBehaviour {

	Socket socket = null;
	Thread thread = null;
	byte[] buffer = null;
	bool receState = true;

	int readTimes = 0;

    public RawImage rawImage;

    private Queue<byte[]> datas;

    void Start () {
		buffer = new byte[1024 * 1024 * 10];

        // 创建服务器, 以Tcp的方式
		socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		socket.Connect(IPAddress.Parse("192.168.1.87"), 10002);

        // 开启一个线程, 用于接受数据
		thread = new Thread(new ThreadStart(Receive));
		thread.Start();

        datas = new Queue<byte[]>();
    }

    private void Receive()
    {
        while (thread.ThreadState == ThreadState.Running && socket.Connected)
        {
            // 接受数据Buffer count是数据的长度
			int count = socket.Receive(buffer);
            if (receState && count > 0)
            {
				receState = false;
                BytesToImage(count, buffer);
            }
        }
    }

	MemoryStream ms = null;
	public void BytesToImage(int count, byte[] bytes)
    {
        try
        {
            ms = new MemoryStream(bytes, 0, count);
            datas.Enqueue(ms.ToArray());    // 将数据存储在一个队列中,在主线程中解析数据。这是一个多线程的处理。

            readTimes++;

            if (readTimes > 5000)
            {
                readTimes = 0;
                GC.Collect(2);  // 达到一定次数的时候,开启GC,释放内存
            }
        }
        catch
        {

        }
        receState = true;
    }

    void Update()
    {
        if (datas.Count > 0)
        {
            // 处理纹理数据,并显示
            Texture2D texture2D = new Texture2D(Screen.width, Screen.height);
            texture2D.LoadImage(datas.Dequeue());
            rawImage.texture = texture2D;
        }
    }

    void OnDestroy()
    {
        try
        {
            if (socket != null)
            {
                socket.Shutdown(SocketShutdown.Both);
            }
        }
        catch { }

        try
        {
            if (thread != null)
            {
                thread.Abort();
            }
        }
        catch { }

        datas.Clear();
    }
}

然后在场景中创建一个GameObject,将脚本挂载上,并将创建的RawImage拖拽到Inspector面板上对应的位置。

如图:

在这里插入图片描述

3.Unity客户端

然后我们创建一个客户端工程,创建一个Client场景。
选中Main Camera,用Ctrl+D复制一个摄像机,放在Main Camera下面。
设置localPosition 和 localRotation为零。
这个相机的主要作用抓取屏幕渲染纹理。

如图:

在这里插入图片描述

然后再创建一个脚本,命名为UnityClient.cs脚本。在Start中开启Socket,然后开启一个线程发送数据。
将其挂载在Main Camera上面,并将渲染摄像机拖拽到相应的位置。

UnityClient.cs代码如下:


using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

public class UnityClient : MonoBehaviour {

	public Camera cam;
	public int port = 10002;

	RenderTexture cameraView = null;

	Socket socket = null;

	Thread thread = null;

	bool success = true;

	Dictionary<string, Client> clients = new Dictionary<string, Client>();

	Vector3 old_position;   // 旧位置
	Quaternion old_rotation;	// 旧旋转

	void Start () {
		cameraView = new RenderTexture(Screen.width, Screen.height, 24);
		cameraView.enableRandomWrite = true;

		cam.targetTexture = cameraView;
		old_position = transform.position;
		old_rotation = transform.rotation;

        // 开启Socket
		socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		socket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.87"), port));
		socket.Listen(100);

        // 开启一个线程发送渲染数据
		thread = new Thread(new ThreadStart(OnStart));
		thread.Start();
	}

	int isNewAdd = 0;

	void OnStart()
    {
        Debug.Log("Socket创建成功");
        while (thread.ThreadState == ThreadState.Running)
        {
			Socket _socket = socket.Accept();
            if (clients.ContainsKey(_socket.RemoteEndPoint.ToString()))
            {
                try
                {
					clients[_socket.RemoteEndPoint.ToString()].socket.Shutdown(SocketShutdown.Both);
                }
                catch
                {
                }
				clients.Remove(_socket.RemoteEndPoint.ToString());
            }

			Client client = new Client
			{
				socket = _socket
			};

			clients.Add(_socket.RemoteEndPoint.ToString(), client);

			isNewAdd = 1;
        }
    }

	void Update()
    {
        if (success && clients.Count > 0)
        {
			success = false;
            SendTexture();
        }

        if (isNewAdd > 0)
        {
            isNewAdd = 0;
            SendTexture(1);
        }
    }

	void OnGUI()
    {
		GUI.DrawTexture(new Rect(10, 10, 240, 135), cameraView, ScaleMode.StretchToFill);
    }

	void OnApplicationQuit()
    {
        try
        {
			socket.Shutdown(SocketShutdown.Both);
        }
        catch { }

        try
        {
			thread.Abort();
        }
        catch { }
    }

	Texture2D screenShot = null;
	int gc_count = 0;

	void SendTexture(int isInt = 0)
    {
        if ((!old_position.Equals(transform.position) || !old_rotation.Equals(transform.rotation)) || isInt == 1)
        {
            if (null == screenShot)
            {
				screenShot = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
            }

            // 读取屏幕像素进行渲染
			RenderTexture.active = cameraView;
			screenShot.ReadPixels(new Rect(0, 0, cameraView.width, cameraView.height), 0, 0);
			RenderTexture.active = null;
			byte[] bytes = screenShot.EncodeToJPG(100);

            foreach (var val in clients.Values)
            {
                try
                {
					val.socket.Send(bytes);
                }
                catch
                {
                    if (!val.socket.Connected)
                    {
                        clients.Remove(val.socket.RemoteEndPoint.ToString());
                    }
                }
            }
            gc_count++;
            if (gc_count > 5000)
            {
                gc_count = 0;
                GC.Collect(2);
            }
            Debug.Log("发送数据:" + (float)bytes.Length / 1024f + "KB");

            old_position = cam.transform.position;
            old_rotation = cam.transform.rotation;
        }
        success = true;
    }

    void OnDestroy()
    {
        try
        {
            socket.Shutdown(SocketShutdown.Both);
        }
        catch { }

        try
        {
            thread.Abort();
        }
        catch { }
    }
}

class Client {
	public Socket socket = null;
}

4.最终效果

在这里插入图片描述

到此这篇关于Unity Sockect实现画面实时传输的文章就介绍到这了,更多相关Unity Sockect画面实时传输内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Unity Sockect实现画面实时传输案例原理解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录