完成
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Stary.Evo;
|
||||
using Script;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.UIFarme;
|
||||
|
||||
namespace RenderStreaming
|
||||
@@ -12,6 +13,7 @@ namespace RenderStreaming
|
||||
RegisterSystem<IPanelSystem>(new PanelSystem(new ResourcesAssetLoader()));
|
||||
RegisterSystem<IGlobalConfigSystem>(new GlobalConfigSystem());
|
||||
RegisterSystem<ITimerSystem>(new TimerSystem());
|
||||
RegisterSystem<IRenderStreamingSystem>(new RenderStreamingSystem());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Script
|
||||
private Text _confirmBtnTimeText;
|
||||
private Button _confirmButton;
|
||||
private bool _isCountingDown;
|
||||
private MeetingContacts _meetingContacts;
|
||||
private MeetingContactsController _meetingContactsController;
|
||||
private Transform _messageText;
|
||||
private Transform _nameText;
|
||||
private MainPanel.UsersItem _usersItem;
|
||||
@@ -33,10 +33,10 @@ namespace Script
|
||||
return MainArchitecture.Interface;
|
||||
}
|
||||
|
||||
public void SetData(MainPanel.UsersItem item, MeetingContacts meetingContactsController)
|
||||
public void SetData(MainPanel.UsersItem item, MeetingContactsController meetingContactsControllerController)
|
||||
{
|
||||
_usersItem = item;
|
||||
_meetingContacts = meetingContactsController;
|
||||
_meetingContactsController = meetingContactsControllerController;
|
||||
|
||||
_background = transform.Find("headBackground/image").GetComponent<Image>();
|
||||
_backgroundName = transform.Find("headBackground/Name").GetComponent<Text>();
|
||||
@@ -120,9 +120,9 @@ namespace Script
|
||||
Debug.Log($"点击了联系人: {_usersItem.name}");
|
||||
// 这里可以添加点击联系人后的逻辑,比如打开聊天窗口
|
||||
// 或者发送邀请请求等
|
||||
_meetingContacts.ClickContactEntry(_background.GetComponent<Image>().sprite,
|
||||
_meetingContactsController.ClickContactEntry(_background.GetComponent<Image>().sprite,
|
||||
_backgroundName.GetComponent<Text>().text, _usersItem);
|
||||
_meetingContacts.CurrentEntry = this;
|
||||
_meetingContactsController.CurrentEntry = this;
|
||||
}
|
||||
|
||||
public void StartCountdown()
|
||||
|
||||
@@ -28,17 +28,17 @@ namespace Script
|
||||
/// <summary>
|
||||
/// 会议联系人类和面板
|
||||
/// </summary>
|
||||
private MeetingContacts _meetingContacts;
|
||||
private MeetingContactsController _meetingContactsController;
|
||||
|
||||
/// <summary>
|
||||
/// 会议参与者列表和面板
|
||||
/// </summary>
|
||||
private MeetingInfoList _meetingInfoList;
|
||||
private MeetingInfoListController _meetingInfoListController;
|
||||
|
||||
/// <summary>
|
||||
/// 会议聊天面板
|
||||
/// </summary>
|
||||
private MeetingChat _meetingChat;
|
||||
private MeetingChatController _meetingChatController;
|
||||
|
||||
/// <summary>
|
||||
/// 房间人数
|
||||
@@ -80,18 +80,18 @@ namespace Script
|
||||
_contactsTog = panelGo.transform.Find("Tabs/Contacts").GetComponent<Toggle>();
|
||||
_meetingOrganizersTog = panelGo.transform.Find("Tabs/MeetingOrganizers").GetComponent<Toggle>();
|
||||
|
||||
_meetingContacts = new MeetingContacts();
|
||||
_meetingContacts.Initialize(panelGo.transform.Find("MeetingContacts").gameObject);
|
||||
_meetingInfoList = new MeetingInfoList();
|
||||
_meetingInfoList.Initialize(panelGo.transform.Find("MeetingInfoList").gameObject, this);
|
||||
_meetingChat = new MeetingChat();
|
||||
_meetingChat.Initialize(panelGo.transform.Find("MeetingChat").gameObject, this);
|
||||
_meetingContactsController = new MeetingContactsController();
|
||||
_meetingContactsController.Initialize(panelGo.transform.Find("MeetingContacts").gameObject);
|
||||
_meetingInfoListController = new MeetingInfoListController();
|
||||
_meetingInfoListController.Initialize(panelGo.transform.Find("MeetingInfoList").gameObject, this);
|
||||
_meetingChatController = new MeetingChatController();
|
||||
_meetingChatController.Initialize(panelGo.transform.Find("MeetingChat").gameObject, this);
|
||||
}
|
||||
|
||||
public override void OnEnter(Action complete = null)
|
||||
{
|
||||
base.OnEnter(complete);
|
||||
_meetingInfoList.OnEnter();
|
||||
_meetingInfoListController.OnEnter();
|
||||
_titleText.text = this.GetSystem<IGlobalConfigSystem>().GetConnectionName();
|
||||
this.GetSystem<IGlobalConfigSystem>().StartConnectionTime(_timeText);
|
||||
_idText.text = this.GetSystem<IGlobalConfigSystem>().GetConnectionId();
|
||||
@@ -107,7 +107,7 @@ namespace Script
|
||||
public override void OnExit(float delay = 0)
|
||||
{
|
||||
base.OnExit(delay);
|
||||
_meetingInfoList.OnExit();
|
||||
_meetingInfoListController.OnExit();
|
||||
_arrowLeft.onClick.RemoveListener(OnArrowLeftClick);
|
||||
_chatTog.onValueChanged.RemoveListener(OnChatTog);
|
||||
_contactsTog.onValueChanged.RemoveListener(OnContactsTog);
|
||||
@@ -131,7 +131,7 @@ namespace Script
|
||||
}
|
||||
|
||||
// 更新会议聊天面板人数
|
||||
_meetingChat.OnUserCountChangedEvent?.Invoke(obj.Count);
|
||||
_meetingChatController.OnUserCountChangedEvent?.Invoke(obj.Count);
|
||||
}
|
||||
|
||||
private void OnUsersChangedDisconnected(string obj)
|
||||
@@ -152,26 +152,26 @@ namespace Script
|
||||
}
|
||||
|
||||
_roomPeopleNumText.text = _userMap.Count.ToString();
|
||||
_meetingChat.OnUserCountChangedEvent?.Invoke(_userMap.Count);
|
||||
_meetingChatController.OnUserCountChangedEvent?.Invoke(_userMap.Count);
|
||||
}
|
||||
|
||||
private void OnMeetingOrganizersTog(bool value)
|
||||
{
|
||||
_meetingInfoList.PanelGo.SetActive(value);
|
||||
_meetingInfoListController.PanelGo.SetActive(value);
|
||||
}
|
||||
|
||||
private void OnContactsTog(bool value)
|
||||
{
|
||||
_meetingContacts.PanelGo.SetActive(value);
|
||||
_meetingContactsController.PanelGo.SetActive(value);
|
||||
if (value)
|
||||
_meetingContacts.OnEnter();
|
||||
_meetingContactsController.OnEnter();
|
||||
}
|
||||
|
||||
private void OnChatTog(bool value)
|
||||
{
|
||||
_meetingChat.PanelGo.SetActive(value);
|
||||
_meetingChatController.PanelGo.SetActive(value);
|
||||
if (value)
|
||||
_meetingChat.OnEnter();
|
||||
_meetingChatController.OnEnter();
|
||||
}
|
||||
|
||||
private void OnArrowLeftClick()
|
||||
@@ -181,8 +181,8 @@ namespace Script
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
_meetingChat.OnDestroy();
|
||||
_meetingContacts.OnDestroy();
|
||||
_meetingChatController.OnDestroy();
|
||||
_meetingContactsController.OnDestroy();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using RenderStreaming;
|
||||
using Script.Util;
|
||||
@@ -10,7 +11,7 @@ using UnityEngine.UI;
|
||||
|
||||
namespace Script
|
||||
{
|
||||
public class MeetingChat : IController
|
||||
public class MeetingChatController : IController
|
||||
{
|
||||
public GameObject PanelGo;
|
||||
private MainPanel _mainPanel;
|
||||
@@ -62,6 +63,9 @@ namespace Script
|
||||
_objectLeftPool.AllRelease();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送聊天信息
|
||||
/// </summary>
|
||||
private void OnSendButtonClick()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_messageInput.text)) return;
|
||||
@@ -97,6 +101,9 @@ namespace Script
|
||||
SignalingMessageHelper.SendMessage(JsonConvert.SerializeObject(send));
|
||||
_messageInput.text = "";
|
||||
chatData.isSelf = !chatData.isSelf;
|
||||
var utc = DateTimeOffset.Parse(chatData.timestamp, CultureInfo.InvariantCulture);
|
||||
var local = utc.ToLocalTime().ToString("HH:mm");
|
||||
chatData.timestamp = local;
|
||||
_chatDatas.Add(chatData);
|
||||
var entry = _objectRightPool.Get();
|
||||
SetMessageEntry(chatData, entry);
|
||||
@@ -119,7 +126,7 @@ namespace Script
|
||||
var name = entry.transform.Find("name");
|
||||
var message = entry.transform.Find("message");
|
||||
var sprite = entry.transform.Find("sprite");
|
||||
|
||||
var time = entry.transform.Find("message/messagebg/time");
|
||||
if (string.IsNullOrEmpty(data.senderAvatar))
|
||||
{
|
||||
var randomColor = WebRTCUtil.GetRandomColor();
|
||||
@@ -148,6 +155,11 @@ namespace Script
|
||||
new Rect(0, 0, texture.width, texture.height),
|
||||
Vector2.one);
|
||||
}
|
||||
|
||||
var utc = DateTimeOffset.Parse(data.timestamp, CultureInfo.InvariantCulture);
|
||||
var local = utc.ToLocalTime().ToString("HH:mm");
|
||||
data.timestamp = local;
|
||||
time.GetComponent<Text>().text = data.timestamp;
|
||||
}
|
||||
|
||||
private void OnUserCountChanged(int obj)
|
||||
@@ -9,7 +9,7 @@ using UnityEngine.UI;
|
||||
|
||||
namespace Script
|
||||
{
|
||||
public class MeetingContacts : IController
|
||||
public class MeetingContactsController : IController
|
||||
{
|
||||
/// <summary>
|
||||
/// 联系人列表项字典
|
||||
@@ -1,14 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using RenderStreaming;
|
||||
using Script.Util;
|
||||
using Stary.Evo;
|
||||
using Stary.Evo.UIFarme;
|
||||
using Unity.RenderStreaming;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Script
|
||||
{
|
||||
public class MeetingInfoList : IController
|
||||
public class MeetingInfoListController : IController
|
||||
{
|
||||
private MainPanel _mainPanel;
|
||||
public GameObject PanelGo;
|
||||
@@ -29,6 +31,15 @@ namespace Script
|
||||
|
||||
private GameObjectPool _objectPool;
|
||||
|
||||
|
||||
private Toggle _micTog;
|
||||
private Toggle _videoTog;
|
||||
private Toggle _recordTog;
|
||||
private Button _hangUpTog;
|
||||
|
||||
private AudioStreamSender _audioStreamSender;
|
||||
private VideoStreamSender _videoStreamSender;
|
||||
|
||||
public void Initialize(GameObject panelGo, MainPanel mainPanel)
|
||||
{
|
||||
PanelGo = panelGo;
|
||||
@@ -37,18 +48,88 @@ namespace Script
|
||||
_content = panelGo.transform.Find("MeetingGrid/Viewport/Content");
|
||||
_meetingNum = panelGo.transform.Find("MeetingNum/Num").GetComponent<Text>();
|
||||
_objectPool = PanelGo.transform.Find("MeetingGrid/Pool").GetComponent<GameObjectPool>();
|
||||
_micTog = PanelGo.transform.Find("menuBar/mic").GetComponent<Toggle>();
|
||||
_videoTog = PanelGo.transform.Find("menuBar/video").GetComponent<Toggle>();
|
||||
_recordTog = PanelGo.transform.Find("menuBar/record").GetComponent<Toggle>();
|
||||
_hangUpTog = PanelGo.transform.Find("menuBar/hangUp").GetComponent<Button>();
|
||||
_audioStreamSender = GameObject.Find("RenderStreaming").GetComponent<AudioStreamSender>();
|
||||
_videoStreamSender = GameObject.Find("RenderStreaming").GetComponent<VideoStreamSender>();
|
||||
}
|
||||
|
||||
public void OnEnter()
|
||||
{
|
||||
LoadUsers();
|
||||
_micTog.onValueChanged.AddListener(OnMicTogValueChanged);
|
||||
_videoTog.onValueChanged.AddListener(OnVideoTogValueChanged);
|
||||
_recordTog.onValueChanged.AddListener(OnRecordTogValueChanged);
|
||||
_hangUpTog.onClick.AddListener(OnHangUpTogValueChanged);
|
||||
|
||||
GameObject.FindObjectOfType<MessageChannel>().OnMediaStateChangeReceived += OnMediaStateChangeReceived;
|
||||
GameObject.FindObjectOfType<MessageChannel>().OnUserInfoMessageReceived += OnUserInfoMessageReceived;
|
||||
GameObject.FindObjectOfType<HostConnection>().OnParticipantDisconnected += OnParticipantDisconnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
private void OnMicTogValueChanged(bool value)
|
||||
{
|
||||
var message = new
|
||||
{
|
||||
type = "on-message",
|
||||
data = new
|
||||
{
|
||||
connectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId(),
|
||||
message = new
|
||||
{
|
||||
type = "media-state-changed",
|
||||
data = new
|
||||
{
|
||||
userId = this.GetSystem<IGlobalConfigSystem>().GetUserId(),
|
||||
audio = value,
|
||||
video = _videoTog.isOn
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
_audioStreamSender.enabled = value;
|
||||
SignalingMessageHelper.SendMessage(JsonConvert.SerializeObject(message));
|
||||
}
|
||||
|
||||
private void OnVideoTogValueChanged(bool value)
|
||||
{
|
||||
var message = new
|
||||
{
|
||||
type = "on-message",
|
||||
data = new
|
||||
{
|
||||
connectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId(),
|
||||
message = new
|
||||
{
|
||||
type = "media-state-changed",
|
||||
data = new
|
||||
{
|
||||
userId = this.GetSystem<IGlobalConfigSystem>().GetUserId(),
|
||||
audio = _micTog.isOn,
|
||||
video = value
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
_videoStreamSender.enabled = value;
|
||||
SignalingMessageHelper.SendMessage(JsonConvert.SerializeObject(message));
|
||||
}
|
||||
|
||||
private void OnRecordTogValueChanged(bool value)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnHangUpTogValueChanged()
|
||||
{
|
||||
this.GetSystem<IRenderStreamingSystem>().HangUp();
|
||||
this.GetSystem<IRenderStreamingSystem>().SetLocalVideoImage(null);
|
||||
this.GetSystem<IPanelSystem>().PopQueue<MainPanel>();
|
||||
this.GetSystem<IPanelSystem>().PushQueue<StartPanel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当有用户断开连接时调用
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
@@ -225,6 +306,9 @@ namespace Script
|
||||
GameObject.FindObjectOfType<MessageChannel>().OnMediaStateChangeReceived -= OnMediaStateChangeReceived;
|
||||
GameObject.FindObjectOfType<MessageChannel>().OnUserInfoMessageReceived -= OnUserInfoMessageReceived;
|
||||
GameObject.FindObjectOfType<HostConnection>().OnParticipantDisconnected -= OnParticipantDisconnected;
|
||||
|
||||
foreach (var info in _meetingList.Values) _objectPool.Release(info.participant);
|
||||
_meetingList.Clear();
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
@@ -1,11 +0,0 @@
|
||||
using RenderStreaming;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
|
||||
public class MeetingChatController : MonoBehaviour, IController
|
||||
{
|
||||
public IArchitecture GetArchitecture()
|
||||
{
|
||||
return MainArchitecture.Interface;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd96585787c8a4d42b3ad979cf3e31e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using RenderStreaming;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
|
||||
public class MeetingContactsController : MonoBehaviour ,IController
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IArchitecture GetArchitecture()
|
||||
{
|
||||
return MainArchitecture.Interface;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19625967ea163c04095b4ef20880146a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using RenderStreaming;
|
||||
using Stary.Evo;
|
||||
using UnityEngine;
|
||||
|
||||
public class MeetingInfoList : MonoBehaviour, IController
|
||||
{
|
||||
public Dictionary<GameObject, MeetingInfo> Meetings;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
Meetings = new Dictionary<GameObject, MeetingInfo>();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
}
|
||||
|
||||
public IArchitecture GetArchitecture()
|
||||
{
|
||||
return MainArchitecture.Interface;
|
||||
}
|
||||
|
||||
public class MeetingInfo
|
||||
{
|
||||
public Texture2D Icon;
|
||||
public string Name;
|
||||
public string Message;
|
||||
public bool Ismic;
|
||||
public bool Iscam;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e57592024ba7ab4ab283f87f98a49e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
242
Assets/Script/RenderStreamingSystem.cs
Normal file
242
Assets/Script/RenderStreamingSystem.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using System.Collections.Generic;
|
||||
using Stary.Evo;
|
||||
using Unity.RenderStreaming;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Script
|
||||
{
|
||||
public interface IRenderStreamingSystem : ISystem
|
||||
{
|
||||
void SetUp();
|
||||
void HangUp();
|
||||
|
||||
void SetLocalVideoImage(RawImage localVideoImage);
|
||||
}
|
||||
|
||||
public class RenderStreamingSystem : AbstractSystem, IRenderStreamingSystem
|
||||
{
|
||||
[Header("核心组件")] private RenderStreamingSettings settings;
|
||||
|
||||
/// <summary>
|
||||
/// Host连接
|
||||
/// </summary>
|
||||
private HostConnection hostConnection;
|
||||
|
||||
[Header("Host本地视频")] private VideoStreamSender videoStreamSender;
|
||||
|
||||
/// <summary>
|
||||
/// 麦克风流发送器
|
||||
/// </summary>
|
||||
private AudioStreamSender microphoneStreamer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 每个Participant的UI信息
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, ParticipantUI> participantUIs = new();
|
||||
|
||||
|
||||
[Header("Participant视频容器")] private Transform participantVideoContainer;
|
||||
|
||||
/// <summary>
|
||||
/// 渲染流管理
|
||||
/// </summary>
|
||||
private SignalingManager _signalingManager;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 本地视频显示
|
||||
/// </summary>
|
||||
private RawImage localVideoImage;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
var renderStreaming = GameObject.Find("RenderStreaming").transform;
|
||||
_signalingManager = renderStreaming.GetComponent<SignalingManager>();
|
||||
hostConnection = renderStreaming.GetComponent<HostConnection>();
|
||||
videoStreamSender = hostConnection.GetComponent<VideoStreamSender>();
|
||||
microphoneStreamer = hostConnection.GetComponent<AudioStreamSender>();
|
||||
if (settings == null)
|
||||
settings = new RenderStreamingSettings();
|
||||
if (settings != null)
|
||||
{
|
||||
videoStreamSender.width = (uint)settings.StreamSize.x;
|
||||
videoStreamSender.height = (uint)settings.StreamSize.y;
|
||||
}
|
||||
|
||||
if (_signalingManager.runOnAwake)
|
||||
return;
|
||||
if (settings != null)
|
||||
_signalingManager.useDefaultSettings = settings.UseDefaultSettings;
|
||||
if (settings?.SignalingSettings != null)
|
||||
_signalingManager.SetSignalingSettings(settings.SignalingSettings);
|
||||
if (settings != null)
|
||||
videoStreamSender.SetCodec(settings.SenderVideoCodec);
|
||||
_signalingManager.Run();
|
||||
|
||||
|
||||
if (hostConnection != null)
|
||||
{
|
||||
hostConnection.OnParticipantConnected += HandleParticipantConnected;
|
||||
hostConnection.OnParticipantDisconnected += HandleParticipantDisconnected;
|
||||
}
|
||||
|
||||
// 本地视频:Sender启动后显示
|
||||
videoStreamSender.OnStartedStream += OnStartedStream;
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (hostConnection != null)
|
||||
{
|
||||
hostConnection.OnParticipantConnected -= HandleParticipantConnected;
|
||||
hostConnection.OnParticipantDisconnected -= HandleParticipantDisconnected;
|
||||
}
|
||||
|
||||
videoStreamSender.OnStartedStream -= OnStartedStream;
|
||||
}
|
||||
|
||||
private void OnStartedStream(string id)
|
||||
{
|
||||
if (videoStreamSender.sourceWebCamTexture != null && localVideoImage != null)
|
||||
localVideoImage.texture = videoStreamSender.sourceWebCamTexture;
|
||||
}
|
||||
|
||||
public void SetUp()
|
||||
{
|
||||
videoStreamSender.enabled = true;
|
||||
microphoneStreamer.enabled = true;
|
||||
hostConnection.RoomConnectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId();
|
||||
hostConnection.CreateConnection(hostConnection.RoomConnectionId);
|
||||
}
|
||||
|
||||
public void HangUp()
|
||||
{
|
||||
videoStreamSender.enabled = false;
|
||||
microphoneStreamer.enabled = false;
|
||||
hostConnection.DeleteConnection(hostConnection.RoomConnectionId);
|
||||
|
||||
// 清理所有Participant UI
|
||||
foreach (var ui in participantUIs.Values)
|
||||
if (ui.root != null)
|
||||
GameObject.Destroy(ui.root);
|
||||
|
||||
participantUIs.Clear();
|
||||
if (localVideoImage != null)
|
||||
localVideoImage.texture = null;
|
||||
}
|
||||
|
||||
public void SetLocalVideoImage(RawImage localVideoImage)
|
||||
{
|
||||
this.localVideoImage = localVideoImage;
|
||||
}
|
||||
|
||||
#region 开启相关
|
||||
|
||||
/// <summary>
|
||||
/// 新Participant连接成功回调
|
||||
/// 此时Participant的独立Receiver已创建,可以绑定视频显示
|
||||
/// </summary>
|
||||
private void HandleParticipantConnected(ParticipantStreams ps)
|
||||
{
|
||||
// 创建Participant UI
|
||||
var ui = CreateParticipantUI(ps.participantId);
|
||||
participantUIs[ps.participantId] = ui;
|
||||
|
||||
// 绑定视频:当Receiver收到纹理时更新RawImage
|
||||
ps.videoReceiver.OnUpdateReceiveTexture += texture =>
|
||||
{
|
||||
ui.videoImage.color = Color.white;
|
||||
// 防止纹理为null时导致RawImage闪黑(重协商/track切换时可能短暂为null)
|
||||
if (ui.videoImage != null && texture != null)
|
||||
ui.videoImage.texture = texture;
|
||||
};
|
||||
|
||||
// 绑定音频:AudioSource已在HostConnection中配置
|
||||
ps.audioReceiver.OnUpdateReceiveAudioSource += source =>
|
||||
{
|
||||
if (source != null && !source.isPlaying)
|
||||
{
|
||||
source.loop = true;
|
||||
source.Play();
|
||||
}
|
||||
};
|
||||
|
||||
Debug.Log($"[MultiParticipantHost] Participant UI created: {ps.participantId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动态创建单个Participant的视频显示UI
|
||||
/// 结构: [NameLabel] + [RawImage(视频画面)]
|
||||
/// </summary>
|
||||
private ParticipantUI CreateParticipantUI(string participantId)
|
||||
{
|
||||
var ui = new ParticipantUI();
|
||||
|
||||
// 根节点
|
||||
ui.root = new GameObject($"ParticipantUI_{participantId}");
|
||||
ui.root.transform.SetParent(participantVideoContainer, false);
|
||||
// 添加VerticalLayoutGroup使内容垂直排列
|
||||
var vlg = ui.root.AddComponent<VerticalLayoutGroup>();
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childControlHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.spacing = 2;
|
||||
ui.root.transform.GetComponent<RectTransform>().anchorMin = Vector2.zero;
|
||||
ui.root.transform.GetComponent<RectTransform>().anchorMax = Vector2.one;
|
||||
// 名称标签
|
||||
var labelObj = new GameObject("NameLabel");
|
||||
labelObj.transform.SetParent(ui.root.transform, false);
|
||||
ui.nameLabel = labelObj.AddComponent<Text>();
|
||||
ui.nameLabel.text = $"Participant: {participantId}";
|
||||
ui.nameLabel.fontSize = 14;
|
||||
ui.nameLabel.color = Color.white;
|
||||
ui.nameLabel.alignment = TextAnchor.MiddleCenter;
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.preferredHeight = 20;
|
||||
|
||||
// 视频画面
|
||||
var imageObj = new GameObject("VideoImage");
|
||||
imageObj.transform.SetParent(ui.root.transform, false);
|
||||
ui.videoImage = imageObj.AddComponent<RawImage>();
|
||||
ui.videoImage.color = Color.black;
|
||||
var imageLayout = imageObj.AddComponent<LayoutElement>();
|
||||
imageLayout.preferredHeight = 200;
|
||||
|
||||
// AspectRatioFitter保持视频比例
|
||||
var aspectRatio = imageObj.AddComponent<AspectRatioFitter>();
|
||||
aspectRatio.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
|
||||
aspectRatio.aspectRatio = 16f / 9f;
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Participant断开连接回调
|
||||
/// 销毁其UI
|
||||
/// </summary>
|
||||
private void HandleParticipantDisconnected(string participantId)
|
||||
{
|
||||
if (participantUIs.TryGetValue(participantId, out var ui))
|
||||
{
|
||||
if (ui.root != null)
|
||||
GameObject.Destroy(ui.root);
|
||||
participantUIs.Remove(participantId);
|
||||
}
|
||||
|
||||
Debug.Log($"[MultiParticipantHost] Participant UI removed: {participantId}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ParticipantUI
|
||||
{
|
||||
public Text nameLabel;
|
||||
public GameObject root;
|
||||
public RawImage videoImage;
|
||||
}
|
||||
}
|
||||
3
Assets/Script/RenderStreamingSystem.cs.meta
Normal file
3
Assets/Script/RenderStreamingSystem.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a92be42fdf2e46899bd9e52daf0ad589
|
||||
timeCreated: 1779630178
|
||||
@@ -13,11 +13,6 @@ namespace Unity.RenderStreaming
|
||||
{
|
||||
public class StartPanel : BasePanel
|
||||
{
|
||||
/// <summary>
|
||||
/// 每个Participant的UI信息
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, ParticipantUI> participantUIs = new();
|
||||
|
||||
/// <summary>
|
||||
/// 返回按钮
|
||||
/// </summary>
|
||||
@@ -60,30 +55,6 @@ namespace Unity.RenderStreaming
|
||||
/// </summary>
|
||||
private Dropdown _timeDropdown;
|
||||
|
||||
/// <summary>
|
||||
/// Host连接
|
||||
/// </summary>
|
||||
private HostConnection hostConnection;
|
||||
|
||||
/// <summary>
|
||||
/// 本地视频显示
|
||||
/// </summary>
|
||||
private RawImage localVideoImage;
|
||||
|
||||
/// <summary>
|
||||
/// 麦克风流发送器
|
||||
/// </summary>
|
||||
private AudioStreamSender microphoneStreamer;
|
||||
|
||||
[Header("Participant视频容器")] private Transform participantVideoContainer;
|
||||
|
||||
/// <summary>
|
||||
/// 渲染流管理
|
||||
/// </summary>
|
||||
private SignalingManager renderStreaming;
|
||||
|
||||
[Header("核心组件")] private RenderStreamingSettings settings;
|
||||
[Header("Host本地视频")] private VideoStreamSender videoStreamSender;
|
||||
|
||||
public override UITweenType TweenType => UITweenType.Fade;
|
||||
|
||||
@@ -109,26 +80,6 @@ namespace Unity.RenderStreaming
|
||||
|
||||
_profileSpriteIndex = 0;
|
||||
OnClickProfilePhoto();
|
||||
|
||||
renderStreaming = GameObject.FindObjectOfType<SignalingManager>();
|
||||
hostConnection = GameObject.FindObjectOfType<HostConnection>();
|
||||
videoStreamSender = hostConnection.GetComponent<VideoStreamSender>();
|
||||
microphoneStreamer = hostConnection.GetComponent<AudioStreamSender>();
|
||||
if (settings == null)
|
||||
settings = new RenderStreamingSettings();
|
||||
if (settings != null)
|
||||
{
|
||||
videoStreamSender.width = (uint)settings.StreamSize.x;
|
||||
videoStreamSender.height = (uint)settings.StreamSize.y;
|
||||
}
|
||||
|
||||
if (renderStreaming.runOnAwake)
|
||||
return;
|
||||
if (settings != null)
|
||||
renderStreaming.useDefaultSettings = settings.UseDefaultSettings;
|
||||
if (settings?.SignalingSettings != null)
|
||||
renderStreaming.SetSignalingSettings(settings.SignalingSettings);
|
||||
renderStreaming.Run();
|
||||
}
|
||||
|
||||
public override void OnEnter(Action complete = null)
|
||||
@@ -139,14 +90,6 @@ namespace Unity.RenderStreaming
|
||||
_randomMeetingId.onClick.AddListener(OnClickRandomMeetingId);
|
||||
|
||||
_startButton.onClick.AddListener(OnClickStartButton);
|
||||
if (hostConnection != null)
|
||||
{
|
||||
hostConnection.OnParticipantConnected += HandleParticipantConnected;
|
||||
hostConnection.OnParticipantDisconnected += HandleParticipantDisconnected;
|
||||
}
|
||||
|
||||
// 本地视频:Sender启动后显示
|
||||
videoStreamSender.OnStartedStream += OnStartedStream;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,13 +100,6 @@ namespace Unity.RenderStreaming
|
||||
_profilePhoto.onClick.RemoveListener(OnClickProfilePhoto);
|
||||
_randomMeetingId.onClick.RemoveListener(OnClickRandomMeetingId);
|
||||
_startButton.onClick.RemoveListener(OnClickStartButton);
|
||||
if (hostConnection != null)
|
||||
{
|
||||
hostConnection.OnParticipantConnected -= HandleParticipantConnected;
|
||||
hostConnection.OnParticipantDisconnected -= HandleParticipantDisconnected;
|
||||
}
|
||||
|
||||
videoStreamSender.OnStartedStream -= OnStartedStream;
|
||||
}
|
||||
|
||||
private void OnClickProfilePhoto()
|
||||
@@ -196,7 +132,7 @@ namespace Unity.RenderStreaming
|
||||
this.GetSystem<IGlobalConfigSystem>().SetUserId(OnClickRandomUserId());
|
||||
this.GetSystem<IGlobalConfigSystem>().SetConnectionTimeType(_timeDropdown.value);
|
||||
this.GetSystem<IGlobalConfigSystem>().SetConnectionId(_meetingId.text);
|
||||
hostConnection.RoomConnectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId();
|
||||
|
||||
this.GetSystem<IGlobalConfigSystem>().SetConnectionName(_meetingNameInput.text);
|
||||
if (!SignalingMessageHelper.IsReady())
|
||||
{
|
||||
@@ -216,146 +152,9 @@ namespace Unity.RenderStreaming
|
||||
}
|
||||
};
|
||||
SignalingMessageHelper.SendMessage(JsonConvert.SerializeObject(userInfo));
|
||||
SetUp();
|
||||
this.GetSystem<IRenderStreamingSystem>().SetUp();
|
||||
PanelSystem.PopQueue<StartPanel>();
|
||||
PanelSystem.PushQueue<MainPanel>();
|
||||
}
|
||||
|
||||
private void OnStartedStream(string id)
|
||||
{
|
||||
if (videoStreamSender.sourceWebCamTexture != null)
|
||||
localVideoImage.texture = videoStreamSender.sourceWebCamTexture;
|
||||
}
|
||||
|
||||
private class ParticipantUI
|
||||
{
|
||||
public Text nameLabel;
|
||||
public GameObject root;
|
||||
public RawImage videoImage;
|
||||
}
|
||||
|
||||
|
||||
#region 开启相关
|
||||
|
||||
private void SetUp()
|
||||
{
|
||||
videoStreamSender.enabled = true;
|
||||
|
||||
if (settings != null)
|
||||
videoStreamSender.SetCodec(settings.SenderVideoCodec);
|
||||
|
||||
hostConnection.CreateConnection(hostConnection.RoomConnectionId);
|
||||
}
|
||||
|
||||
private void HangUp()
|
||||
{
|
||||
hostConnection.DeleteConnection(hostConnection.RoomConnectionId);
|
||||
|
||||
// 清理所有Participant UI
|
||||
foreach (var ui in participantUIs.Values)
|
||||
if (ui.root != null)
|
||||
GameObject.Destroy(ui.root);
|
||||
|
||||
participantUIs.Clear();
|
||||
|
||||
localVideoImage.texture = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新Participant连接成功回调
|
||||
/// 此时Participant的独立Receiver已创建,可以绑定视频显示
|
||||
/// </summary>
|
||||
private void HandleParticipantConnected(ParticipantStreams ps)
|
||||
{
|
||||
// 创建Participant UI
|
||||
var ui = CreateParticipantUI(ps.participantId);
|
||||
participantUIs[ps.participantId] = ui;
|
||||
|
||||
// 绑定视频:当Receiver收到纹理时更新RawImage
|
||||
ps.videoReceiver.OnUpdateReceiveTexture += texture =>
|
||||
{
|
||||
ui.videoImage.color = Color.white;
|
||||
// 防止纹理为null时导致RawImage闪黑(重协商/track切换时可能短暂为null)
|
||||
if (ui.videoImage != null && texture != null)
|
||||
ui.videoImage.texture = texture;
|
||||
};
|
||||
|
||||
// 绑定音频:AudioSource已在HostConnection中配置
|
||||
ps.audioReceiver.OnUpdateReceiveAudioSource += source =>
|
||||
{
|
||||
if (source != null && !source.isPlaying)
|
||||
{
|
||||
source.loop = true;
|
||||
source.Play();
|
||||
}
|
||||
};
|
||||
|
||||
Debug.Log($"[MultiParticipantHost] Participant UI created: {ps.participantId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动态创建单个Participant的视频显示UI
|
||||
/// 结构: [NameLabel] + [RawImage(视频画面)]
|
||||
/// </summary>
|
||||
private ParticipantUI CreateParticipantUI(string participantId)
|
||||
{
|
||||
var ui = new ParticipantUI();
|
||||
|
||||
// 根节点
|
||||
ui.root = new GameObject($"ParticipantUI_{participantId}");
|
||||
ui.root.transform.SetParent(participantVideoContainer, false);
|
||||
// 添加VerticalLayoutGroup使内容垂直排列
|
||||
var vlg = ui.root.AddComponent<VerticalLayoutGroup>();
|
||||
vlg.childControlWidth = true;
|
||||
vlg.childControlHeight = false;
|
||||
vlg.childForceExpandWidth = true;
|
||||
vlg.childForceExpandHeight = false;
|
||||
vlg.spacing = 2;
|
||||
ui.root.transform.GetComponent<RectTransform>().anchorMin = Vector2.zero;
|
||||
ui.root.transform.GetComponent<RectTransform>().anchorMax = Vector2.one;
|
||||
// 名称标签
|
||||
var labelObj = new GameObject("NameLabel");
|
||||
labelObj.transform.SetParent(ui.root.transform, false);
|
||||
ui.nameLabel = labelObj.AddComponent<Text>();
|
||||
ui.nameLabel.text = $"Participant: {participantId}";
|
||||
ui.nameLabel.fontSize = 14;
|
||||
ui.nameLabel.color = Color.white;
|
||||
ui.nameLabel.alignment = TextAnchor.MiddleCenter;
|
||||
var labelLayout = labelObj.AddComponent<LayoutElement>();
|
||||
labelLayout.preferredHeight = 20;
|
||||
|
||||
// 视频画面
|
||||
var imageObj = new GameObject("VideoImage");
|
||||
imageObj.transform.SetParent(ui.root.transform, false);
|
||||
ui.videoImage = imageObj.AddComponent<RawImage>();
|
||||
ui.videoImage.color = Color.black;
|
||||
var imageLayout = imageObj.AddComponent<LayoutElement>();
|
||||
imageLayout.preferredHeight = 200;
|
||||
|
||||
// AspectRatioFitter保持视频比例
|
||||
var aspectRatio = imageObj.AddComponent<AspectRatioFitter>();
|
||||
aspectRatio.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
|
||||
aspectRatio.aspectRatio = 16f / 9f;
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Participant断开连接回调
|
||||
/// 销毁其UI
|
||||
/// </summary>
|
||||
private void HandleParticipantDisconnected(string participantId)
|
||||
{
|
||||
if (participantUIs.TryGetValue(participantId, out var ui))
|
||||
{
|
||||
if (ui.root != null)
|
||||
GameObject.Destroy(ui.root);
|
||||
participantUIs.Remove(participantId);
|
||||
}
|
||||
|
||||
Debug.Log($"[MultiParticipantHost] Participant UI removed: {participantId}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user