最近刚好有需要做个视频通话,走局域网的那种,林新发大佬之前发布的那个工程没了,不然可以直接用他的(去年还是前年我试过那个工程,可以手机跟电脑实现视频通话完全不卡,感兴趣可以去看看文章)【游戏开发实战】Unity从零开发多人视频聊天功能,无聊了就和自己视频聊天(附源码 | Mirror | 多人视频 | 详细教程)_unity mirror实现语音-优快云博客
下面是基于yq_sprite大佬的源码改的,原文章的源码只能在本机进行传输,两台机器传输就传不了,我也是折腾了一天才搞通,在此踩了坑特此记录,顺带给新来的人排下雷,废话没了,上代码:
原文中的客户端代码(实际上是服务端)
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;
public class UnityClient : MonoBehaviour {
public int port = 10002;
Socket socket = null;
Thread thread = null;
bool success = true;
Dictionary<string, Client> clients = new Dictionary<string, Client>();
private WebCamTexture webCamTexture;
private Texture2D texture2D;
public RawImage image;
private void Start()
{
Go();
}
public void Go () {
// 开启Socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
socket.Listen(100);
// 开启一个线程发送渲染数据
thread = new Thread(new ThreadStart(OnStart));
thread.Start();
WebCamDevice[] devices = WebCamTexture.devices;
webCamTexture = new WebCamTexture(devices[0].name, 1920, 1080, 60);
webCamTexture.Play();
texture2D = new Texture2D(webCamTexture.width, webCamTexture.height, TextureFormat.RGB24, false);
}
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;
texture2D.SetPixels(webCamTexture.GetPixels());
texture2D.Apply();
image.texture = texture2D;
SendTexture(texture2D);
}
}
void OnApplicationQuit()
{
try
{
socket.Shutdown(SocketShutdown.Both);
}
catch { }
try
{
thread.Abort();
}
catch { }
}
int gc_count = 0;
void SendTexture(Texture2D texture2D)
{
if (texture2D != null)
{
byte[] bytes = texture2D.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");
}
success = true;
}
void OnDestroy()
{
try
{
socket.Shutdown(SocketShutdown.Both);
}
catch { }
try
{
thread.Abort();
}
catch { }
}
}
class Client {
public Socket socket = null;
}
原文中的服务端代码(实际是客户端代码):
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;
public InputField inputField;
private Queue<byte[]> datas;
public void Go()
{
buffer = new byte[1024 * 1024 * 10];
// 创建服务器, 以Tcp的方式
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(IPAddress.Parse(inputField.text), 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()
{
try
{
if (datas.Count > 0)
{
// 处理纹理数据,并显示
Texture2D texture2D = new Texture2D(Screen.width, Screen.height);
texture2D.LoadImage(datas.Dequeue());
rawImage.texture = texture2D;
}
}
catch (Exception e)
{
}
}
void OnDestroy()
{
try
{
if (socket != null)
{
socket.Shutdown(SocketShutdown.Both);
}
}
catch { }
try
{
if (thread != null)
{
thread.Abort();
}
}
catch { }
datas.Clear();
}
}
这里面实际上还有些坑,比如连接完成关闭程序的时候会报错,粘包问题,以及频繁使用SetPixels方法高度消耗性能等问题,还需要后人注意了,再次再次鸣谢大佬们的文章