WebRTC Video Chat
作者:追风剑情 发布于:2023-12-13 11:28 分类:Unity3d
WebRTC Video Chat
Unity跨平台视频通话插件WebRtcVideoChat,可实现PC、Android、IOS跨平台的视频通话,可语音、视频以及发送文字。
[百度网盘] WebRTC Video Chat 0.9854.unitypackage 提取码 ijys
[GitHub] WebRTC Signaling Server
注意:在Android平台上创建两个网络对象会报错 Channel is unrecoverably broken and will be disposed! 导致闪退。
注意:4G/5G网络无法建立P2P
视频画面需要逆时针旋转90度再显示,下面是利用Shader旋转uv
fixed4 frag(v2f i) : SV_Target { //逆时针旋转90度 fixed2 uv = fixed2(i.uv.y, i.uv.x); fixed4 col = tex2D(_MainTex, uv); return col; }
WebRTC辅助类
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- using Byn.Awrtc;
- using Byn.Awrtc.Unity;
- /// <summary>
- /// WebRtcVideoChat 辅助类
- /// 同一个网络对象上只能创建一个房间,但可以加入多个房间
- /// 注意:Android平台上不能同时创建多个网络对象(Network),会报错闪退。
- /// </summary>
- public sealed class WebRTCHelper
- {
- #region 变量
- //信令服务器地址 (这是官方的测试地址)
- public static string uSignalingUrl = "ws://signaling.because-why-not.com/chatapp";
- //信令服务器地址,基于WebGL的浏览器用这个地址 (这是官方的测试地址)
- public static string uSecureSignalingUrl = "wss://signaling.because-why-not.com/chatapp";
- //true: 强制使用 uSecureSignalingUrl
- public static bool uForceSecureSignaling = false;
- //NAT穿透服务器地址, Stun server。(这是官方的测试地址)
- public static readonly string stunUrl = "stun:stun.because-why-not.com:443";
- //NAT穿透服务器地址, Turn server。(这是官方的测试地址)
- public static readonly string turnUrl = "turn:turn.because-why-not.com:443";
- //Turn server user
- public static readonly string turnUser = "testuser140";
- //Turn server password
- public static readonly string turnPass = "pass140";
- //NAT穿透服务器地址 (Turn server 或 Stun server)
- public static string uIceServer = turnUrl;
- //备用服务器
- public static string uIceServer2 = "stun:stun.l.google.com:19302";
- public static string uIceServerUser = turnUser;
- public static string uIceServerPassword = turnPass;
- //可以是 native webrtc 或 browser webrtc
- //BasicNetwork用于收发自定义数据
- private static IBasicNetwork mBasicNetwork = null;
- //谁开房间,谁就作为服务器方
- private static bool mIsServer = false;
- //房间名允许的最长字符个数,不要使用中文
- private const int MAX_CODE_LENGTH = 256;
- //连接ID列表
- private static List<ConnectionId> mConnections = new List<ConnectionId>();
- //用于音/视频聊天的对象
- private static ICall mCall;
- //true: 每次本地视频设备创建新帧时,FrameUpdateEventArgs都会更新
- private static bool mLocalFrameEvents = true;
- private static ConnectionId mRemoteUserId;
- private static MediaConfig mMediaConfig;
- private static MediaConfig mMediaConfigInUse;
- public static Action<string> OnTextHandler;
- public static Action<string> OnLogHandler;
- public static Action<FrameUpdateEventArgs> OnVideoFrameUpdate;
- //信令协议
- public static string SignalingProtocol
- {
- get
- {
- string protocol = "ws";
- if (Application.platform == RuntimePlatform.WebGLPlayer)
- {
- protocol = "wss";
- }
- return protocol;
- }
- }
- #endregion
- #region 初始化
- /// <summary>
- /// 初始WebRTC
- /// </summary>
- /// <param name="initBasicNetwork">初始化基本网络对象用于:收发消息</param>
- /// <param name="initCall">初始化Call对象用于:收发消息、音/视频通话</param>
- public static void Init(bool initBasicNetwork=true, bool initCall=true)
- {
- InitFactory();
- if (initBasicNetwork)
- InitBasicNetwork();
- if (initCall)
- InitCall();
- }
- #endregion
- #region Factory
- //初始化 WebRtc Network Factory
- private static void InitFactory()
- {
- Debug.Log("Initializing webrtc factory");
- UnityCallFactory.EnsureInit(OnCallFactoryReady, OnCallFactoryFailed);
- }
- private static void OnCallFactoryReady()
- {
- Debug.Log("WebRtcNetworkFactory created");
- UnityCallFactory.Instance.RequestLogLevel(UnityCallFactory.LogLevel.Info);
- }
- private static void OnCallFactoryFailed(string error)
- {
- string fullErrorMsg = "The " + typeof(UnityCallFactory).Name + " failed to initialize with following error: " + error;
- Debug.LogError(fullErrorMsg);
- }
- #endregion
- #region 基本网络
- //初始化 WebRTC BasicNetwork
- private static void InitBasicNetwork()
- {
- if (mBasicNetwork != null)
- return;
- Debug.Log("Initializing webrtc network");
- string signalingUrl = uSignalingUrl;
- if (Application.platform == RuntimePlatform.WebGLPlayer || uForceSecureSignaling)
- {
- signalingUrl = uSecureSignalingUrl;
- }
- List<IceServer> iceServers = new List<IceServer>();
- if (string.IsNullOrEmpty(uIceServer) == false)
- iceServers.Add(new IceServer(uIceServer, uIceServerUser, uIceServerPassword));
- if (string.IsNullOrEmpty(uIceServer2) == false)
- iceServers.Add(new IceServer(uIceServer2));
- if (string.IsNullOrEmpty(signalingUrl))
- {
- throw new InvalidOperationException("set signaling url is null or empty");
- }
- mBasicNetwork = UnityCallFactory.Instance.CreateBasicNetwork(signalingUrl, iceServers.ToArray());
- if (mBasicNetwork != null)
- {
- Debug.Log("WebRTCNetwork created");
- }
- else
- {
- Debug.Log("Failed to access webrtc ");
- }
- }
- /// <summary>
- /// 断开某个连接
- /// </summary>
- /// <param name="id">连接ID</param>
- public static void Disconnect(ConnectionId id)
- {
- if (mBasicNetwork == null)
- return;
- mBasicNetwork.Disconnect(id);
- }
- /// <summary>
- /// 断开所有连接
- /// </summary>
- public static void DisconnectAll()
- {
- if (mBasicNetwork == null)
- return;
- mBasicNetwork.Dispose();
- }
- //检查网络事件,需在FixedUpdate()中调用此方法
- public static void HandleNetwork()
- {
- if (mBasicNetwork == null)
- return;
- //从底层读取数据
- mBasicNetwork.Update();
- //处理自上次更新以来发生的所有新事件
- NetworkEvent evt;
- //检查每个事件
- while (mBasicNetwork != null && mBasicNetwork.Dequeue(out evt))
- {
- Debug.LogFormat("Handler Network Event: {0}", evt);
- switch (evt.Type)
- {
- //服务器初始化完成
- case NetEventType.ServerInitialized:
- mIsServer = true;
- string address = evt.Info;
- Debug.Log("Server started. Address: " + address);
- break;
- //启动服务器失败,可能是信令服务器关闭了
- case NetEventType.ServerInitFailed:
- mIsServer = false;
- Debug.Log("Server start failed." + evt.Info);
- Dispose();
- break;
- //服务器关闭
- case NetEventType.ServerClosed:
- mIsServer = false;
- Debug.LogFormat("Server closed. {0}", evt.Info);
- break;
- //用户运行客户端并连接到服务器
- //用户运行该服务器,并连接了一个新的客户端
- case NetEventType.NewConnection:
- mConnections.Add(evt.ConnectionId);
- Debug.Log("New local connection! ID: " + evt.ConnectionId);
- //如果是服务器,请向所有人发送公告,并使用本地Id作为用户名
- if (mIsServer)
- {
- //用户运行一个服务器, 向大家宣布新的连接.
- //使用服务器端连接ID作为标识
- string msg = "New user " + evt.ConnectionId + " joined the room.";
- Debug.Log(msg);
- SendString(msg);
- }
- break;
- //服务器收到失败连接
- //与服务器建立连接失败
- case NetEventType.ConnectionFailed:
- if (mIsServer)
- {
- //收到一个失败连接
- //原因: 收到了连接请求,但信令传送失败。
- //1.防火墙阻止了直接连接,但允许信号启动连接过程(可能是本地防火墙,也可能是远端防火墙)
- //2.STUN/TURN 服务器未启动
- //3.用户在连接完全建立之前就切断了连接
- Debug.Log("An incoming connection failed.");
- }
- else
- {
- //与服务器建立连接失败
- Debug.Log("Connection failed. " + evt.Info);
- Dispose();
- }
- break;
- //连接中断
- case NetEventType.Disconnected:
- mConnections.Remove(evt.ConnectionId);
- Debug.Log("Local Connection ID " + evt.ConnectionId + " disconnected");
- if (mIsServer)
- {
- string userLeftMsg = "User " + evt.ConnectionId + " left the room.";
- Debug.Log(userLeftMsg);
- //通过其他客户端,有玩家离线
- if (mConnections.Count > 0)
- {
- SendString(userLeftMsg);
- }
- }
- else
- {
- Dispose();
- }
- break;
- //收到可靠消息
- case NetEventType.ReliableMessageReceived:
- //收到不可靠消息
- case NetEventType.UnreliableMessageReceived:
- HandleIncommingMessage(ref evt);
- break;
- }
- }
- //如果更新期间网络未中断,请刷新消息以完成此更新
- if (mBasicNetwork != null)
- mBasicNetwork.Flush();
- }
- //处理收到的消息
- private static void HandleIncommingMessage(ref NetworkEvent evt)
- {
- MessageDataBuffer buffer = (MessageDataBuffer)evt.MessageData;
- string msg = Encoding.UTF8.GetString(buffer.Buffer, 0, buffer.ContentLength);
- OnTextHandler?.Invoke(msg);
- //如果服务器将消息转发给包括发件人在内的所有其他人
- if (mIsServer)
- {
- //我们使用服务器端连接id来标识客户端
- string idAndMessage = evt.ConnectionId + ":" + msg;
- Debug.Log(idAndMessage);
- //SendString(idAndMessage);
- }
- else
- {
- //客户端收到服务器发来的消息
- Debug.Log(msg);
- }
- //返回缓冲区,以便网络可以重用它
- buffer.Dispose();
- }
- //发送消息
- public static void SendString(string msg, bool reliable = true)
- {
- if (mBasicNetwork != null)
- {
- byte[] msgData = Encoding.UTF8.GetBytes(msg);
- foreach (ConnectionId id in mConnections)
- {
- mBasicNetwork.SendData(id, msgData, 0, msgData.Length, reliable);
- }
- }
- else if (mCall != null)
- {
- mCall.Send(msg, reliable);
- }
- }
- /// <summary>
- /// 创建房间 (仅支持文本聊天)
- /// </summary>
- /// <param name="roomName">房间名称(不能超过256个字符)</param>
- public static void OpenChatRoom(string roomName)
- {
- InitBasicNetwork();
- roomName = EnsureLength(roomName);
- //如果该地址正在使用中,则底层系统将返回服务器连接失败
- mBasicNetwork.StartServer(roomName);
- Debug.Log("Open Room " + roomName);
- }
- /// <summary>
- /// 关闭房间
- /// 房间关闭后,将不再接受新用户的连接,但不影响现有已建立连接的用户
- /// 当房间达到预设的最大人数后,可以调用此方法
- /// </summary>
- public static void CloseChatRoom()
- {
- if (mBasicNetwork == null)
- return;
- mBasicNetwork.StopServer();
- Debug.Log("Close Room");
- }
- /// <summary>
- /// 加入房间,可以同时加入不同的房间
- /// </summary>
- /// <param name="roomName">房间名称(不能超过256个字符)</param>
- public static void JoinChatRoom(string roomName)
- {
- InitBasicNetwork();
- roomName = EnsureLength(roomName);
- mBasicNetwork.Connect(roomName);
- Debug.Log("Join Room " + roomName + " ...");
- }
- //确保房间名称长度不超过256
- private static string EnsureLength(string roomName)
- {
- if (roomName.Length > MAX_CODE_LENGTH)
- {
- roomName = roomName.Substring(0, MAX_CODE_LENGTH);
- }
- return roomName;
- }
- #endregion
- #region 视频聊天
- //初始化Call, 音/视频聊天需要调用这个方法
- private static void InitCall()
- {
- if (mCall != null)
- return;
- NetworkConfig config = CreateNetworkConfig();
- mCall = CreateCall(config);
- if (mCall == null)
- {
- Debug.Log("Failed to create the call");
- return;
- }
- mCall.LocalFrameEvents = mLocalFrameEvents;
- mCall.CallEvent += OnCallEvent;
- if (mMediaConfig == null)
- mMediaConfig = CreateMediaConfig();
- mMediaConfigInUse = mMediaConfig.DeepClone();
- //如果没有配置摄像头设备名称,则自动设置为前置摄像头
- if (mMediaConfigInUse.Video && string.IsNullOrEmpty(mMediaConfigInUse.VideoDeviceName))
- {
- string[] devices = UnityCallFactory.Instance.GetVideoDevices();
- if (devices == null || devices.Length == 0)
- {
- Debug.Log("no device found or no device information available");
- }
- else
- {
- foreach (string s in devices)
- Debug.Log("device found: " + s + " IsFrontFacing: " + UnityCallFactory.Instance.IsFrontFacing(s));
- }
- mMediaConfigInUse.VideoDeviceName = UnityCallFactory.Instance.GetDefaultVideoDevice();
- }
- Debug.Log("Configure call using MediaConfig: " + mMediaConfigInUse);
- //设置配置
- mCall.Configure(mMediaConfigInUse);
- }
- //创建网络配置
- private static NetworkConfig CreateNetworkConfig()
- {
- NetworkConfig netConfig = new NetworkConfig();
- if (string.IsNullOrEmpty(uIceServer) == false)
- netConfig.IceServers.Add(new IceServer(uIceServer, uIceServerUser, uIceServerPassword));
- if (string.IsNullOrEmpty(uIceServer2) == false)
- netConfig.IceServers.Add(new IceServer(uIceServer2));
- if (Application.platform == RuntimePlatform.WebGLPlayer || uForceSecureSignaling)
- {
- netConfig.SignalingUrl = uSecureSignalingUrl;
- }
- else
- {
- netConfig.SignalingUrl = uSignalingUrl;
- }
- if (netConfig.SignalingUrl == "")
- {
- throw new InvalidOperationException("set signaling url is empty");
- }
- return netConfig;
- }
- //创建Call对象
- private static ICall CreateCall(NetworkConfig netConfig)
- {
- return UnityCallFactory.Instance.Create(netConfig);
- }
- //创建媒体配置
- public static MediaConfig CreateMediaConfig()
- {
- MediaConfig mediaConfig = new MediaConfig();
- //是否消除回音 (native only)
- bool useEchoCancellation = true;
- if (useEchoCancellation)
- {
- #if (!UNITY_WEBGL && !UNITY_WSA)
- var nativeConfig = new Byn.Awrtc.Native.NativeMediaConfig();
- nativeConfig.AudioOptions.echo_cancellation = true;
- mediaConfig = nativeConfig;
- #endif
- }
- #if UNITY_WSA && !UNITY_EDITOR
- var uwpConfig = new Byn.Awrtc.Uwp.UwpMediaConfig();
- uwpConfig.Mrc = true;
- //uwpConfig.ProcessLocalFrames = false;
- //uwpConfig.DefaultCodec = "H264";
- mediaConfig = uwpConfig;
- Debug.Log("Using uwp specific media config: " + mediaConfig);
- #endif
- //开启音频
- mediaConfig.Audio = true;
- //开启视频
- mediaConfig.Video = true;
- mediaConfig.VideoDeviceName = null;
- //图像格式
- mediaConfig.Format = FramePixelFormat.ABGR;
- //最小分辨率
- mediaConfig.MinWidth = 160;
- mediaConfig.MinHeight = 120;
- //分辨率越大编码/解码越慢
- mediaConfig.MaxWidth = 1920 * 2;
- mediaConfig.MaxHeight = 1080 * 2;
- //WebRTC会优先考虑IdealWidth、IdealHeight、IdealFrameRate
- //理想分辨率、帧率
- mediaConfig.IdealWidth = 160;
- mediaConfig.IdealHeight = 120;
- mediaConfig.IdealFrameRate = 30;
- return mediaConfig;
- }
- //创建视频聊天房间
- public static void OpenCallRoom(string roomName)
- {
- InitCall();
- mCall.Listen(roomName);
- }
- //加入视频聊天房间
- public static void JoinCallRoom(string roomName)
- {
- InitCall();
- //不要在会议模式下使用Call接口
- mCall.Call(roomName);
- }
- //是否设置成了静音
- public static bool IsMute()
- {
- if (mCall == null)
- return true;
- return mCall.IsMute();
- }
- //设置静音
- public static void SetMute(bool val)
- {
- if (mCall == null)
- return;
- mCall.SetMute(val);
- }
- //检查手机扬声器是否已打开。仅适用于移动本地平台
- public static bool GetLoudspeakerStatus()
- {
- if (mCall != null)
- {
- return UnityCallFactory.Instance.GetLoudspeakerStatus();
- }
- return false;
- }
- //打开/关闭手机扬声器。仅适用于移动本地平台
- public static void SetLoudspeakerStatus(bool state)
- {
- if (mCall != null)
- {
- UnityCallFactory.Instance.SetLoudspeakerStatus(state);
- }
- }
- //设置是否显示本地视频
- //如果不需要显示本地摄像头画面,可以设置成false
- public static void SetShowLocalVideo(bool showLocalVideo)
- {
- mLocalFrameEvents = showLocalVideo;
- }
- //视频聊天驱动方法,需要每帧调用
- public static void CallUpdate()
- {
- if (mCall == null)
- return;
- //将底层事件转发到Unity线程中
- mCall.Update();
- }
- //处理Call事件
- private static void OnCallEvent(object sender, CallEventArgs e)
- {
- Debug.Log(e.Type);
- switch (e.Type)
- {
- //呼出电话成功或来电到达
- case CallEventType.CallAccepted:
- {
- //HasAudioTrack() 判断对方麦克风是否可用
- //HasVideoTrack() 判断对方摄像头是否可用
- mRemoteUserId = ((CallAcceptedEventArgs)e).ConnectionId;
- string msg = "New connection with id: " + mRemoteUserId
- + " audio:" + mCall.HasAudioTrack(mRemoteUserId)
- + " video:" + mCall.HasVideoTrack(mRemoteUserId);
- Debug.Log(msg);
- OnLogHandler?.Invoke(msg);
- mConnections.Add(mRemoteUserId);
- }
- break;
- //通话结束或其中一个用户挂断电话
- case CallEventType.CallEnded:
- CleanupCall();
- break;
- //来电失败
- case CallEventType.ListeningFailed:
- {
- ErrorEventArgs args = e as ErrorEventArgs;
- string msg = "ListeningFailed: " + args.Info;
- Debug.Log(msg);
- OnLogHandler?.Invoke(msg);
- }
- break;
- //连接房间失败
- case CallEventType.ConnectionFailed:
- {
- ErrorEventArgs args = e as ErrorEventArgs;
- string msg = "ConnectionFailed: " + args.Info;
- Debug.Log(msg);
- OnLogHandler?.Invoke(msg);
- CleanupCall();
- }
- break;
- //当前系统不支持摄像头或麦克风,或不支持请求的分辨率,或权限不足
- case CallEventType.ConfigurationFailed:
- {
- ErrorEventArgs args = e as ErrorEventArgs;
- string msg = "ConfigurationFailed: " + args.Info;
- Debug.Log(msg);
- OnLogHandler?.Invoke(msg);
- CleanupCall();
- }
- break;
- //收到一个新的视频帧(来自于本地摄像头或网络)
- case CallEventType.FrameUpdate:
- if (e is FrameUpdateEventArgs)
- {
- var evt = e as FrameUpdateEventArgs;
- OnVideoFrameUpdate?.Invoke(evt);
- }
- break;
- //收到文本消息
- case CallEventType.Message:
- {
- MessageEventArgs args = e as MessageEventArgs;
- Debug.Log(args.Content);
- OnTextHandler?.Invoke(args.Content);
- }
- break;
- //接到来电
- case CallEventType.WaitForIncomingCall:
- {
- //聊天应用程序将等待另一个应用程序通过相同的字符串连接
- WaitForIncomingCallEventArgs args = e as WaitForIncomingCallEventArgs;
- string msg = "WaitForIncomingCall: " + args.Address;
- Debug.Log(msg);
- OnLogHandler?.Invoke(msg);
- }
- break;
- }
- }
- //清理Call
- public static void CleanupCall()
- {
- if (mCall == null)
- return;
- mCall.CallEvent -= OnCallEvent;
- mCall.Dispose();
- mCall = null;
- mRemoteUserId = ConnectionId.INVALID;
- mConnections.Clear();
- GC.Collect();
- GC.WaitForPendingFinalizers();
- Debug.Log("Call destroyed");
- }
- //请求权限
- public static IEnumerator RequestPermissions(bool audio = true, bool video = true)
- {
- if (audio)
- {
- yield return RequestAudioPermission();
- }
- if (video)
- {
- yield return RequestVideoPermission();
- }
- yield return null;
- }
- //请求麦克风权限
- public static IEnumerator RequestAudioPermission()
- {
- #if UNITY_ANDROID && UNITY_2018_3_OR_NEWER
- if (!HasAudioPermission())
- {
- Debug.Log("Requesting microphone permissions");
- UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.Microphone);
- //同一帧中连续调用两次请求会导致请求被忽略
- //等待用户按下 "允许" 或 "拒绝"
- yield return new WaitForSeconds(0.1f);
- //极少数情况下即使用户选择"允许",也可能返回false
- Debug.Log("microphone permission: " + HasAudioPermission());
- }
- #endif
- yield return null;
- }
- //请求摄像头权限
- public static IEnumerator RequestVideoPermission()
- {
- #if UNITY_ANDROID && UNITY_2018_3_OR_NEWER
- if (!HasVideoPermission())
- {
- Debug.Log("Requesting camera permissions");
- UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.Camera);
- yield return new WaitForSeconds(0.1f);
- Debug.Log("camera permission: " + HasVideoPermission());
- }
- #endif
- yield return null;
- }
- //是否有使用麦克风的权限
- public static bool HasAudioPermission()
- {
- #if UNITY_ANDROID && UNITY_2018_3_OR_NEWER
- if (Application.platform == RuntimePlatform.Android)
- {
- return UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Microphone);
- }
- #endif
- return true;
- }
- //是否有使用摄像头的权限
- public static bool HasVideoPermission()
- {
- #if UNITY_ANDROID && UNITY_2018_3_OR_NEWER
- if (Application.platform == RuntimePlatform.Android)
- {
- return UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Camera);
- }
- #endif
- return true;
- }
- #endregion
- #region 释放资源
- //释放
- public static void Dispose()
- {
- if (mBasicNetwork != null)
- {
- mBasicNetwork.Dispose();
- mBasicNetwork = null;
- }
- mConnections.Clear();
- mIsServer = false;
- CleanupCall();
- }
- #endregion
- }
视频通话测试
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
- using Byn.Awrtc;
- using Byn.Awrtc.Unity;
- /// <summary>
- /// 视频通话测试
- /// </summary>
- public class CallTest : MonoBehaviour
- {
- [SerializeField]
- private InputField roomNameInput;
- [SerializeField]
- private InputField messageInput;
- [SerializeField]
- private Text msgText;
- [SerializeField]
- private Text logText;
- [SerializeField]
- private RawImage localVideoImage;
- private Texture2D localTexture;
- [SerializeField]
- private RawImage remoteVideoImage;
- private Texture2D remoteTexture;
- void Start()
- {
- //请求权限
- StartCoroutine(WebRTCHelper.RequestPermissions());
- //初始化
- WebRTCHelper.Init(false, true);
- WebRTCHelper.OnTextHandler = OnTextHandler;
- WebRTCHelper.OnLogHandler = OnLogHandler;
- WebRTCHelper.OnVideoFrameUpdate = OnVideoFrameUpdate;
- }
- private void OnDestroy()
- {
- WebRTCHelper.Dispose();
- }
- void Update()
- {
- WebRTCHelper.CallUpdate();
- }
- private void FixedUpdate()
- {
- WebRTCHelper.HandleNetwork();
- }
- public void OnClickOpenChatRoom()
- {
- WebRTCHelper.OpenChatRoom(roomNameInput.text);
- }
- public void OnClickJoinChatRoom()
- {
- WebRTCHelper.JoinChatRoom(roomNameInput.text);
- }
- public void OnClickOpenCallRoom()
- {
- WebRTCHelper.OpenCallRoom(roomNameInput.text);
- }
- public void OnClickJoinCallRoom()
- {
- WebRTCHelper.JoinCallRoom(roomNameInput.text);
- }
- public void OnClickSend()
- {
- WebRTCHelper.SendString(messageInput.text);
- }
- public void OnClickHungup()
- {
- WebRTCHelper.CleanupCall();
- }
- //处理收到的文本消息
- private void OnTextHandler(string msg)
- {
- msgText.text = msg;
- }
- //处理收到的错误消息
- private void OnLogHandler(string error)
- {
- logText.text = error;
- }
- //处理收到的视频帧
- private void OnVideoFrameUpdate(FrameUpdateEventArgs e)
- {
- if (e.IsRemote)
- {
- UpdateRemoteTexture(e.Frame);
- }
- else
- {
- UpdateLocalTexture(e.Frame);
- }
- }
- //更新本地视频画面
- private void UpdateLocalTexture(IFrame frame)
- {
- if (frame == null)
- return;
- //UnityMediaHelper.UpdateRawImage(localVideoImage, frame);
- if (localTexture == null)
- localTexture = new Texture2D(frame.Width, frame.Height, TextureFormat.RGBA32, false);
- UnityMediaHelper.UpdateTexture(frame, ref localTexture);
- if (localVideoImage.texture == null)
- localVideoImage.texture = localTexture;
- }
- //更新远程视频画面
- private void UpdateRemoteTexture(IFrame frame)
- {
- if (frame == null)
- return;
- //UnityMediaHelper.UpdateRawImage(remoteVideoImage, frame);
- if (remoteTexture == null)
- remoteTexture = new Texture2D(frame.Width, frame.Height, TextureFormat.RGBA32, false);
- UnityMediaHelper.UpdateTexture(frame, ref remoteTexture);
- if (remoteVideoImage.texture == null)
- remoteVideoImage.texture = remoteTexture;
- }
- }
标签: Unity3d
日历
最新文章
随机文章
热门文章
分类
存档
- 2025年3月(4)
- 2025年2月(3)
- 2025年1月(1)
- 2024年12月(5)
- 2024年11月(5)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
- Open CASCADE
- CascadeStudio
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号
