服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Unity Sockect实现画面实时传输案例原理解析

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

2022-11-30 13:26yq_sprite C#

Socket是比较常用的一种通信方式,本文通过案例给大家介绍Unity Sockect实现画面实时传输功能,感兴趣的朋友一起看看吧

前言

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

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

一、Socket通信原理

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

二、画面传输设计

 1.逻辑设计图

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

2.Unity服务端

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

如图:

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

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

UnityServer .cs代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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面板上对应的位置。

如图:

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

3.Unity客户端

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

如图:

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

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

UnityClient.cs代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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实现画面实时传输的文章就介绍到这了,更多相关Unity Sockect画面实时传输内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq_34907362/article/details/119864856

延伸 · 阅读

精彩推荐