Files
webRtc/Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManagerInternal.cs
2026-04-30 23:27:34 +08:00

498 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 += () =>
{
// PeerConnection失败时不自动发送disconnect避免重复断开通知。
// 主动断开由 Handler 的 DeleteConnection 触发,远端断开由服务器的 disconnect 消息触发。
// 这里只触发 onDisconnect 事件通知 Handler 进行资源清理。
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不触发onDeletedConnection事件
// 因为onParticipantLeft已经会通知Handler进行资源清理避免重复通知
if (_mapConnectionIdAndPeer.ContainsKey(e.participantId))
{
DeletePeerConnection(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);
}
}
}