495 lines
16 KiB
C#
495 lines
16 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Unity.RenderStreaming.Signaling;
|
||
using Unity.WebRTC;
|
||
using UnityEngine;
|
||
|
||
namespace Unity.RenderStreaming
|
||
{
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
internal struct RenderStreamingDependencies
|
||
{
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public ISignaling signaling;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public RTCConfiguration config;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public Func<IEnumerator, Coroutine> startCoroutine;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public Action<Coroutine> stopCoroutine;
|
||
|
||
/// <summary>
|
||
/// unit is second;
|
||
/// </summary>
|
||
public float resentOfferInterval;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
internal class SignalingManagerInternal : IDisposable,
|
||
IRenderStreamingHandler, IRenderStreamingDelegate
|
||
{
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action onStart;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string> onCreatedConnection;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string> onDeletedConnection;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string, string> onGotOffer;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string, string> onGotAnswer;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string> onConnect;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string> onDisconnect;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string, RTCRtpTransceiver> onAddTransceiver;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public event Action<string, RTCDataChannel> onAddChannel;
|
||
|
||
/// <summary>
|
||
/// 参与者加入事件 (connectionId, participantId)
|
||
/// </summary>
|
||
public event Action<string, string> onParticipantJoined;
|
||
|
||
/// <summary>
|
||
/// 参与者离开事件 (connectionId, participantId)
|
||
/// </summary>
|
||
public event Action<string, string> onParticipantLeft;
|
||
|
||
/// <summary>
|
||
/// 呼叫请求事件 (connectionId, data)
|
||
/// </summary>
|
||
public event Action<string, string> onCallRequest;
|
||
|
||
/// <summary>
|
||
/// 自定义消息事件 (connectionId, participantId, message)
|
||
/// </summary>
|
||
public event Action<string, string, string> onMessage;
|
||
|
||
private bool _disposed;
|
||
private readonly ISignaling _signaling;
|
||
private RTCConfiguration _config;
|
||
private readonly Func<IEnumerator, Coroutine> _startCoroutine;
|
||
private readonly Action<Coroutine> _stopCoroutine;
|
||
private readonly Dictionary<string, PeerConnection> _mapConnectionIdAndPeer =
|
||
new Dictionary<string, PeerConnection>();
|
||
private bool _runningResendCoroutine;
|
||
private float _resendInterval = 3.0f;
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="dependencies"></param>
|
||
public SignalingManagerInternal(ref RenderStreamingDependencies dependencies)
|
||
{
|
||
if (dependencies.signaling == null)
|
||
throw new ArgumentException("Signaling instance is null.");
|
||
if (dependencies.startCoroutine == null)
|
||
throw new ArgumentException("Coroutine action instance is null.");
|
||
|
||
_config = dependencies.config;
|
||
_startCoroutine = dependencies.startCoroutine;
|
||
_stopCoroutine = dependencies.stopCoroutine;
|
||
_resendInterval = dependencies.resentOfferInterval;
|
||
_signaling = dependencies.signaling;
|
||
_signaling.OnStart += OnStart;
|
||
_signaling.OnCreateConnection += OnCreateConnection;
|
||
_signaling.OnDestroyConnection += OnDestroyConnection;
|
||
_signaling.OnOffer += OnOffer;
|
||
_signaling.OnAnswer += OnAnswer;
|
||
_signaling.OnIceCandidate += OnIceCandidate;
|
||
_signaling.OnParticipantJoined += OnParticipantJoinedHandler;
|
||
_signaling.OnParticipantLeft += OnParticipantLeftHandler;
|
||
_signaling.OnCallRequest += OnCallRequestHandler;
|
||
_signaling.OnMessage += OnMessageHandler;
|
||
_signaling.Start();
|
||
_startCoroutine(WebRTC.WebRTC.Update());
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
~SignalingManagerInternal()
|
||
{
|
||
Dispose();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
if (this._disposed)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_runningResendCoroutine = false;
|
||
|
||
_signaling.Stop();
|
||
_signaling.OnStart -= OnStart;
|
||
_signaling.OnCreateConnection -= OnCreateConnection;
|
||
_signaling.OnDestroyConnection -= OnDestroyConnection;
|
||
_signaling.OnOffer -= OnOffer;
|
||
_signaling.OnAnswer -= OnAnswer;
|
||
_signaling.OnIceCandidate -= OnIceCandidate;
|
||
_signaling.OnParticipantJoined -= OnParticipantJoinedHandler;
|
||
_signaling.OnParticipantLeft -= OnParticipantLeftHandler;
|
||
_signaling.OnCallRequest -= OnCallRequestHandler;
|
||
_signaling.OnMessage -= OnMessageHandler;
|
||
|
||
foreach (var pair in _mapConnectionIdAndPeer)
|
||
pair.Value.Dispose();
|
||
|
||
this._disposed = true;
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
public void CreateConnection(string connectionId)
|
||
{
|
||
_signaling.OpenConnection(connectionId);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
public void DeleteConnection(string connectionId)
|
||
{
|
||
_signaling.CloseConnection(connectionId);
|
||
}
|
||
|
||
public bool ExistConnection(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer.ContainsKey(connectionId);
|
||
}
|
||
|
||
public bool IsConnected(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer.TryGetValue(connectionId, out var peer) && peer.IsConnected();
|
||
}
|
||
|
||
public bool IsStable(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer.TryGetValue(connectionId, out var peer) && peer.IsStable();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="track"></param>
|
||
public void RemoveSenderTrack(string connectionId, MediaStreamTrack track)
|
||
{
|
||
var sender = GetSenders(connectionId).First(s => s.Track == track);
|
||
_mapConnectionIdAndPeer[connectionId].peer.RemoveTrack(sender);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="track"></param>
|
||
/// <param name="direction"></param>
|
||
/// <returns></returns>
|
||
public RTCRtpTransceiver AddTransceiver(string connectionId, MediaStreamTrack track, RTCRtpTransceiverInit init = null)
|
||
{
|
||
var transceiver = _mapConnectionIdAndPeer[connectionId].peer.AddTransceiver(track, init);
|
||
return transceiver;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="kind"></param>
|
||
/// <param name="direction"></param>
|
||
/// <returns></returns>
|
||
public RTCRtpTransceiver AddTransceiver(string connectionId, TrackKind kind, RTCRtpTransceiverInit init = null)
|
||
{
|
||
var transceiver = _mapConnectionIdAndPeer[connectionId].peer.AddTransceiver(kind, init);
|
||
return transceiver;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="name"></param>
|
||
/// <returns></returns>
|
||
public RTCDataChannel CreateChannel(string connectionId, string name)
|
||
{
|
||
RTCDataChannelInit conf = new RTCDataChannelInit();
|
||
if (string.IsNullOrEmpty(name))
|
||
name = Guid.NewGuid().ToString();
|
||
return _mapConnectionIdAndPeer[connectionId].peer.CreateDataChannel(name, conf);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="track"></param>
|
||
/// <returns></returns>
|
||
public IEnumerable<RTCRtpSender> GetSenders(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer[connectionId].peer.GetSenders();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="track"></param>
|
||
/// <returns></returns>
|
||
public IEnumerable<RTCRtpReceiver> GetReceivers(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer[connectionId].peer.GetReceivers();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
/// <param name="track"></param>
|
||
/// <returns></returns>
|
||
public IEnumerable<RTCRtpTransceiver> GetTransceivers(string connectionId)
|
||
{
|
||
return _mapConnectionIdAndPeer[connectionId].peer.GetTransceivers();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
public void SendOffer(string connectionId)
|
||
{
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(connectionId, out var pc))
|
||
return;
|
||
pc.SendOffer();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="connectionId"></param>
|
||
public void SendAnswer(string connectionId)
|
||
{
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(connectionId, out var pc))
|
||
return;
|
||
pc.SendAnswer();
|
||
}
|
||
|
||
IEnumerator ResendOfferCoroutine()
|
||
{
|
||
HashSet<string> failedConnections = new HashSet<string>();
|
||
while (_runningResendCoroutine)
|
||
{
|
||
failedConnections.Clear();
|
||
foreach (var peer in _mapConnectionIdAndPeer)
|
||
{
|
||
if (peer.Value.peer.ConnectionState == RTCPeerConnectionState.Failed)
|
||
{
|
||
failedConnections.Add(peer.Key);
|
||
}
|
||
else if (peer.Value.waitingAnswer)
|
||
{
|
||
peer.Value.SendOffer();
|
||
}
|
||
}
|
||
|
||
foreach (var connectionId in failedConnections)
|
||
{
|
||
DestroyConnection(connectionId);
|
||
}
|
||
|
||
yield return 0;
|
||
}
|
||
}
|
||
|
||
void OnStart(ISignaling signaling)
|
||
{
|
||
if (!_runningResendCoroutine)
|
||
{
|
||
_runningResendCoroutine = true;
|
||
_startCoroutine(ResendOfferCoroutine());
|
||
}
|
||
onStart?.Invoke();
|
||
}
|
||
|
||
void OnCreateConnection(ISignaling signaling, string connectionId, bool polite)
|
||
{
|
||
CreatePeerConnection(connectionId, polite);
|
||
onCreatedConnection?.Invoke(connectionId);
|
||
}
|
||
|
||
void OnDestroyConnection(ISignaling signaling, string connectionId)
|
||
{
|
||
DestroyConnection(connectionId);
|
||
}
|
||
|
||
void DestroyConnection(string connectionId)
|
||
{
|
||
DeletePeerConnection(connectionId);
|
||
onDeletedConnection?.Invoke(connectionId);
|
||
}
|
||
|
||
PeerConnection CreatePeerConnection(string connectionId, bool polite)
|
||
{
|
||
if (_mapConnectionIdAndPeer.TryGetValue(connectionId, out var peer))
|
||
{
|
||
peer.Dispose();
|
||
}
|
||
|
||
peer = new PeerConnection(polite, _config, _resendInterval, _startCoroutine, _stopCoroutine);
|
||
_mapConnectionIdAndPeer[connectionId] = peer;
|
||
|
||
peer.OnConnectHandler += () => onConnect?.Invoke(connectionId);
|
||
peer.OnDisconnectHandler += () =>
|
||
{
|
||
_signaling?.CloseConnection(connectionId);
|
||
onDisconnect?.Invoke(connectionId);
|
||
};
|
||
peer.OnDataChannelHandler += channel => onAddChannel?.Invoke(connectionId, channel); ;
|
||
peer.OnTrackEventHandler += e => onAddTransceiver?.Invoke(connectionId, e.Transceiver);
|
||
peer.SendOfferHandler += desc => _signaling?.SendOffer(connectionId, desc);
|
||
peer.SendAnswerHandler += desc => _signaling?.SendAnswer(connectionId, desc);
|
||
peer.SendCandidateHandler += candidate => _signaling?.SendCandidate(connectionId, candidate);
|
||
return peer;
|
||
}
|
||
|
||
void DeletePeerConnection(string connectionId)
|
||
{
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(connectionId, out var peer))
|
||
{
|
||
return;
|
||
}
|
||
|
||
peer.Dispose();
|
||
_mapConnectionIdAndPeer.Remove(connectionId);
|
||
}
|
||
|
||
void OnAnswer(ISignaling signaling, DescData e)
|
||
{
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(e.connectionId, out var pc))
|
||
{
|
||
RenderStreaming.Logger.Log(LogType.Warning, $"connectionId:{e.connectionId}, peerConnection not exist");
|
||
return;
|
||
}
|
||
|
||
RTCSessionDescription description = new RTCSessionDescription { type = RTCSdpType.Answer, sdp = e.sdp };
|
||
_startCoroutine(pc.OnGotDescription(description, () => onGotAnswer?.Invoke(e.connectionId, e.sdp)));
|
||
}
|
||
|
||
void OnIceCandidate(ISignaling signaling, CandidateData e)
|
||
{
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(e.connectionId, out var pc))
|
||
{
|
||
return;
|
||
}
|
||
|
||
RTCIceCandidateInit option = new RTCIceCandidateInit
|
||
{
|
||
candidate = e.candidate,
|
||
sdpMLineIndex = e.sdpMLineIndex,
|
||
sdpMid = e.sdpMid
|
||
};
|
||
pc.OnGotIceCandidate(new RTCIceCandidate(option));
|
||
}
|
||
|
||
void OnOffer(ISignaling signaling, DescData e)
|
||
{
|
||
var connectionId = e.connectionId;
|
||
if (!_mapConnectionIdAndPeer.TryGetValue(connectionId, out var pc))
|
||
{
|
||
pc = CreatePeerConnection(connectionId, e.polite);
|
||
}
|
||
|
||
// 先触发 onGotOffer,让 Handler 在 SetRemoteDescription 之前添加 transceiver
|
||
// 这样 transceiver 会在 offer 的媒体行内正确匹配
|
||
onGotOffer?.Invoke(connectionId, e.sdp);
|
||
|
||
// 然后设置远程描述,完成后自动 SendAnswer
|
||
RTCSessionDescription description = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = e.sdp };
|
||
_startCoroutine(pc.OnGotDescription(description, () =>
|
||
{
|
||
// SetRemoteDescription 成功后,自动创建并发送 answer
|
||
pc.SendAnswer();
|
||
}));
|
||
}
|
||
|
||
void OnParticipantJoinedHandler(ISignaling signaling, ParticipantEventData e)
|
||
{
|
||
onParticipantJoined?.Invoke(e.connectionId, e.participantId);
|
||
}
|
||
|
||
void OnParticipantLeftHandler(ISignaling signaling, ParticipantEventData e)
|
||
{
|
||
// 参与者离开时,销毁对应的PeerConnection
|
||
if (_mapConnectionIdAndPeer.ContainsKey(e.participantId))
|
||
{
|
||
DestroyConnection(e.participantId);
|
||
}
|
||
onParticipantLeft?.Invoke(e.connectionId, e.participantId);
|
||
}
|
||
|
||
void OnCallRequestHandler(ISignaling signaling, CallRequestData e)
|
||
{
|
||
onCallRequest?.Invoke(e.connectionId, e.data);
|
||
}
|
||
|
||
void OnMessageHandler(ISignaling signaling, OnMessageData e)
|
||
{
|
||
onMessage?.Invoke(e.connectionId, e.participantId, e.message);
|
||
}
|
||
}
|
||
}
|