This repository has been archived on 2026-01-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
SH-INT/Assets/Scripts/Caller.cs
정영민 f4cf556cde update
2025-02-20 10:30:18 +09:00

361 lines
13 KiB
C#

/*
* Copyright (C) 2021 because-why-not.com Limited
*
* Please refer to the license.txt for license information
*/
using Byn.Awrtc;
using Byn.Awrtc.Unity;
using Byn.Unity.Examples;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SHINT.WebRTC
{
public class Caller : MonoBehaviour
{
private const int MAX_CODE_LENGTH = 256;
public GameObject uVideoLayout;
public GameObject uVideoPrefab;
public Texture2D uNoImgTexture;
private ICall mCall;
public MediaConfig MediaConfig = new MediaConfig();
public NetworkConfig NetConfig = new NetworkConfig();
//public NetworkConfig NetConfig { get { return mNetConfig; } set { mNetConfig = value; } }
public RectTransform InCallPanel;
private class VideoData
{
public GameObject uiObject;
public Texture2D texture;
public RawImage image;
}
private Dictionary<ConnectionId, VideoData> mVideoUiElements = new Dictionary<ConnectionId, VideoData>();
public int ConnectionCount
{
get
{
return mVideoUiElements.Count - 1;
}
}
private string mOwnUserName = "User";
Dictionary<ConnectionId, string> mIdToUser;
protected virtual void OnCallFactoryReady()
{
//to trigger android permission requests
StartCoroutine(ExampleGlobals.RequestPermissions());
//use video and audio by default (the UI is toggled on by default as well it will change on click )
MediaConfig.Video = true;
MediaConfig.Audio = true;
MediaConfig.VideoDeviceName = UnityCallFactory.Instance.GetDefaultVideoDevice();
NetConfig.KeepSignalingAlive = true;
NetConfig.MaxIceRestart = 5;
IceServer ice = new IceServer("turn:t.y-not.app:443", "user_nov", "pass_nov");
NetConfig.IceServers.Add(ice);
NetConfig.SignalingUrl = "wss://s.y-not.app/testshared";
NetConfig.IsConference = true;
onActivate();
//Debug.Log("OnCallFactoryReady");
}
protected virtual void OnCallFactoryFailed(string error)
{
string fullErrorMsg = typeof(CallApp).Name + " can't start. The " + typeof(UnityCallFactory).Name + " failed to initialize with following error: " + error;
Debug.LogError(fullErrorMsg);
}
public event Action onActivate;
public void Active()
{
//Append("Setting up ...");
UnityCallFactory.RequestLogLevelStatic(UnityCallFactory.LogLevel.Info);
UnityCallFactory.EnsureInit(OnCallFactoryReady, OnCallFactoryFailed);
//lets just give them a random number for now.
mOwnUserName = mOwnUserName + "_" + (int)UnityEngine.Random.Range(0, 10000);
mIdToUser = new Dictionary<ConnectionId, string>();
}
public void Setup(bool useAudio = true, bool useVideo = true)
{
//setup the server
// Debug.Log("Creating ICall with " + NetConfig);
mCall = UnityCallFactory.Instance.Create(NetConfig);
if (mCall == null)
{
//Append("Failed to create the call");
return;
}
//Append("Call created!");
mCall.CallEvent += Call_CallEvent;
//setup local video element
SetupVideoUi(ConnectionId.INVALID);
mCall.Configure(MediaConfig);
onSetupEnd();
}
public event Action onSetupEnd;
/// <summary>
/// Destroys the call object and shows the setup screen again.
/// Called after a call ends or an error occurred.
/// </summary>
private void ResetCall()
{
foreach (var v in mVideoUiElements)
{
Destroy(v.Value.uiObject);
if (v.Value.texture != null)
Destroy(v.Value.texture);
}
mVideoUiElements.Clear();
CleanupCall();
}
/// <summary>
/// Handler of call events.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void Call_CallEvent(object sender, CallEventArgs e)
{
switch (e.Type)
{
case CallEventType.CallAccepted:
//Outgoing call was successful or an incoming call arrived
OnNewCall(e as CallAcceptedEventArgs);
break;
case CallEventType.CallEnded:
OnCallEnded(e as CallEndedEventArgs);
break;
case CallEventType.ListeningFailed:
Append("Failed to listen for incoming calls! Server might be down!");
ResetCall();
break;
case CallEventType.ConnectionFailed:
{
//this should be impossible to happen in conference mode!
ErrorEventArgs args = e as ErrorEventArgs;
Append("Error: " + args.Info);
Debug.LogError(args.Info);
ResetCall();
}
break;
case CallEventType.FrameUpdate:
//new frame received from webrtc (either from local camera or network)
FrameUpdateEventArgs frameargs = e as FrameUpdateEventArgs;
UpdateFrame(frameargs);
break;
case CallEventType.Message:
{
//text message received
MessageEventArgs args = e as MessageEventArgs;
//due to timing issues it can happen that a message arrives before we get the NewUser notification
//if we get a message from a not yet known user we add them here
if (mIdToUser.ContainsKey(args.ConnectionId) == false)
{
AddNewConnection(args.ConnectionId);
}
if (mIdToUser[args.ConnectionId] == "unknown")
{
//don't know this user yet. First message is expected to be their username
string name = args.Content;
OnNewUserDiscovered(name, args.ConnectionId);
}
else
{
//known user so we likely got a regular text message form them
string name = mIdToUser[args.ConnectionId];
Append(name + ":" + args.Content);
}
break;
}
case CallEventType.WaitForIncomingCall:
{
//the chat app will wait for another app to connect via the same string
WaitForIncomingCallEventArgs args = e as WaitForIncomingCallEventArgs;
Append("Waiting for incoming call address: " + args.Address);
break;
}
}
}
/// <summary>
/// Event triggers for a new incoming call
/// (in conference mode there is no difference between incoming / outgoing)
/// </summary>
/// <param name="args"></param>
private void OnNewCall(CallAcceptedEventArgs args)
{
SetupVideoUi(args.ConnectionId);
AddNewConnection(args.ConnectionId);
//let them know our username!
mCall.Send(mOwnUserName);
}
/// <summary>
/// Adds a new user. Can be called several times without adding the user twice
/// </summary>
/// <param name="id">
/// ConnectionId the new user
/// </param>
private void AddNewConnection(ConnectionId id)
{
//new connection. we do not know who that is yet until we get the first message!
if (mIdToUser.ContainsKey(id) == false)
{
mIdToUser[id] = "unknown";
Append("New connection with ID " + id + " username not yet known");
}
}
private void OnNewUserDiscovered(string name, ConnectionId id)
{
//Debug.Log("Received first message from ConnectionId " + id + "! Their username is " + name);
//store for later use
mIdToUser[id] = name;
//Append("New user discovered name: " + name + " and connection id: " + id);
}
private void OnUserLeft(ConnectionId id)
{
if (mIdToUser.ContainsKey(id))
{
string name = mIdToUser[id];
//Append("User with name " + name + " and local ID " + id + " got disconnected");
}
}
/// <summary>
/// Creates the connection specific data / ui
/// </summary>
/// <param name="id"></param>
private void SetupVideoUi(ConnectionId id)
{
//create texture + ui element
VideoData vd = new VideoData();
vd.uiObject = Instantiate(uVideoPrefab);
vd.uiObject.transform.SetParent(uVideoLayout.transform, false);
vd.image = vd.uiObject.GetComponentInChildren<RawImage>();
vd.image.texture = uNoImgTexture;
mVideoUiElements[id] = vd;
}
/// <summary>
/// User left. Cleanup connection specific data / ui
/// </summary>
/// <param name="args"></param>
private void OnCallEnded(CallEndedEventArgs args)
{
VideoData data;
if (mVideoUiElements.TryGetValue(args.ConnectionId, out data))
{
if (data.texture != null)
Destroy(data.texture);
Destroy(data.uiObject);
mVideoUiElements.Remove(args.ConnectionId);
}
OnUserLeft(args.ConnectionId);
}
private void UpdateFrame(FrameUpdateEventArgs args)
{
if (mVideoUiElements.ContainsKey(args.ConnectionId))
{
VideoData videoData = mVideoUiElements[args.ConnectionId];
//make sure not to overwrite / destroy our texture for missing image data
if (videoData.image.texture == this.uNoImgTexture)
videoData.image.texture = null;
bool mirror = args.IsRemote == false;
//converts the frame data to a texture and sets it to the raw image
UnityMediaHelper.UpdateRawImageTransform(videoData.image, args.Frame, mirror);
videoData.texture = videoData.image.texture as Texture2D;
}
}
/// <summary>
/// Destroys the call. Used if unity destroys the object or if a call
/// ended / failed due to an error.
///
/// </summary>
private void CleanupCall()
{
if (mCall != null)
{
Debug.Log("Destroying call!");
mCall.Dispose();
mCall = null;
Debug.Log("Call destroyed");
}
}
private void OnDestroy()
{
CleanupCall();
}
/// <summary>
/// toggle audio on / off
/// </summary>
/// <param name="state"></param>
public void AudioToggle(bool state)
{
MediaConfig.Audio = state;
}
/// <summary>
/// toggle video on / off
/// </summary>
/// <param name="state"></param>
public void VideoToggle(bool state)
{
MediaConfig.Video = state;
}
/// <summary>
/// Adds a new message to the message view
/// </summary>
/// <param name="text"></param>
private void Append(string text)
{
Debug.Log("Chat: " + text);
}
public void Listen(string address)
{
mCall.Listen(address);
}
/// <summary>
/// The call object needs to be updated regularly to sync data received via webrtc with
/// unity. All events will be triggered during the update method in the unity main thread
/// to avoid multi threading errors
/// </summary>
private void Update()
{
if (mCall != null)
{
//update the call
mCall.Update();
}
}
}
}