2026-03-18 20:09:32 +03:00

251 lines
8.0 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.WebRTC;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using WebSocketSharp;
public class VideoChatMediaStreaming : MonoBehaviour
{
[SerializeField] private string clientId;
[SerializeField] private string IPAddress;
[SerializeField] private Camera cameraStream;
[SerializeField] private RawImage sourceImage;
[SerializeField] private RawImage destinationImage;
[SerializeField] private List<RawImage> recievedImages = new List<RawImage>();
private Dictionary<string, RTCPeerConnection> pcs = new Dictionary<string, RTCPeerConnection>();
private VideoStreamTrack videoTrack;
private bool hasRecievedOffer = false;
private SessionDescription recievedSessDescTemp;
private string recievedChannelId;
private WebSocket ws;
private WebCamTexture webCamTexture;
private int recievedImageCounter = 0;
private void Start()
{
//InitWebCamTexture();
InitClient(IPAddress, 8080);
}
private void Update()
{
if (hasRecievedOffer)
{
hasRecievedOffer = !hasRecievedOffer;
StartCoroutine(CreateAnswer(pcs[recievedChannelId], recievedChannelId));
}
}
private void OnDestroy()
{
videoTrack.Stop();
foreach (var connection in pcs.Values)
{
connection.Close();
}
ws.Close();
}
private void InitWebCamTexture()
{
WebCamDevice myDevice = new WebCamDevice();
myDevice = WebCamTexture.devices.Last();
webCamTexture = new WebCamTexture(myDevice.name);
sourceImage.texture = webCamTexture;
webCamTexture.Play();
}
private void InitClient(string ip, int port)
{
ws = new WebSocket($"ws://{ip}:{port}/{nameof(VideoChatMediaStreamService)}");
ws.OnMessage += (sender, e) =>
{
var signalingMessage = new SignalingMessage(e.Data);
switch(signalingMessage.Type)
{
case SignalingMessageType.OFFER:
if (clientId == signalingMessage.ChannelId.Substring(1, 1))
{
Debug.Log(clientId + " - Got <b>Offer</b> with channel ID " + signalingMessage.ChannelId + " from Maximus: " + signalingMessage.Message);
recievedChannelId = signalingMessage.ChannelId;
recievedSessDescTemp = SessionDescription.FromJson(signalingMessage.Message);
hasRecievedOffer = true;
}
break;
case SignalingMessageType.ANSWER:
if (clientId == signalingMessage.ChannelId.Substring(0, 1))
{
Debug.Log(clientId + " - Got <b>Answer</b> with channel ID " + signalingMessage.ChannelId + " from Maximus: " + signalingMessage.Message);
var recievedAnswerSessionDescTemp = SessionDescription.FromJson(signalingMessage.Message);
RTCSessionDescription answerSessionDesc = new RTCSessionDescription();
answerSessionDesc.type = RTCSdpType.Answer;
answerSessionDesc.sdp = recievedAnswerSessionDescTemp.Sdp;
pcs[signalingMessage.ChannelId].SetRemoteDescription(ref answerSessionDesc);
}
break;
case SignalingMessageType.CANDIDATE:
if (clientId == signalingMessage.ChannelId.Substring(1, 1))
{
Debug.Log(clientId + " - Got <b>Candidate</b> with channel ID " + signalingMessage.ChannelId + " from Maximus: " + signalingMessage.Message);
var candidateInit = CandidateInit.FromJson(signalingMessage.Message);
RTCIceCandidateInit init = new RTCIceCandidateInit();
init.sdpMid = candidateInit.SdpMid;
init.sdpMLineIndex = candidateInit.SdpMLineIndex;
init.candidate = candidateInit.Candidate;
RTCIceCandidate candidate = new RTCIceCandidate(init);
pcs[signalingMessage.ChannelId].AddIceCandidate(candidate);
}
break;
default:
if (e.Data.Contains("|"))
{
var connectionsIds = e.Data.Split('|');
foreach (var connectionId in connectionsIds)
{
if (connectionId.Contains(clientId))
{
pcs.Add(connectionId, CreatePeerConnection(connectionId));
}
}
}
else
{
clientId = e.Data;
}
break;
}
};
ws.Connect();
var gfxType = SystemInfo.graphicsDeviceType;
var format = WebRTC.GetSupportedRenderTextureFormat(gfxType);
//RenderTexture rt = new RenderTexture(1280, 720, 0, format);
//videoTrack = new VideoStreamTrack(sourceImage.texture);
videoTrack = cameraStream.CaptureStreamTrack(1280, 720);
sourceImage.texture = cameraStream.targetTexture;
StartCoroutine(WebRTC.Update());
}
private RTCPeerConnection CreatePeerConnection(string id)
{
var pc = new RTCPeerConnection();
pc.OnIceCandidate = candidate =>
{
var candidateInit = new CandidateInit()
{
SdpMid = candidate.SdpMid,
SdpMLineIndex = candidate.SdpMLineIndex ?? 0,
Candidate = candidate.Candidate
};
ws.Send("CANDIDATE!" + id + "!" + candidateInit.ConvertToJson());
};
pc.OnIceConnectionChange = state =>
{
Debug.Log(state);
};
pc.OnNegotiationNeeded = () =>
{
StartCoroutine(CreateOffer(pc, id));
};
pc.OnTrack = e =>
{
if (e.Track is VideoStreamTrack track)
{
track.OnVideoReceived += tex =>
{
recievedImages[recievedImageCounter].texture = tex;
recievedImageCounter++;
};
}
};
return pc;
}
private IEnumerator CreateOffer(RTCPeerConnection pc, string id)
{
var offer = pc.CreateOffer();
yield return offer;
var offerDesc = offer.Desc;
var localDescOp = pc.SetLocalDescription(ref offerDesc);
yield return localDescOp;
var offerSessionDesc = new SessionDescription()
{
SessionType = offerDesc.type.ToString(),
Sdp = offerDesc.sdp
};
ws.Send("OFFER!" + id + "!" + offerSessionDesc.ConvertToJson());
}
private IEnumerator CreateAnswer(RTCPeerConnection pc, string id)
{
RTCSessionDescription offerSessionDesc = new RTCSessionDescription();
offerSessionDesc.type = RTCSdpType.Offer;
offerSessionDesc.sdp = recievedSessDescTemp.Sdp;
var remoteDescOp = pc.SetRemoteDescription(ref offerSessionDesc);
yield return remoteDescOp;
var answer = pc.CreateAnswer();
yield return answer;
var answerDesc = answer.Desc;
var localDescOp = pc.SetLocalDescription(ref answerDesc);
yield return localDescOp;
var answerSessionDesc = new SessionDescription()
{
SessionType = answerDesc.type.ToString(),
Sdp = answerDesc.sdp
};
ws.Send("ANSWER!" + id + "!" + answerSessionDesc.ConvertToJson());
}
public void Call()
{
foreach (var connection in pcs)
{
if (connection.Key.Substring(0, 1) == clientId)
{
connection.Value.AddTrack(videoTrack);
}
}
}
}