同步画面有问题,传图片吧
using System.Text;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System.Net;
using System;
using System.Threading.Tasks;
using System.Threading;
public class UdpNet : MonoBehaviour
{
public static UdpNet Instance { get; private set; }
/// <summary>
/// 默认发送数据最大长度,UDP一次性最大发送长度为65536,也就是64kb
/// </summary>
const int DEFAULT_SIZE = 60000;
public UdpClient udpClient;
/// <summary>
/// 是否可以发送数据
/// </summary>
public bool isSend;
/// <summary>
/// 要发送的数据缓存队列
/// </summary>
private Queue<NetData> datasBuffer;
/// <summary>
/// 当前发送的数据
/// </summary>
private NetData curSendData;
/// <summary>
/// 数据接收注册方法
/// </summary>
public UnityAction<byte[]> ReceiveDataAction;
/// <summary>
/// UDP是否开启
/// </summary>
public bool IsConnect { get; private set; }
/// <summary>
/// 数据接收缓存队列
/// </summary>
public Queue<NetData> receiveBufferQueue;
/// <summary>
/// 当前接收的缓存数据
/// </summary>
private NetData curReceiveData;
/// <summary>
/// 当前发送的数据长度
/// </summary> <summary>
private int byteLen;
/// <summary>
/// 当前接收的数据长度
/// </summary> <summary>
private int receiveLen;
public int Port;
/// <summary>
/// 开启UDP
/// </summary>
/// <param name="port"></param>
public void Init(int port)
{
if (udpClient != null)
{
//Debug.LogWarning("已开启UDP控制端");
return;
}
Port = port;
udpClient = new UdpClient(port);
datasBuffer = new Queue<NetData>();
receiveBufferQueue = new Queue<NetData>();
isSend = true;
IsConnect = true;
ReceiveFile();
}
/// <summary>
/// 关闭UDP
/// </summary>
void Close()
{
if (udpClient != null)
{
udpClient.Close();
udpClient.Dispose();
udpClient = null;
IsConnect = false;
isSend = false;
datasBuffer.Clear();
curSendData = null;
curReceiveData = null;
receiveBufferQueue.Clear();
//Debug.Log("UdpNet 已关闭");
}
}
#region Mono方法
void Awake()
{
Instance = this;
// Init(Port);
}
private void Update()
{
if (!IsConnect)
{
return;
}
if (isSend && datasBuffer.Count > 0 && curSendData == null)
{
isSend = false;
curSendData = datasBuffer.Dequeue();
byteLen = 0;
int len = curSendData.byteArray.length;
if (curSendData.byteArray.length > DEFAULT_SIZE)
{
len = DEFAULT_SIZE;
}
byteLen += len;
byte[] bytes = curSendData.byteArray.Read(len);
udpClient.BeginSend(bytes, len, curSendData.iPEndPoint, SendFileAysncCallBack, curSendData);
}
if (receiveBufferQueue.Count > 0)
{
ReceiveDataAction?.Invoke(receiveBufferQueue.Dequeue().byteArray.bytes);
}
}
void OnDestroy()
{
Close();
}
#endregion
#region 发送消息
public void SendMsg(string msg, string ip, int port)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
SendBytes(data, ip, port);
}
public void SendMsg(string msg, IPEndPoint iPEndPoint)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
SendBytes(data, iPEndPoint);
}
public void SendBytes(byte[] data, string ip, int port)
{
IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
SendBytes(data, iPEndPoint);
}
public void SendBytes(byte[] data, IPEndPoint iPEndPoint)
{
byte[] bytes = Encode(data);
//Debug.Log(bytes.Length);
ByteArray byteArray = new ByteArray(bytes);
datasBuffer.Enqueue(new NetData(byteArray, iPEndPoint));
}
private void SendFileAysncCallBack(IAsyncResult ar)
{
try
{
int count = udpClient.EndSend(ar);
if (ar.IsCompleted)
{
curSendData.byteArray.readIdx += count;
}
else
{
Debug.Log("发送未成功,重新发送");
}
if (curSendData.byteArray.length == 0)
{
isSend = true;
//Debug.Log("发送完毕,共发送数据: " + byteLen);
curSendData = null;
return;
}
int len = curSendData.byteArray.length;
if (curSendData.byteArray.length > DEFAULT_SIZE)
{
len = DEFAULT_SIZE;
}
byte[] bytes;
lock (curSendData)
{
bytes = curSendData.byteArray.Read(len);
}
byteLen += len;
//Debug.Log(len);
RunThread(bytes, len);
}
catch (System.Exception e)
{
Debug.LogError(e.Message);
Close();
return;
}
}
//延迟1毫秒发送已缓解udp无法接收完全的问题
async void RunThread(byte[] bytes, int len)
{
await Task.Run(() =>
{
Thread.Sleep(1);
udpClient.BeginSend(bytes, len, curSendData.iPEndPoint, SendFileAysncCallBack, curSendData);
});
}
#endregion
#region 接收消息
private void ReceiveFile()
{
udpClient.BeginReceive(ReceiveFileAsyncBackCall, null);
}
private void ReceiveFileAsyncBackCall(IAsyncResult ar)
{
IPEndPoint remoteIp = null;
byte[] data = udpClient.EndReceive(ar, ref remoteIp);
receiveLen += data.Length;
if (curReceiveData == null)
{
int len = Decode(data, out byte[] conData);
curReceiveData = new NetData(new ByteArray(len), remoteIp);
// curReceiveData.byteArray.Write(data, 4, data.Length - 4);
curReceiveData.byteArray.Write(conData, 0, conData.Length);
//Debug.Log($"当前接收数据长度: {receiveLen},总接收数据长度: {receiveLen}, 当前剩余容量: {curReceiveData.byteArray.remain}");
}
else
{
int dataLen = data.Length;
if (data.Length > curReceiveData.byteArray.remain)
{
dataLen = curReceiveData.byteArray.remain;
}
curReceiveData.byteArray.Write(data, 0, dataLen);
//Debug.Log($"当前接收数据长度: {data.Length},总接收数据长度: {receiveLen}, 当前剩余容量: {curReceiveData.byteArray.remain}");
}
if (curReceiveData.byteArray.remain == 0)
{
receiveBufferQueue.Enqueue(curReceiveData);
//Debug.Log("接收完毕: " + curReceiveData.byteArray.writeIdx);
curReceiveData = null;
}
ReceiveFile();
}
#endregion
private static byte[] Encode(byte[] data)
{
byte[] bytes = new byte[data.Length + 4];
byte[] byteLen = BitConverter.GetBytes(data.Length);
Array.Copy(byteLen, 0, bytes, 0, 4);
Array.Copy(data, 0, bytes, 4, data.Length);
//Debug.Log(bytes.Length);
return bytes;
}
private static int Decode(byte[] data, out byte[] bytes)
{
byte[] byteLen = new byte[4];
Array.Copy(data, 0, byteLen, 0, 4);
int len = BitConverter.ToInt32(byteLen);
bytes = new byte[data.Length - 4];
//Debug.Log("总数据长度: " + (len + 4));
//Debug.Log("数据内容长度: " + len);
Array.Copy(data, 4, bytes, 0, bytes.Length);
return len;
}
public class NetData
{
public NetData(ByteArray byteArray, IPEndPoint iPEndPoint)
{
this.byteArray = byteArray;
this.iPEndPoint = iPEndPoint;
}
public ByteArray byteArray;
public IPEndPoint iPEndPoint;
}
}
using UnityEngine.UI;
using System;
[Serializable]
public class ByteArray
{
//默认大小
const int DEFAULT_SIZE = 4096;
//初始大小
int initSize = 0;
//缓冲区
public byte[] bytes;
//读写位置
public int readIdx = 0;
public int writeIdx = 0;
//容量
private int capacity = 0;
//剩余空间
public int remain { get { return capacity - writeIdx; } }
//数据长度
public int length { get { return writeIdx - readIdx; } }
//构造函数
public ByteArray(int size = DEFAULT_SIZE)
{
bytes = new byte[size];
capacity = size;
initSize = size;
readIdx = 0;
writeIdx = 0;
}
//构造函数
public ByteArray(byte[] defaultBytes)
{
bytes = defaultBytes;
capacity = defaultBytes.Length;
initSize = defaultBytes.Length;
readIdx = 0;
writeIdx = defaultBytes.Length;
}
//重设尺寸
public void ReSize(int size)
{
if (size < length) return;
if (size < initSize) return;
int n = 1;
while (n < size) n *= 2;
capacity = n;
byte[] newBytes = new byte[capacity];
Array.Copy(bytes, readIdx, newBytes, 0, writeIdx - readIdx);
bytes = newBytes;
writeIdx = length;
readIdx = 0;
}
//写入数据
public int Write(byte[] bs, int offset, int count)
{
// UnityEngine.Debug.Log($"remain: {remain} - bs.Length: {bs.Length} - offset: {offset} - count{count} - bytes.Length: {bytes.Length} - writeIdx: {writeIdx}");
if (remain < count)
{
ReSize(length + count);
}
Array.Copy(bs, offset, bytes, writeIdx, count);
writeIdx += count;
return count;
}
//读取数据
public int Read(byte[] bs, int offset, int count)
{
count = Math.Min(count, length);
Array.Copy(bytes, 0, bs, offset, count);
readIdx += count;
CheckAndMoveBytes();
return count;
}
//读取数据
public byte[] Read(int count)
{
// UnityEngine.Debug.Log($"当前数据长度为 {length},从 {readIdx} 开始读取长度为 {count} 的数据, 剩余数据长度为 {writeIdx - readIdx - count}");
byte[] bs = new byte[count];
Array.Copy(bytes, readIdx, bs, 0, count);
// readIdx += count;
return bs;
}
//检查并移动数据
public void CheckAndMoveBytes()
{
if (length < 8)
{
MoveBytes();
}
}
//移动数据
public void MoveBytes()
{
Array.Copy(bytes, readIdx, bytes, 0, length);
writeIdx = length;
readIdx = 0;
}
//读取Int16
public Int16 ReadInt16()
{
if (length < 2) return 0;
Int16 ret = BitConverter.ToInt16(bytes, readIdx);
readIdx += 2;
CheckAndMoveBytes();
return ret;
}
//读取Int32
public Int32 ReadInt32()
{
if (length < 4) return 0;
Int32 ret = BitConverter.ToInt32(bytes, readIdx);
readIdx += 4;
CheckAndMoveBytes();
return ret;
}
//打印缓冲区
public override string ToString()
{
return BitConverter.ToString(bytes, readIdx, length);
}
//打印调试信息
public string Debug()
{
return string.Format("readIdx({0}) writeIdx({1}) bytes({2})",
readIdx,
writeIdx,
BitConverter.ToString(bytes, 0, capacity)
);
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
//接收视频画面
public class : MonoBehaviour
{
public RawImage rawImage;
public int port = 8889;
// Start is called before the first frame update
void Start()
{
UdpNet.Instance.Init(port);
UdpNet.Instance.ReceiveDataAction += ReceiveDataAction;
}
private void ReceiveDataAction(byte[] arg0)
{
if (arg0.Length < 1000)
{
Debug.Log(Encoding.UTF8.GetString(arg0));
return;
}
Texture2D texture2D = new Texture2D(10, 10);
texture2D.LoadImage(arg0);
texture2D.Apply();
rawImage.texture = texture2D;
Resources.UnloadUnusedAssets();
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using System.Net;
using UnityEngine;
using UnityEngine.UI;
//发送视频画面
public class SendWebCameraVideo: MonoBehaviour
{
public string deviceName;
public WebCamTexture webCam;
public RawImage rawImage;
public int frames = 30;
public string ToIP = "127.0.0.1";
public int ToPort = 8889;
public int Port = 7777;
private IPEndPoint iPEndPoint;
public float maxTime;
public float timer;
public bool send;
// Start is called before the first frame update
void Start()
{
UdpNet.Instance.Init(Port);
WebCamDevice[] devices = WebCamTexture.devices;
deviceName = devices[0].name;
RectTransform rawRect = rawImage.GetComponent<RectTransform>();
webCam = new WebCamTexture(deviceName, (int)rawRect.sizeDelta.x, (int)rawRect.sizeDelta.y, frames);//设置宽、高和帧率
rawImage.texture = webCam;//渲染脚本所在有RawImage组件的物体
maxTime = 1f / frames;
iPEndPoint = new IPEndPoint(IPAddress.Parse(ToIP), ToPort);
UdpNet.Instance.SendMsg("gogogo", iPEndPoint);
}
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
if (timer >= maxTime && send)
{
// send = false;
timer = 0;
byte[] data = TextureToTexture2D(rawImage.texture).EncodeToJPG();
UdpNet.Instance.SendBytes(data, iPEndPoint);
// Resources.UnloadUnusedAssets();
}
}
public void Play()
{
webCam.Play();
send = true;
}
public void Pause()
{
send = false;
webCam.Pause();
}
/// 运行模式下Texture转换成Texture2D
private Texture2D TextureToTexture2D(Texture texture)
{
Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
RenderTexture currentRT = RenderTexture.active;
RenderTexture renderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
Graphics.Blit(texture, renderTexture);
RenderTexture.active = renderTexture;
texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
texture2D.Apply();
RenderTexture.active = currentRT;
RenderTexture.ReleaseTemporary(renderTexture);
return texture2D;
}
}
建两个工程,一个发送一个接收,UdpNet和ByteArray是通用的,每个工程都必须要有
1.发送工程界面
2.接收界面工程