Files
webRtc/Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/HostConnection.cs
2026-04-29 15:13:24 +08:00

244 lines
8.8 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.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Unity.RenderStreaming
{
/// <summary>
/// Host模式的连接处理器
/// 适用于私有模式下Unity作为Host第一个加入房间的客户端
/// 支持多个Participant连接每个Participant拥有独立的PeerConnection
///
/// 工作原理:
/// - Host通过CreateConnection(connectionId)创建房间连接
/// - 当Participant加入时服务器发送participant-joined通知
/// - WebSocketSignaling将participantId作为内部connectionId使用
/// 因此每个Participant会自动创建独立的PeerConnection
/// - 当收到Participant的offer时自动添加Stream并发送answer
/// - 当Participant离开时自动清理对应的连接资源
/// </summary>
[AddComponentMenu("Render Streaming/Host Connection")]
public class HostConnection : SignalingHandlerBase,
IOfferHandler, IAddChannelHandler, IDisconnectHandler, IDeletedConnectionHandler,
IAddReceiverHandler, IParticipantJoinedHandler, IParticipantLeftHandler,
ICallRequestHandler, IMessageHandler
{
[SerializeField] private List<Component> streams = new List<Component>();
/// <summary>
/// 当前连接的所有Participant的participantId列表
/// </summary>
private List<string> participantIds = new List<string>();
/// <summary>
/// 房间的connectionIdHost创建连接时使用的ID
/// </summary>
private string roomConnectionId;
public override IEnumerable<Component> Streams => streams;
public void AddComponent(Component component)
{
streams.Add(component);
}
public void RemoveComponent(Component component)
{
streams.Remove(component);
}
/// <summary>
/// 创建Host连接即创建房间
/// Unity作为第一个连接的客户端服务器会分配host角色(polite=false)
/// </summary>
/// <param name="connectionId">房间ID</param>
public override void CreateConnection(string connectionId)
{
roomConnectionId = connectionId;
base.CreateConnection(connectionId);
}
/// <summary>
/// 删除Host连接关闭房间
/// Host离开时服务器会自动断开所有Participant
/// </summary>
public override void DeleteConnection(string connectionId)
{
// 清理所有Participant连接
foreach (var participantId in participantIds.ToList())
{
DisconnectParticipant(participantId);
}
participantIds.Clear();
if (connectionId == roomConnectionId)
{
base.DeleteConnection(connectionId);
roomConnectionId = null;
}
else
{
base.DeleteConnection(connectionId);
}
}
public void OnDeletedConnection(SignalingEventData eventData)
{
Disconnect(eventData.connectionId);
}
public void OnDisconnect(SignalingEventData eventData)
{
Disconnect(eventData.connectionId);
}
private void Disconnect(string connectionId)
{
if (!participantIds.Contains(connectionId) && connectionId != roomConnectionId)
return;
participantIds.Remove(connectionId);
foreach (var sender in streams.OfType<IStreamSender>())
{
RemoveSender(connectionId, sender);
}
foreach (var receiver in streams.OfType<IStreamReceiver>())
{
RemoveReceiver(connectionId, receiver);
}
foreach (var channel in streams.OfType<IDataChannel>().Where(c => c.ConnectionId == connectionId))
{
RemoveChannel(connectionId, channel);
}
}
/// <summary>
/// 断开指定Participant的连接资源
/// </summary>
private void DisconnectParticipant(string participantId)
{
foreach (var sender in streams.OfType<IStreamSender>())
{
RemoveSender(participantId, sender);
}
foreach (var receiver in streams.OfType<IStreamReceiver>())
{
RemoveReceiver(participantId, receiver);
}
foreach (var channel in streams.OfType<IDataChannel>().Where(c => c.ConnectionId == participantId))
{
RemoveChannel(participantId, channel);
}
}
/// <summary>
/// 收到Participant的offer时触发
/// WebSocketSignaling已经将participantId作为内部connectionId
/// 所以每个Participant的offer会自动创建独立的PeerConnection
/// </summary>
public void OnOffer(SignalingEventData data)
{
// 记录新的Participant连接
if (!participantIds.Contains(data.connectionId))
{
participantIds.Add(data.connectionId);
Debug.Log($"[HostConnection] Participant offer received: {data.connectionId}");
}
// 为该Participant添加所有Stream
foreach (var source in streams.OfType<IStreamSender>())
{
AddSender(data.connectionId, source);
}
foreach (var channel in streams.OfType<IDataChannel>().Where(c => c.IsLocal))
{
AddChannel(data.connectionId, channel);
}
// 发送answer给该Participant
SendAnswer(data.connectionId);
}
/// <summary>
/// 收到远程Track时触发
/// </summary>
public void OnAddReceiver(SignalingEventData data)
{
var track = data.transceiver.Receiver.Track;
IStreamReceiver receiver = GetReceiver(track.Kind);
SetReceiver(data.connectionId, receiver, data.transceiver);
}
/// <summary>
/// 收到远程DataChannel时触发
/// </summary>
public void OnAddChannel(SignalingEventData data)
{
var channel = streams.OfType<IDataChannel>().
FirstOrDefault(r => !r.IsConnected && !r.IsLocal);
channel?.SetChannel(data.connectionId, data.channel);
}
/// <summary>
/// 新Participant加入房间通知
/// 在私有模式下服务器会通知Host有新的Participant加入
/// 此时不做额外处理等收到Participant的offer时再建立连接
/// </summary>
public void OnParticipantJoined(SignalingEventData eventData)
{
Debug.Log($"[HostConnection] Participant joined: connectionId={eventData.connectionId}, participantId={eventData.participantId}");
}
/// <summary>
/// Participant离开房间通知
/// 清理该Participant的连接资源
/// </summary>
public void OnParticipantLeft(SignalingEventData eventData)
{
string participantId = eventData.participantId;
if (!string.IsNullOrEmpty(participantId))
{
Debug.Log($"[HostConnection] Participant left: connectionId={eventData.connectionId}, participantId={participantId}");
DisconnectParticipant(participantId);
participantIds.Remove(participantId);
}
}
/// <summary>
/// 收到呼叫请求
/// </summary>
public void OnCallRequest(SignalingEventData eventData)
{
Debug.Log($"[HostConnection] Call request from: {eventData.connectionId}, data: {eventData.message}");
}
/// <summary>
/// 收到自定义消息
/// </summary>
public void OnMessage(SignalingEventData eventData)
{
Debug.Log($"[HostConnection] Message from: {eventData.connectionId}, participantId: {eventData.participantId}, message: {eventData.message}");
}
/// <summary>
/// 获取当前连接的Participant数量
/// </summary>
public int ParticipantCount => participantIds.Count;
/// <summary>
/// 获取所有Participant的ID列表
/// </summary>
public IReadOnlyList<string> ParticipantIds => participantIds.AsReadOnly();
IStreamReceiver GetReceiver(WebRTC.TrackKind kind)
{
if (kind == WebRTC.TrackKind.Audio)
return streams.OfType<AudioStreamReceiver>().FirstOrDefault();
if (kind == WebRTC.TrackKind.Video)
return streams.OfType<VideoStreamReceiver>().FirstOrDefault();
throw new System.ArgumentException();
}
}
}