Unity Sockect實現(xiàn)畫面實時傳輸案例原理解析
前言
提示:這里可以添加本文要記錄的大概內(nèi)容:
例如:隨著人工智能的不斷發(fā)展,機器學習這門技術(shù)也越來越重要,很多人都開啟了學習機器學習,本文就介紹了機器學習的基礎(chǔ)內(nèi)容。
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、Socket通信原理
Socket是比較常用的一種通信方式。有關(guān)介紹可以點擊查看Socket通信原理
二、畫面?zhèn)鬏斣O(shè)計
1.邏輯設(shè)計圖

2.Unity服務(wù)端
首先創(chuàng)建一個Unity工程,然后新建Server場景,用于接受數(shù)據(jù),展示畫面。
然后再場景中創(chuàng)建一個RawImage并設(shè)置為全屏。
如圖:

然后創(chuàng)建一個腳本,命名為UnityServer,再創(chuàng)建一個UnityServer.cs
在Start函數(shù)中創(chuàng)建Socket服務(wù)器,并開啟一個線程用于接受數(shù)據(jù)。
這里要注意一點,不能在接受數(shù)據(jù)線程中處理數(shù)據(jù),需要在主線程中進行處理。
因為Unity主線程里面的資源不允許其他線程進行訪問。
在Update函數(shù)中處理數(shù)據(jù),并展示圖片。
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];
// 創(chuàng)建服務(wù)器, 以Tcp的方式
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(IPAddress.Parse("192.168.1.87"), 10002);
// 開啟一個線程, 用于接受數(shù)據(jù)
thread = new Thread(new ThreadStart(Receive));
thread.Start();
datas = new Queue<byte[]>();
}
private void Receive()
{
while (thread.ThreadState == ThreadState.Running && socket.Connected)
{
// 接受數(shù)據(jù)Buffer count是數(shù)據(jù)的長度
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()); // 將數(shù)據(jù)存儲在一個隊列中,在主線程中解析數(shù)據(jù)。這是一個多線程的處理。
readTimes++;
if (readTimes > 5000)
{
readTimes = 0;
GC.Collect(2); // 達到一定次數(shù)的時候,開啟GC,釋放內(nèi)存
}
}
catch
{
}
receState = true;
}
void Update()
{
if (datas.Count > 0)
{
// 處理紋理數(shù)據(jù),并顯示
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();
}
}
然后在場景中創(chuàng)建一個GameObject,將腳本掛載上,并將創(chuàng)建的RawImage拖拽到Inspector面板上對應(yīng)的位置。
如圖:

3.Unity客戶端
然后我們創(chuàng)建一個客戶端工程,創(chuàng)建一個Client場景。
選中Main Camera,用Ctrl+D復制一個攝像機,放在Main Camera下面。
設(shè)置localPosition 和 localRotation為零。
這個相機的主要作用抓取屏幕渲染紋理。
如圖:

然后再創(chuàng)建一個腳本,命名為UnityClient.cs腳本。在Start中開啟Socket,然后開啟一個線程發(fā)送數(shù)據(jù)。
將其掛載在Main Camera上面,并將渲染攝像機拖拽到相應(yīng)的位置。
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; // 舊旋轉(zhuǎn)
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);
// 開啟一個線程發(fā)送渲染數(shù)據(jù)
thread = new Thread(new ThreadStart(OnStart));
thread.Start();
}
int isNewAdd = 0;
void OnStart()
{
Debug.Log("Socket創(chuàng)建成功");
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("發(fā)送數(shù)據(jù):" + (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.最終效果

到此這篇關(guān)于Unity Sockect實現(xiàn)畫面實時傳輸?shù)奈恼戮徒榻B到這了,更多相關(guān)Unity Sockect畫面實時傳輸內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#使用GZipStream實現(xiàn)文件的壓縮與解壓
這篇文章主要為大家詳細介紹了C#使用GZipStream實現(xiàn)文件的壓縮與解壓,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10
利用MySqlBulkLoader實現(xiàn)批量插入數(shù)據(jù)的示例詳解
MySQLBulkLoader是MySQL?Connector/Net類中的一個類,用于包裝MySQL語句。本文將利用MySqlBulkLoader實現(xiàn)批量插入數(shù)據(jù)功能,感興趣的可以了解一下2022-06-06
C#解決SQlite并發(fā)異常問題的方法(使用讀寫鎖)
這篇文章主要介紹了C#解決SQlite并發(fā)異常問題的方法,通過使用讀寫鎖達到多線程安全訪問,進而解決SQLite并發(fā)異常的問題,具有一定參考借鑒價值,需要的朋友可以參考下2016-07-07
C#中FileSystemWatcher類實現(xiàn)監(jiān)控文件夾
在C#中,如果你想要監(jiān)控一個文件夾內(nèi)文件的變動情況,比如文件的創(chuàng)建、刪除、修改等,你可以使用FileSystemWatcher類,下面就來介紹一下FileSystemWatcher監(jiān)控的使用,感興趣的可以了解一下2024-03-03
C#控制臺程序如何發(fā)布到服務(wù)器Linux上運行
這篇文章主要給大家介紹了關(guān)于C#控制臺程序如何發(fā)布到服務(wù)器Linux上運行的相關(guān)資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2021-11-11

