251 lines
8.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|