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 recievedImages = new List(); private Dictionary pcs = new Dictionary(); 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 Offer 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 Answer 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 Candidate 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); } } } }