【m】插件上传
This commit is contained in:
5
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/AssemblyInfo.cs
vendored
Normal file
5
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/AssemblyInfo.cs
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.RenderStreaming.RuntimeTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.RenderStreaming.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.RenderStreaming.Editor")]
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/AssemblyInfo.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/AssemblyInfo.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81b39ad18b63ea145ad69369505d8405
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins.meta
vendored
Normal file
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa1ca3aae718ea459e7bc96b24b0616
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins/websocket-sharp.dll
vendored
Normal file
BIN
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins/websocket-sharp.dll
vendored
Normal file
Binary file not shown.
33
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins/websocket-sharp.dll.meta
vendored
Normal file
33
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Plugins/websocket-sharp.dll.meta
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4631a21d3baadd748aef654b0dd02674
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/RenderStreamingSettings.asset
vendored
Normal file
29
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/RenderStreamingSettings.asset
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bc8ba17751ad4107a50fa1415017b6b1, type: 3}
|
||||
m_Name: RenderStreamingSettings
|
||||
m_EditorClassIdentifier:
|
||||
automaticStreaming: 1
|
||||
signalingSettings:
|
||||
id: 0
|
||||
references:
|
||||
version: 1
|
||||
00000000:
|
||||
type: {class: WebSocketSignalingSettings, ns: Unity.RenderStreaming, asm: Unity.RenderStreaming}
|
||||
data:
|
||||
m_url: ws://127.0.0.1:80
|
||||
m_iceServers:
|
||||
- m_urls:
|
||||
- stun:stun.l.google.com:19302
|
||||
m_username:
|
||||
m_credentialType: 0
|
||||
m_credential:
|
||||
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/RenderStreamingSettings.asset.meta
vendored
Normal file
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/RenderStreamingSettings.asset.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaaad242393318e4f85c45e69c8837f0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts.meta
vendored
Normal file
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc7ace171347147499aba2a6042b3285
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
140
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioCodecInfo.cs
vendored
Normal file
140
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioCodecInfo.cs
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents information about an audio codec, including its MIME type, SDP format parameters, channel count, and sample rate.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AudioCodecInfo : IEquatable<AudioCodecInfo>
|
||||
{
|
||||
[SerializeField]
|
||||
private string m_MimeType;
|
||||
[SerializeField]
|
||||
private string m_SdpFmtpLine;
|
||||
[SerializeField]
|
||||
private int m_ChannelCount;
|
||||
[SerializeField]
|
||||
private int m_SampleRate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the audio codec.
|
||||
/// </summary>
|
||||
public string name { get { return m_MimeType.GetCodecName(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MIME type of the audio codec.
|
||||
/// </summary>
|
||||
public string mimeType { get { return m_MimeType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels.
|
||||
/// </summary>
|
||||
public int channelCount { get { return m_ChannelCount; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sample rate of the audio.
|
||||
/// </summary>
|
||||
public int sampleRate { get { return m_SampleRate; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SDP format parameters line.
|
||||
/// </summary>
|
||||
public string sdpFmtpLine { get { return m_SdpFmtpLine; } }
|
||||
|
||||
static internal AudioCodecInfo Create(RTCRtpCodecCapability caps)
|
||||
{
|
||||
return new AudioCodecInfo(caps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="AudioCodecInfo"/> is equal to the current <see cref="AudioCodecInfo"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// Debug.Log(audioStreamReceiver1.codec.Equals(audioStreamReceiver2.codec));
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="other">The <see cref="AudioCodecInfo"/> to compare with the current <see cref="AudioCodecInfo"/>.</param>
|
||||
/// <returns>true if the specified <see cref="AudioCodecInfo"/> is equal to the current <see cref="AudioCodecInfo"/>; otherwise, false.</returns>
|
||||
public bool Equals(AudioCodecInfo other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
return this.mimeType == other.mimeType
|
||||
&& this.sdpFmtpLine == other.sdpFmtpLine
|
||||
&& this.channelCount == other.channelCount
|
||||
&& this.sampleRate == other.sampleRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current <see cref="AudioCodecInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current <see cref="AudioCodecInfo"/>.</param>
|
||||
/// <returns>true if the specified object is equal to the current <see cref="AudioCodecInfo"/>; otherwise, false.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is AudioCodecInfo ? Equals((AudioCodecInfo)obj) : base.Equals(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for the <see cref="AudioCodecInfo"/>.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the current <see cref="AudioCodecInfo"/>.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return new { mimeType, sdpFmtpLine, channelCount, sampleRate }.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two specified instances of <see cref="AudioCodecInfo"/> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="AudioCodecInfo"/> to compare.</param>
|
||||
/// <param name="right">The second <see cref="AudioCodecInfo"/> to compare.</param>
|
||||
/// <returns>true if the two <see cref="AudioCodecInfo"/> instances are equal; otherwise, false.</returns>
|
||||
public static bool operator ==(AudioCodecInfo left, AudioCodecInfo right)
|
||||
{
|
||||
if (ReferenceEquals(left, null))
|
||||
{
|
||||
return ReferenceEquals(left, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two specified instances of <see cref="AudioCodecInfo"/> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first <see cref="AudioCodecInfo"/> to compare.</param>
|
||||
/// <param name="right">The second <see cref="AudioCodecInfo"/> to compare.</param>
|
||||
/// <returns>true if the two <see cref="AudioCodecInfo"/> instances are not equal; otherwise, false.</returns>
|
||||
public static bool operator !=(AudioCodecInfo left, AudioCodecInfo right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
internal AudioCodecInfo(RTCRtpCodecCapability cap)
|
||||
{
|
||||
m_MimeType = cap.mimeType;
|
||||
m_SdpFmtpLine = cap.sdpFmtpLine;
|
||||
m_ChannelCount = cap.channels.GetValueOrDefault();
|
||||
m_SampleRate = cap.clockRate.GetValueOrDefault();
|
||||
}
|
||||
|
||||
internal bool Equals(RTCRtpCodecCapability other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
return this.mimeType == other.mimeType
|
||||
&& this.sdpFmtpLine == other.sdpFmtpLine
|
||||
&& this.channelCount == other.channels
|
||||
&& this.sampleRate == other.clockRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioCodecInfo.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioCodecInfo.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d20d601e065b9147a6dd6d64fb43de6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamReceiver.cs
vendored
Normal file
124
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamReceiver.cs
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// AudioStreamReceiver is a component that receives audio streams and plays them through a specified AudioSource.
|
||||
/// </summary>
|
||||
/// <seealso cref="AudioCodecInfo"/>
|
||||
[AddComponentMenu("Render Streaming/Audio Stream Receiver")]
|
||||
public class AudioStreamReceiver : StreamReceiverBase
|
||||
{
|
||||
internal const string CodecPropertyName = nameof(m_Codec);
|
||||
internal const string TargetAudioSourcePropertyName = nameof(m_TargetAudioSource);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for handling updates to the received audio source.
|
||||
/// </summary>
|
||||
/// <param name="source">The updated AudioSource.</param>
|
||||
public delegate void OnUpdateReceiveAudioSourceHandler(AudioSource source);
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the received audio source is updated.
|
||||
/// </summary>
|
||||
public OnUpdateReceiveAudioSourceHandler OnUpdateReceiveAudioSource;
|
||||
|
||||
[SerializeField]
|
||||
private AudioSource m_TargetAudioSource;
|
||||
|
||||
[SerializeField, Codec]
|
||||
private AudioCodecInfo m_Codec;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the codec information for the audio stream.
|
||||
/// </summary>
|
||||
public AudioCodecInfo codec
|
||||
{
|
||||
get { return m_Codec; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target AudioSource where the received audio will be played.
|
||||
/// </summary>
|
||||
public AudioSource targetAudioSource
|
||||
{
|
||||
get { return m_TargetAudioSource; }
|
||||
set { m_TargetAudioSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available audio codecs.
|
||||
/// </summary>
|
||||
/// <code>
|
||||
/// var codecs = AudioStreamReceiver.GetAvailableCodecs();
|
||||
/// foreach (var codec in codecs)
|
||||
/// Debug.Log(codec.name);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A list of available codecs.</returns>
|
||||
static public IEnumerable<AudioCodecInfo> GetAvailableCodecs()
|
||||
{
|
||||
var excludeCodecMimeType = new[] { "audio/CN", "audio/telephone-event" };
|
||||
var capabilities = RTCRtpReceiver.GetCapabilities(TrackKind.Audio);
|
||||
return capabilities.codecs.Where(codec => !excludeCodecMimeType.Contains(codec.mimeType)).Select(codec => AudioCodecInfo.Create(codec));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the codec for the audio stream.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var codec = AudioStreamReceiver.GetAvailableCodecs().FirstOrDefault(x => x.mimeType.Contains("opus"));
|
||||
/// audioStreamReceiver.SetCodec(codec);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="codec">The codec information to set.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the transceiver is streaming or the track has ended.</exception>
|
||||
public void SetCodec(AudioCodecInfo codec)
|
||||
{
|
||||
m_Codec = codec;
|
||||
|
||||
if (Transceiver == null)
|
||||
return;
|
||||
if (!string.IsNullOrEmpty(Transceiver.Mid))
|
||||
throw new InvalidOperationException("Transceiver is streaming. This operation is invalid during the track is in use.");
|
||||
if (Transceiver.Sender.Track.ReadyState == TrackState.Ended)
|
||||
throw new InvalidOperationException("Track has already been ended.");
|
||||
|
||||
var codecs = new AudioCodecInfo[] { m_Codec };
|
||||
RTCErrorType error = Transceiver.SetCodecPreferences(SelectCodecCapabilities(codecs).ToArray());
|
||||
if (error != RTCErrorType.None)
|
||||
throw new InvalidOperationException($"Set codec is failed. errorCode={error}");
|
||||
}
|
||||
|
||||
internal IEnumerable<RTCRtpCodecCapability> SelectCodecCapabilities(IEnumerable<AudioCodecInfo> codecs)
|
||||
{
|
||||
return RTCRtpReceiver.GetCapabilities(TrackKind.Audio).SelectCodecCapabilities(codecs);
|
||||
}
|
||||
|
||||
private protected virtual void Start()
|
||||
{
|
||||
OnStartedStream += StartedStream;
|
||||
OnStoppedStream += StoppedStream;
|
||||
}
|
||||
|
||||
private void StartedStream(string connectionId)
|
||||
{
|
||||
if (Track is AudioStreamTrack audioTrack)
|
||||
{
|
||||
m_TargetAudioSource?.SetTrack(audioTrack);
|
||||
OnUpdateReceiveAudioSource?.Invoke(m_TargetAudioSource);
|
||||
}
|
||||
}
|
||||
|
||||
private void StoppedStream(string connectionId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamReceiver.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamReceiver.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6bfd564296404f5897c569b1a6b352b
|
||||
timeCreated: 1600837296
|
||||
568
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamSender.cs
vendored
Normal file
568
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamSender.cs
vendored
Normal file
@@ -0,0 +1,568 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the source of the audio stream.
|
||||
/// </summary>
|
||||
public enum AudioStreamSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the AudioListener component as the audio source.
|
||||
/// </summary>
|
||||
AudioListener = 0,
|
||||
/// <summary>
|
||||
/// Use the AudioSource component as the audio source.
|
||||
/// </summary>
|
||||
AudioSource = 1,
|
||||
/// <summary>
|
||||
/// Use the microphone as the audio source.
|
||||
/// </summary>
|
||||
Microphone = 2,
|
||||
/// <summary>
|
||||
/// Use only the API to provide audio data.
|
||||
/// </summary>
|
||||
APIOnly = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Component for sending audio streams.
|
||||
/// </summary>
|
||||
/// <seealso cref="AudioStreamSource"/>
|
||||
/// <seealso cref="AudioCodecInfo"/>
|
||||
[AddComponentMenu("Render Streaming/Audio Stream Sender")]
|
||||
public class AudioStreamSender : StreamSenderBase
|
||||
{
|
||||
static readonly uint s_defaultMinBitrate = 0;
|
||||
static readonly uint s_defaultMaxBitrate = 200;
|
||||
|
||||
internal const string SourcePropertyName = nameof(m_Source);
|
||||
internal const string AudioSourcePropertyName = nameof(m_AudioSource);
|
||||
internal const string AudioListenerPropertyName = nameof(m_AudioListener);
|
||||
internal const string MicrophoneDeviceIndexPropertyName = nameof(m_MicrophoneDeviceIndex);
|
||||
internal const string AutoRequestUserAuthorizationPropertyName = nameof(m_AutoRequestUserAuthorization);
|
||||
internal const string CodecPropertyName = nameof(m_Codec);
|
||||
internal const string BitratePropertyName = nameof(m_Bitrate);
|
||||
internal const string LoopbackPropertyName = nameof(m_Loopback);
|
||||
|
||||
[SerializeField]
|
||||
private AudioStreamSource m_Source;
|
||||
|
||||
[SerializeField]
|
||||
private AudioListener m_AudioListener;
|
||||
|
||||
[SerializeField]
|
||||
private AudioSource m_AudioSource;
|
||||
|
||||
[SerializeField]
|
||||
private int m_MicrophoneDeviceIndex;
|
||||
|
||||
[SerializeField]
|
||||
private bool m_AutoRequestUserAuthorization = true;
|
||||
|
||||
[SerializeField, Codec]
|
||||
private AudioCodecInfo m_Codec;
|
||||
|
||||
[SerializeField, Bitrate(0, 1000)]
|
||||
private Range m_Bitrate = new Range(s_defaultMinBitrate, s_defaultMaxBitrate);
|
||||
|
||||
[SerializeField]
|
||||
private bool m_Loopback = false;
|
||||
|
||||
private int m_sampleRate = 0;
|
||||
|
||||
private AudioStreamSourceImpl m_sourceImpl = null;
|
||||
|
||||
private int m_frequency = 48000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the source of the audio stream.
|
||||
/// </summary>
|
||||
public AudioStreamSource source
|
||||
{
|
||||
get { return m_Source; }
|
||||
set
|
||||
{
|
||||
if (m_Source == value)
|
||||
return;
|
||||
m_Source = value;
|
||||
|
||||
if (!isPlaying)
|
||||
return;
|
||||
|
||||
var op = CreateTrack();
|
||||
StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the codec used for the audio stream.
|
||||
/// </summary>
|
||||
public AudioCodecInfo codec
|
||||
{
|
||||
get { return m_Codec; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum bitrate for the audio stream.
|
||||
/// </summary>
|
||||
public uint minBitrate
|
||||
{
|
||||
get { return m_Bitrate.min; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum bitrate for the audio stream.
|
||||
/// </summary>
|
||||
public uint maxBitrate
|
||||
{
|
||||
get { return m_Bitrate.max; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to play the audio locally while sending it to the remote peer.
|
||||
/// </summary>
|
||||
public bool loopback
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Loopback;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_Loopback == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Loopback = value;
|
||||
|
||||
if (Track is AudioStreamTrack audioTrack)
|
||||
{
|
||||
audioTrack.Loopback = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the microphone device used as the audio source.
|
||||
/// </summary>
|
||||
public int sourceDeviceIndex
|
||||
{
|
||||
get { return m_MicrophoneDeviceIndex; }
|
||||
set
|
||||
{
|
||||
if (m_MicrophoneDeviceIndex == value)
|
||||
return;
|
||||
m_MicrophoneDeviceIndex = value;
|
||||
|
||||
if (!isPlaying || m_Source != AudioStreamSource.Microphone)
|
||||
return;
|
||||
|
||||
var op = CreateTrack();
|
||||
StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AudioSource component used as the audio source.
|
||||
/// </summary>
|
||||
public AudioSource audioSource
|
||||
{
|
||||
get { return m_AudioSource; }
|
||||
set
|
||||
{
|
||||
if (m_AudioSource == value)
|
||||
return;
|
||||
m_AudioSource = value;
|
||||
|
||||
if (!isPlaying || m_Source != AudioStreamSource.AudioSource)
|
||||
return;
|
||||
|
||||
var op = CreateTrack();
|
||||
StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AudioListener component used as the audio source.
|
||||
/// </summary>
|
||||
public AudioListener audioListener
|
||||
{
|
||||
get { return m_AudioListener; }
|
||||
set
|
||||
{
|
||||
if (m_AudioListener == value)
|
||||
return;
|
||||
m_AudioListener = value;
|
||||
|
||||
if (!isPlaying || m_Source != AudioStreamSource.AudioListener)
|
||||
return;
|
||||
|
||||
var op = CreateTrack();
|
||||
StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available video codecs.
|
||||
/// </summary>
|
||||
/// <code>
|
||||
/// var codecs = VideoStreamSender.GetAvailableCodecs();
|
||||
/// foreach (var codec in codecs)
|
||||
/// Debug.Log(codec.name);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <returns>A list of available codecs.</returns>
|
||||
static public IEnumerable<AudioCodecInfo> GetAvailableCodecs()
|
||||
{
|
||||
var excludeCodecMimeType = new[] { "audio/CN", "audio/telephone-event" };
|
||||
var capabilities = RTCRtpSender.GetCapabilities(TrackKind.Audio);
|
||||
return capabilities.codecs.Where(codec => !excludeCodecMimeType.Contains(codec.mimeType)).Select(codec => AudioCodecInfo.Create(codec));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the bitrate range for the audio stream.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// audioStreamSender.SetBitrate(128, 256);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="minBitrate">The minimum bitrate in kbps. Must be greater than zero.</param>
|
||||
/// <param name="maxBitrate">The maximum bitrate in kbps. Must be greater than or equal to the minimum bitrate.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when the maximum bitrate is less than the minimum bitrate.</exception>
|
||||
public void SetBitrate(uint minBitrate, uint maxBitrate)
|
||||
{
|
||||
if (minBitrate > maxBitrate)
|
||||
throw new ArgumentException("The maxBitrate must be greater than minBitrate.", "maxBitrate");
|
||||
m_Bitrate.min = minBitrate;
|
||||
m_Bitrate.max = maxBitrate;
|
||||
foreach (var transceiver in Transceivers.Values)
|
||||
{
|
||||
RTCError error = transceiver.Sender.SetBitrate(m_Bitrate.min, m_Bitrate.max);
|
||||
if (error.errorType != RTCErrorType.None)
|
||||
RenderStreaming.Logger.Log(LogType.Error, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the codec for the audio stream.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var codec = AudioStreamSender.GetAvailableCodecs().First(x => x.mimeType.Contains("opus"));
|
||||
/// audioStreamSender.SetCodec(codec);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="codec">The codec information to set.</param>
|
||||
public void SetCodec(AudioCodecInfo codec)
|
||||
{
|
||||
m_Codec = codec;
|
||||
foreach (var transceiver in Transceivers.Values)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(transceiver.Mid))
|
||||
continue;
|
||||
if (transceiver.Sender.Track.ReadyState == TrackState.Ended)
|
||||
continue;
|
||||
|
||||
var codecs = new AudioCodecInfo[] { m_Codec };
|
||||
RTCErrorType error = transceiver.SetCodecPreferences(SelectCodecCapabilities(codecs).ToArray());
|
||||
if (error != RTCErrorType.None)
|
||||
throw new InvalidOperationException($"Set codec is failed. errorCode={error}");
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<RTCRtpCodecCapability> SelectCodecCapabilities(IEnumerable<AudioCodecInfo> codecs)
|
||||
{
|
||||
return RTCRtpSender.GetCapabilities(TrackKind.Audio).SelectCodecCapabilities(codecs);
|
||||
}
|
||||
|
||||
private protected virtual void Awake()
|
||||
{
|
||||
OnStartedStream += _OnStartedStream;
|
||||
OnStoppedStream += _OnStoppedStream;
|
||||
}
|
||||
|
||||
private protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
m_sourceImpl?.Dispose();
|
||||
m_sourceImpl = null;
|
||||
}
|
||||
|
||||
void OnAudioConfigurationChanged(bool deviceWasChanged)
|
||||
{
|
||||
m_sampleRate = AudioSettings.outputSampleRate;
|
||||
}
|
||||
|
||||
void _OnStartedStream(string connectionId)
|
||||
{
|
||||
}
|
||||
|
||||
void _OnStoppedStream(string connectionId)
|
||||
{
|
||||
m_sourceImpl?.Dispose();
|
||||
m_sourceImpl = null;
|
||||
}
|
||||
|
||||
internal override WaitForCreateTrack CreateTrack()
|
||||
{
|
||||
m_sourceImpl?.Dispose();
|
||||
m_sourceImpl = CreateAudioStreamSource();
|
||||
return m_sourceImpl.CreateTrack();
|
||||
}
|
||||
|
||||
AudioStreamSourceImpl CreateAudioStreamSource()
|
||||
{
|
||||
switch (m_Source)
|
||||
{
|
||||
case AudioStreamSource.AudioListener:
|
||||
return new AudioStreamSourceAudioListener(this);
|
||||
case AudioStreamSource.AudioSource:
|
||||
return new AudioStreamSourceAudioSource(this);
|
||||
case AudioStreamSource.Microphone:
|
||||
return new AudioStreamSourceMicrophone(this);
|
||||
case AudioStreamSource.APIOnly:
|
||||
return new AudioStreamSourceAPIOnly(this);
|
||||
}
|
||||
throw new InvalidOperationException("");
|
||||
}
|
||||
|
||||
private protected override void OnEnable()
|
||||
{
|
||||
OnAudioConfigurationChanged(false);
|
||||
AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChanged;
|
||||
base.OnEnable();
|
||||
}
|
||||
|
||||
private protected override void OnDisable()
|
||||
{
|
||||
AudioSettings.OnAudioConfigurationChanged -= OnAudioConfigurationChanged;
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the audio data for the stream.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// int sampleRate = AudioSettings.outputSampleRate;
|
||||
/// int frequency = 440;
|
||||
/// int bufferSize = sampleRate; // 1 second buffer
|
||||
/// var audioData = new NativeArray<float>(bufferSize, Allocator.Temp);
|
||||
/// for (int i = 0; i < bufferSize; i++)
|
||||
/// {
|
||||
/// audioData[i] = Mathf.Sin(2 * Mathf.PI * frequency * i / sampleRate);
|
||||
/// }
|
||||
/// audioStreamSender.SetData(audioData.AsReadOnly(), 1);
|
||||
/// audioData.Dispose();
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="nativeArray">The native array containing the audio data.</param>
|
||||
/// <param name="channels">The number of audio channels.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the source property is not set to AudioStreamSource.APIOnly.</exception>
|
||||
public void SetData(NativeArray<float>.ReadOnly nativeArray, int channels)
|
||||
{
|
||||
if (m_Source != AudioStreamSource.APIOnly)
|
||||
throw new InvalidOperationException("To use this method, please set AudioStreamSource.APIOnly to source property");
|
||||
if (!isPlaying)
|
||||
return;
|
||||
(m_sourceImpl as AudioStreamSourceAPIOnly)?.SetData(nativeArray, channels, m_sampleRate);
|
||||
}
|
||||
|
||||
abstract class AudioStreamSourceImpl : IDisposable
|
||||
{
|
||||
protected AudioStreamSourceImpl(AudioStreamSender parent)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract WaitForCreateTrack CreateTrack();
|
||||
public abstract void Dispose();
|
||||
}
|
||||
|
||||
class AudioStreamSourceAudioListener : AudioStreamSourceImpl
|
||||
{
|
||||
private AudioListener m_audioListener;
|
||||
|
||||
public AudioStreamSourceAudioListener(AudioStreamSender parent) : base(parent)
|
||||
{
|
||||
m_audioListener = parent.m_AudioListener;
|
||||
if (m_audioListener == null)
|
||||
throw new InvalidOperationException("The audioListener is not assigned.");
|
||||
}
|
||||
|
||||
public override WaitForCreateTrack CreateTrack()
|
||||
{
|
||||
var instruction = new WaitForCreateTrack();
|
||||
instruction.Done(new AudioStreamTrack(m_audioListener));
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~AudioStreamSourceAudioListener()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class AudioStreamSourceAudioSource : AudioStreamSourceImpl
|
||||
{
|
||||
private AudioSource m_audioSource;
|
||||
public AudioStreamSourceAudioSource(AudioStreamSender parent) : base(parent)
|
||||
{
|
||||
m_audioSource = parent.m_AudioSource;
|
||||
if (m_audioSource == null)
|
||||
throw new InvalidOperationException("The audioSource is not assigned.");
|
||||
|
||||
}
|
||||
|
||||
public override WaitForCreateTrack CreateTrack()
|
||||
{
|
||||
var instruction = new WaitForCreateTrack();
|
||||
instruction.Done(new AudioStreamTrack(m_audioSource));
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~AudioStreamSourceAudioSource()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
class AudioStreamSourceMicrophone : AudioStreamSourceImpl
|
||||
{
|
||||
int m_deviceIndex;
|
||||
bool m_autoRequestUserAuthorization;
|
||||
int m_frequency;
|
||||
string m_deviceName;
|
||||
AudioSource m_audioSource;
|
||||
GameObject m_audioSourceObj;
|
||||
AudioStreamSender m_parent;
|
||||
|
||||
public AudioStreamSourceMicrophone(AudioStreamSender parent) : base(parent)
|
||||
{
|
||||
int deviceIndex = parent.m_MicrophoneDeviceIndex;
|
||||
if (deviceIndex < 0 || Microphone.devices.Length <= deviceIndex)
|
||||
throw new ArgumentOutOfRangeException("deviceIndex", deviceIndex, "The deviceIndex is out of range");
|
||||
m_parent = parent;
|
||||
m_deviceIndex = deviceIndex;
|
||||
m_frequency = parent.m_frequency;
|
||||
m_autoRequestUserAuthorization = parent.m_AutoRequestUserAuthorization;
|
||||
}
|
||||
|
||||
public override WaitForCreateTrack CreateTrack()
|
||||
{
|
||||
var instruction = new WaitForCreateTrack();
|
||||
m_parent.StartCoroutine(CreateTrackCoroutine(instruction));
|
||||
return instruction;
|
||||
}
|
||||
|
||||
IEnumerator CreateTrackCoroutine(WaitForCreateTrack instruction)
|
||||
{
|
||||
if (m_autoRequestUserAuthorization)
|
||||
{
|
||||
AsyncOperation op = Application.RequestUserAuthorization(UserAuthorization.Microphone);
|
||||
yield return op;
|
||||
}
|
||||
if (!Application.HasUserAuthorization(UserAuthorization.Microphone))
|
||||
throw new InvalidOperationException("Call Application.RequestUserAuthorization before creating track with Microphone.");
|
||||
|
||||
m_deviceName = Microphone.devices[m_deviceIndex];
|
||||
Microphone.GetDeviceCaps(m_deviceName, out int minFreq, out int maxFreq);
|
||||
var micClip = Microphone.Start(m_deviceName, true, 1, m_frequency);
|
||||
|
||||
// set the latency to “0” samples before the audio starts to play.
|
||||
yield return new WaitUntil(() => Microphone.GetPosition(m_deviceName) > 0);
|
||||
|
||||
m_audioSourceObj = new GameObject("Audio");
|
||||
m_audioSourceObj.hideFlags = HideFlags.HideInHierarchy;
|
||||
DontDestroyOnLoad(m_audioSourceObj);
|
||||
m_audioSource = m_audioSourceObj.AddComponent<AudioSource>();
|
||||
m_audioSource.clip = micClip;
|
||||
m_audioSource.loop = true;
|
||||
m_audioSource.Play();
|
||||
|
||||
instruction.Done(new AudioStreamTrack(m_audioSource));
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_audioSourceObj != null)
|
||||
{
|
||||
m_audioSource.Stop();
|
||||
var clip = m_audioSource.clip;
|
||||
if (clip != null)
|
||||
{
|
||||
Destroy(clip);
|
||||
}
|
||||
m_audioSource.clip = null;
|
||||
|
||||
Destroy(m_audioSourceObj);
|
||||
m_audioSourceObj = null;
|
||||
m_audioSource = null;
|
||||
}
|
||||
if (Microphone.IsRecording(m_deviceName))
|
||||
Microphone.End(m_deviceName);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~AudioStreamSourceMicrophone()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class AudioStreamSourceAPIOnly : AudioStreamSourceImpl
|
||||
{
|
||||
AudioStreamTrack m_audioTrack;
|
||||
|
||||
public AudioStreamSourceAPIOnly(AudioStreamSender parent) : base(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override WaitForCreateTrack CreateTrack()
|
||||
{
|
||||
var instruction = new WaitForCreateTrack();
|
||||
m_audioTrack = new AudioStreamTrack();
|
||||
instruction.Done(m_audioTrack);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public void SetData(NativeArray<float>.ReadOnly nativeArray, int channels, int sampleRate)
|
||||
{
|
||||
m_audioTrack?.SetData(nativeArray, channels, sampleRate);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~AudioStreamSourceAPIOnly()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamSender.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AudioStreamSender.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28e4557ba7cb056499034647b21b6361
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
180
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AutomaticStreaming.cs
vendored
Normal file
180
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AutomaticStreaming.cs
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
using Unity.RenderStreaming.InputSystem;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Users;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal class AutomaticStreaming : MonoBehaviour
|
||||
{
|
||||
private SignalingManager renderstreaming;
|
||||
private Broadcast broadcast;
|
||||
private VideoStreamSender videoStreamSender;
|
||||
private AudioStreamSender audioStreamSender;
|
||||
private AutoInputReceiver inputReceiver;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
gameObject.hideFlags = HideFlags.HideInHierarchy;
|
||||
|
||||
broadcast = gameObject.AddComponent<Broadcast>();
|
||||
|
||||
videoStreamSender = gameObject.AddComponent<VideoStreamSender>();
|
||||
videoStreamSender.source = VideoStreamSource.Screen;
|
||||
videoStreamSender.SetTextureSize(new Vector2Int(Screen.width, Screen.height));
|
||||
broadcast.AddComponent(videoStreamSender);
|
||||
|
||||
audioStreamSender = gameObject.AddComponent<AudioStreamSender>();
|
||||
audioStreamSender.source = AudioStreamSource.APIOnly;
|
||||
broadcast.AddComponent(audioStreamSender);
|
||||
|
||||
inputReceiver = gameObject.AddComponent<AutoInputReceiver>();
|
||||
broadcast.AddComponent(inputReceiver);
|
||||
|
||||
renderstreaming = gameObject.AddComponent<SignalingManager>();
|
||||
renderstreaming.AddSignalingHandler(broadcast);
|
||||
renderstreaming.Run();
|
||||
|
||||
SceneManager.activeSceneChanged += (scene1, scene2) =>
|
||||
{
|
||||
var audioListener = FindObjectOfType<AudioListener>();
|
||||
if (audioListener == null || audioListener.gameObject.GetComponent<AutoAudioFilter>() != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var autoFilter = audioListener.gameObject.AddComponent<AutoAudioFilter>();
|
||||
autoFilter.SetSender(audioStreamSender);
|
||||
};
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
renderstreaming.Stop();
|
||||
renderstreaming = null;
|
||||
broadcast = null;
|
||||
videoStreamSender = null;
|
||||
audioStreamSender = null;
|
||||
inputReceiver = null;
|
||||
}
|
||||
|
||||
class AutoAudioFilter : MonoBehaviour
|
||||
{
|
||||
private AudioStreamSender sender;
|
||||
|
||||
public void SetSender(AudioStreamSender sender)
|
||||
{
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
this.hideFlags = HideFlags.HideInInspector;
|
||||
}
|
||||
|
||||
private void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
if (sender == null || sender.source != AudioStreamSource.APIOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var nativeArray = new NativeArray<float>(data, Allocator.Temp);
|
||||
sender.SetData(nativeArray.AsReadOnly(), channels);
|
||||
nativeArray.Dispose();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
sender = null;
|
||||
}
|
||||
}
|
||||
|
||||
class AutoInputReceiver : InputChannelReceiverBase
|
||||
{
|
||||
public override event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
onDeviceChange += OnDeviceChange;
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
onDeviceChange -= OnDeviceChange;
|
||||
}
|
||||
|
||||
private void PerformPairingWithDevice(InputDevice device)
|
||||
{
|
||||
inputUser = InputUser.PerformPairingWithDevice(device, inputUser);
|
||||
}
|
||||
|
||||
private void UnpairDevices(InputDevice device)
|
||||
{
|
||||
if (!inputUser.valid)
|
||||
return;
|
||||
inputUser.UnpairDevice(device);
|
||||
}
|
||||
|
||||
public override void SetChannel(string connectionId, RTCDataChannel channel)
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
AssignUserAndDevices();
|
||||
receiver = new Receiver(channel);
|
||||
receiver.onDeviceChange += onDeviceChange;
|
||||
receiverInput = new InputSystem.InputRemoting(receiver);
|
||||
subscriberDisposer = receiverInput.Subscribe(receiverInput);
|
||||
receiverInput.StartSending();
|
||||
}
|
||||
|
||||
base.SetChannel(connectionId, channel);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected virtual void Dispose()
|
||||
{
|
||||
receiverInput?.StopSending();
|
||||
subscriberDisposer?.Dispose();
|
||||
receiver?.Dispose();
|
||||
receiver = null;
|
||||
}
|
||||
|
||||
[NonSerialized] private InputUser inputUser;
|
||||
[NonSerialized] private Receiver receiver;
|
||||
[NonSerialized] private InputSystem.InputRemoting receiverInput;
|
||||
[NonSerialized] private IDisposable subscriberDisposer;
|
||||
|
||||
private void AssignUserAndDevices()
|
||||
{
|
||||
inputUser = InputUser.all.FirstOrDefault();
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceChange(InputDevice device, InputDeviceChange change)
|
||||
{
|
||||
switch (change)
|
||||
{
|
||||
case InputDeviceChange.Added:
|
||||
PerformPairingWithDevice(device);
|
||||
return;
|
||||
case InputDeviceChange.Removed:
|
||||
UnpairDevices(device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AutomaticStreaming.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/AutomaticStreaming.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50f50ea0132f471fa447969dcbceb3e9
|
||||
timeCreated: 1674179059
|
||||
100
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Broadcast.cs
vendored
Normal file
100
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Broadcast.cs
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
public class Broadcast : SignalingHandlerBase,
|
||||
IOfferHandler, IAddChannelHandler, IDisconnectHandler, IDeletedConnectionHandler,
|
||||
IAddReceiverHandler
|
||||
{
|
||||
[SerializeField] private List<Component> streams = new List<Component>();
|
||||
|
||||
private List<string> connectionIds = new List<string>();
|
||||
|
||||
public override IEnumerable<Component> Streams => streams;
|
||||
|
||||
public void AddComponent(Component component)
|
||||
{
|
||||
streams.Add(component);
|
||||
}
|
||||
|
||||
public void RemoveComponent(Component component)
|
||||
{
|
||||
streams.Remove(component);
|
||||
}
|
||||
|
||||
public void OnDeletedConnection(SignalingEventData eventData)
|
||||
{
|
||||
Disconnect(eventData.connectionId);
|
||||
}
|
||||
|
||||
public void OnDisconnect(SignalingEventData eventData)
|
||||
{
|
||||
Disconnect(eventData.connectionId);
|
||||
}
|
||||
|
||||
private void Disconnect(string connectionId)
|
||||
{
|
||||
if (!connectionIds.Contains(connectionId))
|
||||
return;
|
||||
connectionIds.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);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAddReceiver(SignalingEventData data)
|
||||
{
|
||||
var track = data.transceiver.Receiver.Track;
|
||||
IStreamReceiver receiver = GetReceiver(track.Kind);
|
||||
SetReceiver(data.connectionId, receiver, data.transceiver);
|
||||
}
|
||||
|
||||
public void OnOffer(SignalingEventData data)
|
||||
{
|
||||
if (connectionIds.Contains(data.connectionId))
|
||||
{
|
||||
RenderStreaming.Logger.Log($"Already answered this connectionId : {data.connectionId}");
|
||||
return;
|
||||
}
|
||||
connectionIds.Add(data.connectionId);
|
||||
|
||||
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);
|
||||
}
|
||||
SendAnswer(data.connectionId);
|
||||
}
|
||||
|
||||
public void OnAddChannel(SignalingEventData data)
|
||||
{
|
||||
var channel = streams.OfType<IDataChannel>().
|
||||
FirstOrDefault(r => !r.IsConnected && !r.IsLocal);
|
||||
channel?.SetChannel(data.connectionId, data.channel);
|
||||
}
|
||||
|
||||
IStreamReceiver GetReceiver(WebRTC.TrackKind kind)
|
||||
{
|
||||
if (kind == WebRTC.TrackKind.Audio)
|
||||
return streams.OfType<AudioStreamReceiver>().First();
|
||||
if (kind == WebRTC.TrackKind.Video)
|
||||
return streams.OfType<VideoStreamReceiver>().First();
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Broadcast.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Broadcast.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2307e7ad91222841b562bebd81a69a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Codec.cs
vendored
Normal file
36
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Codec.cs
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal static class RTCRtpCodecCapabilityExtension
|
||||
{
|
||||
public static string GetCodecName(this string mimeType)
|
||||
{
|
||||
if (mimeType == null)
|
||||
return null;
|
||||
string[] substrings = mimeType.Split('/');
|
||||
if (substrings.Length > 1)
|
||||
return substrings[1];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<RTCRtpCodecCapability> SelectCodecCapabilities(this RTCRtpCapabilities capabilities, IEnumerable<VideoCodecInfo> codecs)
|
||||
{
|
||||
var caps = capabilities.codecs;
|
||||
return codecs
|
||||
.Where(codec => codec != null)
|
||||
.Select(codec => caps.FirstOrDefault(cap => codec.Equals(cap)))
|
||||
.Where(cap => cap != null);
|
||||
}
|
||||
public static IEnumerable<RTCRtpCodecCapability> SelectCodecCapabilities(this RTCRtpCapabilities capabilities, IEnumerable<AudioCodecInfo> codecs)
|
||||
{
|
||||
var caps = capabilities.codecs;
|
||||
return codecs
|
||||
.Where(codec => codec != null)
|
||||
.Select(codec => caps.FirstOrDefault(cap => codec.Equals(cap)))
|
||||
.Where(cap => cap != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Codec.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Codec.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b406692f7c30444a94245fd5ce14112
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
280
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/CommandLineParser.cs
vendored
Normal file
280
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/CommandLineParser.cs
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Unity.RenderStreaming.Signaling;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
[Serializable]
|
||||
internal struct CommandLineInfo
|
||||
{
|
||||
public string signalingType;
|
||||
public string signalingUrl;
|
||||
public RTCIceServer[] iceServers;
|
||||
public string pollingInterval;
|
||||
}
|
||||
|
||||
static class CommandLineParser
|
||||
{
|
||||
internal static readonly StringArgument SignalingUrl = new StringArgument("-signalingUrl");
|
||||
internal static readonly StringArgument SignalingType = new StringArgument("-signalingType");
|
||||
internal static readonly StringArrayArgument IceServerUrls = new StringArrayArgument("-iceServerUrl");
|
||||
internal static readonly StringArgument IceServerUsername = new StringArgument("-iceServerUsername");
|
||||
internal static readonly StringArgument IceServerCredential = new StringArgument("-iceServerCredential");
|
||||
internal static readonly EnumArgument<IceCredentialType> IceServerCredentialType = new EnumArgument<IceCredentialType>("-iceServerCredentialType");
|
||||
internal static readonly IntArgument PollingInterval = new IntArgument("-pollingInterval");
|
||||
internal static readonly JsonFileArgument<CommandLineInfo> ImportJson = new JsonFileArgument<CommandLineInfo>("-importJson");
|
||||
|
||||
static readonly List<IArgument> options = new List<IArgument>() { SignalingUrl, SignalingType, IceServerUrls, IceServerUsername, IceServerCredential, IceServerCredentialType, PollingInterval, ImportJson };
|
||||
|
||||
internal delegate bool TryParseDelegate<T>(string[] arguments, string argumentName, out T result);
|
||||
|
||||
internal interface IArgument
|
||||
{
|
||||
bool TryParse(string[] arguments);
|
||||
}
|
||||
|
||||
internal abstract class BaseArgument<T> : IArgument
|
||||
{
|
||||
/// <summary>
|
||||
/// A switch that will either retrieve the argument using the resolver, or use the readonly
|
||||
/// argument name string set on construction.
|
||||
/// </summary>
|
||||
public string ArgumentName { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Defined => m_defined;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public readonly bool Required;
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public T Value => m_value;
|
||||
|
||||
protected bool m_defined;
|
||||
protected T m_value;
|
||||
readonly TryParseDelegate<T> m_parser;
|
||||
|
||||
protected abstract bool DefaultParser(string[] arguments, string argumentName, out T parsedResult);
|
||||
|
||||
public bool TryParse(string[] arguments)
|
||||
{
|
||||
m_defined =
|
||||
m_parser != null &&
|
||||
m_parser(arguments, ArgumentName, out m_value);
|
||||
return m_defined;
|
||||
}
|
||||
|
||||
internal BaseArgument(string argumentName, bool required = false)
|
||||
{
|
||||
Required = required;
|
||||
ArgumentName = argumentName;
|
||||
m_parser = DefaultParser;
|
||||
}
|
||||
|
||||
internal BaseArgument(string argumentName, TryParseDelegate<T> tryParseDelegate, bool required = false)
|
||||
{
|
||||
Required = required;
|
||||
ArgumentName = argumentName;
|
||||
m_parser = tryParseDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
internal class StringArgument : BaseArgument<string>
|
||||
{
|
||||
protected override bool DefaultParser(string[] arguments, string argumentName, out string parsedResult) => TryParseStringArgument(arguments, argumentName, out parsedResult, Required);
|
||||
|
||||
internal StringArgument(string argumentName, bool required = false) : base(argumentName, required) { }
|
||||
internal StringArgument(string argumentName, TryParseDelegate<string> tryParse, bool required = false) : base(argumentName, tryParse, required) { }
|
||||
|
||||
public static implicit operator string(StringArgument argument) => !argument.Defined ? null : argument.Value;
|
||||
}
|
||||
|
||||
internal class EnumArgument<T> : BaseArgument<T?> where T : struct
|
||||
{
|
||||
protected override bool DefaultParser(string[] arguments, string argumentName, out T? parsedResult) => TryParseEnumArgument(arguments, argumentName, out parsedResult, Required);
|
||||
|
||||
internal EnumArgument(string argumentName, bool required = false) : base(argumentName, required) { }
|
||||
|
||||
public static implicit operator T?(EnumArgument<T> argument) => !argument.Defined ? null : argument.Value;
|
||||
}
|
||||
|
||||
internal class StringArrayArgument : BaseArgument<string[]>
|
||||
{
|
||||
protected override bool DefaultParser(string[] arguments, string argumentName, out string[] parsedResult) => TryParseStringArrayArgument(arguments, argumentName, out parsedResult, Required);
|
||||
|
||||
internal StringArrayArgument(string argumentName, bool required = false) : base(argumentName, required) { }
|
||||
|
||||
public static implicit operator string[](StringArrayArgument argument) => !argument.Defined ? null : argument.Value;
|
||||
}
|
||||
|
||||
|
||||
internal class IntArgument : BaseArgument<int?>
|
||||
{
|
||||
protected override bool DefaultParser(string[] arguments, string argumentName, out int? parsedResult) => TryParseIntArgument(arguments, argumentName, out parsedResult, Required);
|
||||
|
||||
internal IntArgument(string argumentName, bool required = false) : base(argumentName, required) { }
|
||||
|
||||
public static implicit operator int?(IntArgument argument) => !argument.Defined ? null : argument.Value;
|
||||
}
|
||||
|
||||
internal class JsonFileArgument<T> : BaseArgument<T?> where T : struct
|
||||
{
|
||||
protected override bool DefaultParser(string[] arguments, string argumentName, out T? parsedResult) => TryParseJsonFileArgument(arguments, argumentName, out parsedResult, Required);
|
||||
|
||||
internal JsonFileArgument(string argumentName, bool required = false) : base(argumentName, required) { }
|
||||
|
||||
public static implicit operator T?(JsonFileArgument<T> argument) => !argument.Defined ? null : argument.Value;
|
||||
}
|
||||
|
||||
static bool TryParseStringArgument(string[] arguments, string argumentName, out string argumentValue, bool required = false)
|
||||
{
|
||||
var startIndex = System.Array.FindIndex(arguments, x => x == argumentName);
|
||||
if (startIndex < 0)
|
||||
{
|
||||
argumentValue = null;
|
||||
return !required;
|
||||
}
|
||||
|
||||
if (startIndex + 1 >= arguments.Length)
|
||||
{
|
||||
argumentValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
argumentValue = arguments[startIndex + 1];
|
||||
return !string.IsNullOrEmpty(argumentValue);
|
||||
}
|
||||
|
||||
static bool TryParseEnumArgument<T>(string[] arguments, string argumentName, out T? argumentValue,
|
||||
bool required = false) where T : struct
|
||||
{
|
||||
bool result = TryParseStringArgument(arguments, argumentName, out string value, required);
|
||||
if (result && !string.IsNullOrEmpty(value))
|
||||
{
|
||||
if (Enum.TryParse(value, true, out T enumValue))
|
||||
{
|
||||
argumentValue = enumValue;
|
||||
return true;
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
argumentValue = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static bool TryParseIntArgument(string[] arguments, string argumentName, out int? argumentValue, bool required = false)
|
||||
{
|
||||
var startIndex = System.Array.FindIndex(arguments, x => x == argumentName);
|
||||
if (startIndex < 0)
|
||||
{
|
||||
argumentValue = null;
|
||||
return !required;
|
||||
}
|
||||
|
||||
if (startIndex + 1 >= arguments.Length)
|
||||
{
|
||||
argumentValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!int.TryParse(arguments[startIndex + 1], out var result))
|
||||
{
|
||||
argumentValue = null;
|
||||
return false;
|
||||
}
|
||||
argumentValue = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryParseStringArrayArgument(string[] arguments, string argumentName, out string[] argumentValue, bool required = false)
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
|
||||
for (int i = 0; i < arguments.Length;)
|
||||
{
|
||||
var startIndex = Array.FindIndex(arguments, i, x => x == argumentName);
|
||||
if (startIndex < 0)
|
||||
break;
|
||||
if (startIndex + 1 >= arguments.Length)
|
||||
break;
|
||||
list.Add(arguments[startIndex + 1]);
|
||||
i = startIndex + 2;
|
||||
}
|
||||
if (list.Count == 0)
|
||||
{
|
||||
argumentValue = null;
|
||||
return !required;
|
||||
}
|
||||
argumentValue = list.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryParseJsonFileArgument<T>(string[] arguments, string argumentName, out T? argumentValue,
|
||||
bool required = false) where T : struct
|
||||
{
|
||||
bool result = TryParseFilePathArgument(arguments, argumentName, out string value);
|
||||
if (result && !string.IsNullOrEmpty(value))
|
||||
{
|
||||
string text = File.ReadAllText(value);
|
||||
try
|
||||
{
|
||||
argumentValue = JsonUtility.FromJson<T>(text);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else if (required)
|
||||
result = false;
|
||||
argumentValue = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool TryParseFilePathArgument(string[] arguments, string argumentName, out string argumentValue)
|
||||
{
|
||||
bool ret = TryParseStringArgument(arguments, argumentName, out string value);
|
||||
if (!ret)
|
||||
{
|
||||
argumentValue = null;
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
argumentValue = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!File.Exists(value))
|
||||
{
|
||||
argumentValue = null;
|
||||
return false;
|
||||
}
|
||||
argumentValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool TryParse(string[] arguments)
|
||||
{
|
||||
foreach (var option in options)
|
||||
{
|
||||
if (!option.TryParse(arguments))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/CommandLineParser.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/CommandLineParser.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69acbbff893ba5840937e10e7b325365
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
140
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DataChannelBase.cs
vendored
Normal file
140
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DataChannelBase.cs
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class DataChannelBase : MonoBehaviour, IDataChannel
|
||||
{
|
||||
internal const string LocalPropertyName = nameof(local);
|
||||
internal const string LabelPropertyName = nameof(label);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
protected bool local = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
protected string label;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsLocal => local;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Label => label;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsConnected => Channel != null && Channel.ReadyState == RTCDataChannelState.Open;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ConnectionId { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public RTCDataChannel Channel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStartedChannelHandler OnStartedChannel { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStoppedChannelHandler OnStoppedChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="channel"></param>
|
||||
public virtual void SetChannel(string connectionId, RTCDataChannel channel)
|
||||
{
|
||||
Channel = channel;
|
||||
if (Channel == null)
|
||||
{
|
||||
ConnectionId = String.Empty;
|
||||
OnStoppedChannel?.Invoke(connectionId);
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionId = connectionId;
|
||||
label = Channel.Label;
|
||||
Channel.OnOpen += () => { OnOpen(connectionId); };
|
||||
Channel.OnClose += () => { OnClose(connectionId); };
|
||||
Channel.OnMessage += OnMessage;
|
||||
|
||||
if (Channel.ReadyState == RTCDataChannelState.Open && !IsLocal)
|
||||
{
|
||||
OnStartedChannel?.Invoke(connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public virtual void Send(byte[] msg)
|
||||
{
|
||||
Channel.Send(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public virtual void Send(string msg)
|
||||
{
|
||||
Channel.Send(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public virtual void SetChannel(SignalingEventData data)
|
||||
{
|
||||
SetChannel(data.connectionId, data.channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
protected virtual void OnMessage(byte[] bytes)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
protected virtual void OnOpen(string connectionId)
|
||||
{
|
||||
OnStartedChannel?.Invoke(connectionId);
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
protected virtual void OnClose(string connectionId)
|
||||
{
|
||||
OnStoppedChannel?.Invoke(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DataChannelBase.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DataChannelBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3103cec563af274798002613a5fe0fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DateTimeExtension.cs
vendored
Normal file
20
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DateTimeExtension.cs
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
static class DateTimeExtension
|
||||
{
|
||||
private static readonly long DatetimeMinTimeTicks =
|
||||
(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;
|
||||
|
||||
/// <summary>
|
||||
/// It returns Javascript format timestamp
|
||||
/// </summary>
|
||||
/// <param name="dt"></param>
|
||||
/// <returns></returns>
|
||||
public static long ToJsMilliseconds(this DateTime dt)
|
||||
{
|
||||
return (long)((dt.ToUniversalTime().Ticks - DatetimeMinTimeTicks) / 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DateTimeExtension.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/DateTimeExtension.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 980af3c21e9301b47859d10e42ce95cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
102
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ExecuteSignalingEvents.cs
vendored
Normal file
102
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ExecuteSignalingEvents.cs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
static class ExecuteSignalingEvents
|
||||
{
|
||||
public static T ValidateEventData<T>(BaseEventData data) where T : class
|
||||
{
|
||||
if ((data as T) == null)
|
||||
throw new ArgumentException(
|
||||
$"Invalid type: {data.GetType()} passed to event expecting {typeof(T)}");
|
||||
return data as T;
|
||||
}
|
||||
|
||||
private static readonly ExecuteEvents.EventFunction<ICreatedConnectionHandler>
|
||||
s_CreatedConnectionHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IDeletedConnectionHandler>
|
||||
s_DeletedConnectionHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IConnectHandler>
|
||||
s_ConnectHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IDisconnectHandler>
|
||||
s_DisconnectHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IOfferHandler>
|
||||
s_OfferHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IAnswerHandler>
|
||||
s_AnswerHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IAddChannelHandler>
|
||||
s_AddChannelHandler = Execute;
|
||||
private static readonly ExecuteEvents.EventFunction<IAddReceiverHandler>
|
||||
s_AddReceiverHandler = Execute;
|
||||
|
||||
private static void Execute(ICreatedConnectionHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnCreatedConnection(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IDeletedConnectionHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnDeletedConnection(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IConnectHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnConnect(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IDisconnectHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnDisconnect(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IOfferHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnOffer(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IAnswerHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnAnswer(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IAddChannelHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnAddChannel(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
private static void Execute(IAddReceiverHandler handler, BaseEventData eventData)
|
||||
{
|
||||
handler.OnAddReceiver(ValidateEventData<SignalingEventData>(eventData));
|
||||
}
|
||||
|
||||
public static ExecuteEvents.EventFunction<ICreatedConnectionHandler> createdConnectionHandler
|
||||
{
|
||||
get { return s_CreatedConnectionHandler; }
|
||||
}
|
||||
|
||||
public static ExecuteEvents.EventFunction<IDeletedConnectionHandler> deletedConnectionHandler
|
||||
{
|
||||
get { return s_DeletedConnectionHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IConnectHandler> connectHandler
|
||||
{
|
||||
get { return s_ConnectHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IDisconnectHandler> disconnectHandler
|
||||
{
|
||||
get { return s_DisconnectHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IOfferHandler> offerHandler
|
||||
{
|
||||
get { return s_OfferHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IAnswerHandler> answerHandler
|
||||
{
|
||||
get { return s_AnswerHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IAddChannelHandler> addChannelHandler
|
||||
{
|
||||
get { return s_AddChannelHandler; }
|
||||
}
|
||||
public static ExecuteEvents.EventFunction<IAddReceiverHandler> addReceiverHandler
|
||||
{
|
||||
get { return s_AddReceiverHandler; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ExecuteSignalingEvents.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ExecuteSignalingEvents.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b79f434f6f27c41469614ea531e9c3f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
142
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/IRenderStreamingHandler.cs
vendored
Normal file
142
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/IRenderStreamingHandler.cs
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.WebRTC;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
public interface IRenderStreamingDelegate
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action onStart;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string> onCreatedConnection;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string> onDeletedConnection;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string, string> onGotOffer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string, string> onGotAnswer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string> onConnect;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string> onDisconnect;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string, RTCRtpTransceiver> onAddTransceiver;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string, RTCDataChannel> onAddChannel;
|
||||
}
|
||||
|
||||
public interface IRenderStreamingHandler
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
void CreateConnection(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
void DeleteConnection(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
bool ExistConnection(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
bool IsConnected(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
/// <returns></returns>
|
||||
bool IsStable(string connection);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="label"></param>
|
||||
/// <returns></returns>
|
||||
RTCDataChannel CreateChannel(string connectionId, string label = null);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
void SendOffer(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
void SendAnswer(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="track"></param>
|
||||
/// <param name="init"></param>
|
||||
/// <returns></returns>
|
||||
RTCRtpTransceiver AddTransceiver(string connectionId, MediaStreamTrack track, RTCRtpTransceiverInit init = null);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="kind"></param>
|
||||
/// <param name="init"></param>
|
||||
/// <returns></returns>
|
||||
RTCRtpTransceiver AddTransceiver(string connectionId, TrackKind kind, RTCRtpTransceiverInit init = null);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="track"></param>
|
||||
void RemoveSenderTrack(string connectionId, MediaStreamTrack track);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<RTCRtpTransceiver> GetTransceivers(string connectionId);
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/IRenderStreamingHandler.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/IRenderStreamingHandler.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 196a2af0d323dc84eaa05e0ef17e353a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ISignalingEventHandler.cs
vendored
Normal file
44
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ISignalingEventHandler.cs
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
public interface ICreatedConnectionHandler : IEventSystemHandler
|
||||
{
|
||||
void OnCreatedConnection(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IDeletedConnectionHandler : IEventSystemHandler
|
||||
{
|
||||
void OnDeletedConnection(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IConnectHandler : IEventSystemHandler
|
||||
{
|
||||
void OnConnect(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IDisconnectHandler : IEventSystemHandler
|
||||
{
|
||||
void OnDisconnect(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IOfferHandler : IEventSystemHandler
|
||||
{
|
||||
void OnOffer(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IAnswerHandler : IEventSystemHandler
|
||||
{
|
||||
void OnAnswer(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IAddChannelHandler : IEventSystemHandler
|
||||
{
|
||||
void OnAddChannel(SignalingEventData eventData);
|
||||
}
|
||||
|
||||
public interface IAddReceiverHandler : IEventSystemHandler
|
||||
{
|
||||
void OnAddReceiver(SignalingEventData eventData);
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ISignalingEventHandler.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/ISignalingEventHandler.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 655995007f21f084bb939c19af8254e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputChannelReceiverBase.cs
vendored
Normal file
18
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputChannelReceiverBase.cs
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class InputChannelReceiverBase : DataChannelBase
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
#pragma warning disable 0067
|
||||
public virtual event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
#pragma warning restore 0067
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputChannelReceiverBase.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputChannelReceiverBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf9e39c7e534d094190e759af67eb7bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputPositionCorrector.cs
vendored
Normal file
60
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputPositionCorrector.cs
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
class InputPositionCorrector
|
||||
{
|
||||
public Rect inputRegion { set; get; }
|
||||
|
||||
public Rect outputRegion { set; get; }
|
||||
|
||||
private Action<InputEventPtr, InputDevice> _onEvent;
|
||||
|
||||
public InputPositionCorrector(Action<InputEventPtr, InputDevice> onEvent)
|
||||
{
|
||||
_onEvent = onEvent;
|
||||
}
|
||||
|
||||
public unsafe void Invoke(InputEventPtr ptr, InputDevice device)
|
||||
{
|
||||
// Allocate memory and copy InputEventPtr
|
||||
InputEventPtr dst = (InputEventPtr)
|
||||
UnsafeUtility.Malloc(ptr.sizeInBytes, 4, Collections.Allocator.Temp);
|
||||
UnsafeUtility.MemCpy(dst, ptr, ptr.sizeInBytes);
|
||||
|
||||
// Mapping
|
||||
PointerMap((StateEvent*)dst.data, device);
|
||||
|
||||
_onEvent?.Invoke(dst, device);
|
||||
|
||||
// Free memory
|
||||
UnsafeUtility.Free(dst, Collections.Allocator.Temp);
|
||||
}
|
||||
|
||||
unsafe void PointerMap(StateEvent* data, InputDevice device)
|
||||
{
|
||||
switch (device)
|
||||
{
|
||||
case Mouse mouse:
|
||||
MouseState* mouseState = (MouseState*)data->state;
|
||||
mouseState->position = Map(mouseState->position, inputRegion, outputRegion);
|
||||
break;
|
||||
case Touchscreen touch:
|
||||
// todo(kazuki): multi touch is not supported yet.
|
||||
TouchState* touchState = (TouchState*)data->state;
|
||||
touchState->position = Map(touchState->position, inputRegion, outputRegion);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector2 Map(Vector2 pos, Rect inputRegion, Rect outputRegion)
|
||||
{
|
||||
Vector2 normalized = Rect.PointToNormalized(inputRegion, pos);
|
||||
return Rect.NormalizedToPoint(outputRegion, normalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputPositionCorrector.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputPositionCorrector.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f28e3e5eb23050469ff498aa7d75934
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
507
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputReceiver.cs
vendored
Normal file
507
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputReceiver.cs
vendored
Normal file
@@ -0,0 +1,507 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Unity.RenderStreaming.InputSystem;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Users;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using InputRemoting = Unity.RenderStreaming.InputSystem.InputRemoting;
|
||||
using Inputs = UnityEngine.InputSystem.InputSystem;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a separate player in the game complete with a set of actions exclusive
|
||||
/// to the player and a set of paired device.
|
||||
/// It is the simple version of UnityEngine.InputSystem.PlayerInput that removing dependency of InputControlScheme.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Render Streaming/Input Receiver")]
|
||||
public class InputReceiver : InputChannelReceiverBase
|
||||
{
|
||||
internal const string ActionsPropertyName = nameof(m_Actions);
|
||||
internal const string ActionEventsPropertyName = nameof(m_ActionEvents);
|
||||
internal const string DefaultActionMapPropertyName = nameof(m_DefaultActionMap);
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a device changes.
|
||||
/// </summary>
|
||||
public override event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the input action asset associated with the player.
|
||||
/// </summary>
|
||||
public InputActionAsset actions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_ActionsInitialized && gameObject.activeSelf)
|
||||
InitializeActions();
|
||||
return m_Actions;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_Actions == value)
|
||||
return;
|
||||
|
||||
// Make sure that if we already have actions, they get disabled.
|
||||
if (m_Actions != null)
|
||||
{
|
||||
m_Actions.Disable();
|
||||
if (m_Enabled)
|
||||
UninitializeActions();
|
||||
}
|
||||
|
||||
m_Actions = value;
|
||||
|
||||
if (m_Enabled)
|
||||
{
|
||||
//ClearCaches();
|
||||
AssignUserAndDevices();
|
||||
InitializeActions();
|
||||
if (m_InputActive)
|
||||
ActivateInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the input is currently active.
|
||||
/// </summary>
|
||||
public bool inputIsActive => m_InputActive;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default action map.
|
||||
/// </summary>
|
||||
public InputUser user => m_InputUser;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action events associated with the player.
|
||||
/// </summary>
|
||||
public ReadOnlyArray<InputDevice> devices => m_InputUser.pairedDevices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current action map.
|
||||
/// </summary>
|
||||
public InputActionMap currentActionMap
|
||||
{
|
||||
get => m_CurrentActionMap;
|
||||
set
|
||||
{
|
||||
m_CurrentActionMap?.Disable();
|
||||
m_CurrentActionMap = value;
|
||||
m_CurrentActionMap?.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default action map.
|
||||
/// </summary>
|
||||
public string defaultActionMap
|
||||
{
|
||||
get => m_DefaultActionMap;
|
||||
set => m_DefaultActionMap = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action events associated with the player.
|
||||
/// </summary>
|
||||
public ReadOnlyArray<PlayerInput.ActionEvent> actionEvents
|
||||
{
|
||||
get => m_ActionEvents;
|
||||
set
|
||||
{
|
||||
if (m_Enabled)
|
||||
UninitializeActions();
|
||||
|
||||
m_ActionEvents = value.ToArray();
|
||||
|
||||
if (m_Enabled)
|
||||
InitializeActions();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
m_Enabled = true;
|
||||
onDeviceChange += OnDeviceChange;
|
||||
|
||||
//AssignPlayerIndex();
|
||||
InitializeActions();
|
||||
AssignUserAndDevices();
|
||||
ActivateInput();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
m_Enabled = false;
|
||||
onDeviceChange -= OnDeviceChange;
|
||||
|
||||
DeactivateInput();
|
||||
UnassignUserAndDevices();
|
||||
UninitializeActions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates input for the player.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// inputReceiver.ActivateInput();
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public void ActivateInput()
|
||||
{
|
||||
m_InputActive = true;
|
||||
|
||||
// If we have no current action map but there's a default
|
||||
// action map, make it current.
|
||||
if (m_CurrentActionMap == null && m_Actions != null && !string.IsNullOrEmpty(m_DefaultActionMap))
|
||||
SwitchCurrentActionMap(m_DefaultActionMap);
|
||||
else
|
||||
m_CurrentActionMap?.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates input for the player.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// inputReceiver.DeactivateInput();
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public void DeactivateInput()
|
||||
{
|
||||
m_CurrentActionMap?.Disable();
|
||||
|
||||
m_InputActive = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches the current action map to the one with the given name or ID.
|
||||
/// </summary>
|
||||
/// <param name="mapNameOrId">The name or ID of the action map to switch to.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// inputReceiver.SwitchCurrentActionMap("Gameplay");
|
||||
///]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
|
||||
public void SwitchCurrentActionMap(string mapNameOrId)
|
||||
{
|
||||
// Must be enabled.
|
||||
if (!m_Enabled)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, (object)$"Cannot switch to actions '{mapNameOrId}'; input is not enabled", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Must have actions.
|
||||
if (m_Actions == null)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, (object)$"Cannot switch to actions '{mapNameOrId}'; no actions set on PlayerInput", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Must have map.
|
||||
var actionMap = m_Actions.FindActionMap(mapNameOrId);
|
||||
if (actionMap == null)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, (object)$"Cannot find action map '{mapNameOrId}' in actions '{m_Actions}'", this);
|
||||
return;
|
||||
}
|
||||
|
||||
currentActionMap = actionMap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs pairing with the specified input device.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var playerInput = hostPlayer.GetComponent<InputReceiver>();
|
||||
/// playerInput.PerformPairingWithDevice(device);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="device">The input device to pair with.</param>
|
||||
public void PerformPairingWithDevice(InputDevice device)
|
||||
{
|
||||
m_InputUser = InputUser.PerformPairingWithDevice(device, m_InputUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs pairing with all local devices.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var playerInput = hostPlayer.GetComponent<InputReceiver>();
|
||||
/// playerInput.PerformPairingWithAllLocalDevices();
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public void PerformPairingWithAllLocalDevices()
|
||||
{
|
||||
foreach (var device in Inputs.devices.Where(_ => !_.remote))
|
||||
{
|
||||
PerformPairingWithDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpairs the input user with the given device.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var playerInput = hostPlayer.GetComponent<InputReceiver>();
|
||||
/// playerInput.UnpairDevices(device);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="device">The device to unpair.</param>
|
||||
public void UnpairDevices(InputDevice device)
|
||||
{
|
||||
if (!m_InputUser.valid)
|
||||
return;
|
||||
m_InputUser.UnpairDevice(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the RTCDataChannel for the sender.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// public void OnAddChannel(SignalingEventData data)
|
||||
/// {
|
||||
/// var obj = dictObj[data.connectionId];
|
||||
/// var channels = obj.GetComponentsInChildren<IDataChannel>();
|
||||
/// var channel = channels.FirstOrDefault(_ => !_.IsLocal && !_.IsConnected);
|
||||
/// channel?.SetChannel(data);
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="connectionId">The connection ID.</param>
|
||||
/// <param name="channel">The RTCDataChannel to set.</param>
|
||||
public override void SetChannel(string connectionId, RTCDataChannel channel)
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
receiver = new Receiver(channel);
|
||||
receiver.onDeviceChange += onDeviceChange;
|
||||
receiverInput = new InputRemoting(receiver);
|
||||
subscriberDisposer = receiverInput.Subscribe(receiverInput);
|
||||
receiverInput.StartSending();
|
||||
}
|
||||
base.SetChannel(connectionId, channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the input region based on the given texture size and region in world coordinates.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var (region, size) = remoteVideoImage.GetRegionAndSize();
|
||||
/// inputReceiver.CalculateInputRegion(region, size);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="region">The region of the texture in world coordinate system.</param>
|
||||
/// <param name="size">The size of the texture.</param>
|
||||
public void CalculateInputRegion(Vector2Int size, Rect region)
|
||||
{
|
||||
receiver.CalculateInputRegion(new Rect(Vector2.zero, size), region);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables input position correction.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// inputReceiver.EnableInputPositionCorrection(true);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="enabled">True to enable input position correction, false to disable.</param>
|
||||
public void SetEnableInputPositionCorrection(bool enabled)
|
||||
{
|
||||
receiver.EnableInputPositionCorrection = enabled;
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected virtual void Dispose()
|
||||
{
|
||||
receiverInput?.StopSending();
|
||||
subscriberDisposer?.Dispose();
|
||||
receiver?.Dispose();
|
||||
receiver = null;
|
||||
}
|
||||
|
||||
[Tooltip("Input actions associated with the player.")]
|
||||
[SerializeField] internal InputActionAsset m_Actions;
|
||||
[SerializeField] internal PlayerInput.ActionEvent[] m_ActionEvents;
|
||||
[SerializeField] internal string m_DefaultActionMap;
|
||||
|
||||
[NonSerialized] internal InputActionMap m_CurrentActionMap;
|
||||
|
||||
[NonSerialized] private bool m_InputActive;
|
||||
[NonSerialized] private bool m_Enabled;
|
||||
[NonSerialized] private bool m_ActionsInitialized;
|
||||
[NonSerialized] private InputUser m_InputUser;
|
||||
|
||||
[NonSerialized] private Receiver receiver;
|
||||
[NonSerialized] private InputRemoting receiverInput;
|
||||
[NonSerialized] private IDisposable subscriberDisposer;
|
||||
|
||||
private void AssignUserAndDevices()
|
||||
{
|
||||
// If we already have a user at this point, clear out all its paired devices
|
||||
// to start the pairing process from scratch.
|
||||
if (m_InputUser.valid)
|
||||
m_InputUser.UnpairDevices();
|
||||
|
||||
// All our input goes through actions so there's no point setting
|
||||
// anything up if we have none.
|
||||
if (m_Actions == null)
|
||||
{
|
||||
// Make sure user is invalid.
|
||||
m_InputUser = new InputUser();
|
||||
return;
|
||||
}
|
||||
m_InputUser = InputUser.CreateUserWithoutPairedDevices();
|
||||
|
||||
// If we don't have a valid user at this point, we don't have any paired devices.
|
||||
if (m_InputUser.valid)
|
||||
m_InputUser.AssociateActionsWithUser(actions);
|
||||
}
|
||||
|
||||
private void UnassignUserAndDevices()
|
||||
{
|
||||
m_InputUser.UnpairDevicesAndRemoveUser();
|
||||
}
|
||||
|
||||
private void InitializeActions()
|
||||
{
|
||||
if (m_ActionsInitialized)
|
||||
return;
|
||||
if (m_Actions == null)
|
||||
return;
|
||||
|
||||
var oldActions = m_Actions;
|
||||
m_Actions = Instantiate(m_Actions);
|
||||
for (var actionMap = 0; actionMap < oldActions.actionMaps.Count; actionMap++)
|
||||
{
|
||||
for (var binding = 0; binding < oldActions.actionMaps[actionMap].bindings.Count; binding++)
|
||||
m_Actions.actionMaps[actionMap].ApplyBindingOverride(binding, oldActions.actionMaps[actionMap].bindings[binding]);
|
||||
}
|
||||
|
||||
// Hook up all action events.
|
||||
if (m_ActionEvents != null)
|
||||
{
|
||||
foreach (var actionEvent in m_ActionEvents)
|
||||
{
|
||||
var id = actionEvent.actionId;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
continue;
|
||||
|
||||
// Find action for event.
|
||||
var action = m_Actions.FindAction(id);
|
||||
if (action != null)
|
||||
{
|
||||
////REVIEW: really wish we had a single callback
|
||||
action.performed += actionEvent.Invoke;
|
||||
action.canceled += actionEvent.Invoke;
|
||||
action.started += actionEvent.Invoke;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot find action. Log error.
|
||||
if (!string.IsNullOrEmpty(actionEvent.actionName))
|
||||
{
|
||||
// We have an action name. Show in message.
|
||||
RenderStreaming.Logger.Log(LogType.Error,
|
||||
(object)$"Cannot find action '{actionEvent.actionName}' with ID '{actionEvent.actionId}' in '{m_Actions}",
|
||||
this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no action name. Best we have is ID.
|
||||
RenderStreaming.Logger.Log(LogType.Error,
|
||||
(object)$"Cannot find action with ID '{actionEvent.actionId}' in '{m_Actions}",
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_ActionsInitialized = true;
|
||||
}
|
||||
|
||||
private void UninitializeActions()
|
||||
{
|
||||
if (!m_ActionsInitialized)
|
||||
return;
|
||||
if (m_Actions == null)
|
||||
return;
|
||||
|
||||
//UninstallOnActionTriggeredHook();
|
||||
|
||||
if (m_ActionEvents != null)
|
||||
{
|
||||
foreach (var actionEvent in m_ActionEvents)
|
||||
{
|
||||
var id = actionEvent.actionId;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
continue;
|
||||
|
||||
// Find action for event.
|
||||
var action = m_Actions.FindAction(id);
|
||||
if (action != null)
|
||||
{
|
||||
////REVIEW: really wish we had a single callback
|
||||
action.performed -= actionEvent.Invoke;
|
||||
action.canceled -= actionEvent.Invoke;
|
||||
action.started -= actionEvent.Invoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_CurrentActionMap = null;
|
||||
m_ActionsInitialized = false;
|
||||
}
|
||||
|
||||
protected virtual void OnDeviceChange(InputDevice device, InputDeviceChange change)
|
||||
{
|
||||
switch (change)
|
||||
{
|
||||
case InputDeviceChange.Added:
|
||||
PerformPairingWithDevice(device);
|
||||
return;
|
||||
case InputDeviceChange.Removed:
|
||||
UnpairDevices(device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputReceiver.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputReceiver.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: decb088abd7ec9747b6e9a1011c046b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
110
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSender.cs
vendored
Normal file
110
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSender.cs
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using Unity.RenderStreaming.InputSystem;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// The InputSender component is responsible for sending input data over a data channel in a Unity Render Streaming context.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputRemoting" />
|
||||
[AddComponentMenu("Render Streaming/Input Sender")]
|
||||
public class InputSender : DataChannelBase
|
||||
{
|
||||
private Sender sender;
|
||||
private InputRemoting senderInput;
|
||||
private IDisposable suscriberDisposer;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the RTCDataChannel for the sender.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// public void OnAddChannel(SignalingEventData data)
|
||||
/// {
|
||||
/// var obj = dictObj[data.connectionId];
|
||||
/// var channels = obj.GetComponentsInChildren<IDataChannel>();
|
||||
/// var channel = channels.FirstOrDefault(_ => !_.IsLocal && !_.IsConnected);
|
||||
/// channel?.SetChannel(data);
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="connectionId">The connection ID.</param>
|
||||
/// <param name="channel">The RTCDataChannel to set.</param>
|
||||
public override void SetChannel(string connectionId, RTCDataChannel channel)
|
||||
{
|
||||
if (channel == null)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
sender = new Sender();
|
||||
senderInput = new InputRemoting(sender);
|
||||
suscriberDisposer = senderInput.Subscribe(new Observer(channel));
|
||||
channel.OnOpen += OnOpen;
|
||||
channel.OnClose += OnClose;
|
||||
}
|
||||
base.SetChannel(connectionId, channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the input region based on the given texture size and region in world coordinates.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// var (region, size) = remoteVideoImage.GetRegionAndSize();
|
||||
/// inputSender.CalculateInputResion(region, size);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="region">The region of the texture in world coordinate system.</param>
|
||||
/// <param name="size">The size of the texture.</param>
|
||||
public void CalculateInputResion(Rect region, Vector2Int size)
|
||||
{
|
||||
sender.CalculateInputRegion(region, new Rect(Vector2.zero, size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables input position correction.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// inputSender.EnableInputPositionCorrection(true);
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="enabled">True to enable input position correction, false to disable.</param>
|
||||
public void EnableInputPositionCorrection(bool enabled)
|
||||
{
|
||||
sender.EnableInputPositionCorrection = enabled;
|
||||
}
|
||||
|
||||
void OnOpen()
|
||||
{
|
||||
senderInput.StartSending();
|
||||
}
|
||||
void OnClose()
|
||||
{
|
||||
senderInput.StopSending();
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
protected void Dispose()
|
||||
{
|
||||
senderInput?.StopSending();
|
||||
suscriberDisposer?.Dispose();
|
||||
sender?.Dispose();
|
||||
sender = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSender.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSender.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e6d9188eb6318c488077f3c88318a65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem.meta
vendored
Normal file
8
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem.meta
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1090d262b48b78349954719a7846d309
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
111
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/ArrayHelpers.cs
vendored
Normal file
111
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/ArrayHelpers.cs
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// note:: This script is using code snippets in InputSystem.
|
||||
// https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Utilities/ArrayHelpers.cs
|
||||
// todo(kazuki):: This script should be moved into the WebRTC package.
|
||||
// #if UNITY_WEBRTC_ENABLE_INPUT_SYSTEM
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
// namespace Unity.WebRTC.InputSystem
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal static class ArrayHelpers
|
||||
{
|
||||
public static int LengthSafe<TValue>(this TValue[] array)
|
||||
{
|
||||
if (array == null)
|
||||
return 0;
|
||||
return array.Length;
|
||||
}
|
||||
|
||||
public static int Append<TValue>(ref TValue[] array, TValue value)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
array = new TValue[1];
|
||||
array[0] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
var length = array.Length;
|
||||
Array.Resize(ref array, length + 1);
|
||||
array[length] = value;
|
||||
return length;
|
||||
}
|
||||
|
||||
public static int Append<TValue>(ref TValue[] array, IEnumerable<TValue> values)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
array = values.ToArray();
|
||||
return 0;
|
||||
}
|
||||
|
||||
var oldLength = array.Length;
|
||||
var valueCount = values.Count();
|
||||
|
||||
Array.Resize(ref array, oldLength + valueCount);
|
||||
|
||||
var index = oldLength;
|
||||
foreach (var value in values)
|
||||
array[index++] = value;
|
||||
|
||||
return oldLength;
|
||||
}
|
||||
|
||||
public static int IndexOf<TValue>(TValue[] array, TValue value, int startIndex = 0, int count = -1)
|
||||
{
|
||||
if (array == null)
|
||||
return -1;
|
||||
|
||||
if (count < 0)
|
||||
count = array.Length - startIndex;
|
||||
var comparer = EqualityComparer<TValue>.Default;
|
||||
for (var i = startIndex; i < startIndex + count; ++i)
|
||||
if (comparer.Equals(array[i], value))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static bool Erase<TValue>(ref TValue[] array, TValue value)
|
||||
{
|
||||
var index = IndexOf(array, value);
|
||||
if (index != -1)
|
||||
{
|
||||
EraseAt(ref array, index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void EraseAt<TValue>(ref TValue[] array, int index)
|
||||
{
|
||||
Debug.Assert(array != null);
|
||||
Debug.Assert(index >= 0 && index < array.Length);
|
||||
|
||||
var length = array.Length;
|
||||
if (index == 0 && length == 1)
|
||||
{
|
||||
array = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < length - 1)
|
||||
Array.Copy(array, index + 1, array, index, length - index - 1);
|
||||
|
||||
Array.Resize(ref array, length - 1);
|
||||
}
|
||||
|
||||
public static void PutAtIfNotSet<TValue>(ref TValue[] array, int index, Func<TValue> valueFn)
|
||||
{
|
||||
if (array.LengthSafe() < index + 1)
|
||||
Array.Resize(ref array, index + 1);
|
||||
|
||||
if (EqualityComparer<TValue>.Default.Equals(array[index], default(TValue)))
|
||||
array[index] = valueFn();
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/ArrayHelpers.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/ArrayHelpers.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8f0ece9173904d43a03d40edca9effb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
257
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs
vendored
Normal file
257
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/EmulateInputFieldEvent.cs
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
using System.Collections.Generic;
|
||||
#if URS_USE_TEXTMESHPRO
|
||||
using TMPro;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This partial class is for workaround to support Unity UI InputField.
|
||||
/// </summary>
|
||||
partial class Receiver
|
||||
{
|
||||
private static readonly Dictionary<int, KeyCode> s_KeyMap = new Dictionary<int, KeyCode>()
|
||||
{
|
||||
{ (int)Key.Backspace, KeyCode.Backspace },
|
||||
{ (int)Key.Tab, KeyCode.Tab },
|
||||
{ (int)Key.Enter, KeyCode.Return },
|
||||
{ (int)Key.Space, KeyCode.Space },
|
||||
{ (int)Key.Comma, KeyCode.Comma },
|
||||
{ (int)Key.Minus, KeyCode.Minus },
|
||||
{ (int)Key.Period, KeyCode.Period },
|
||||
{ (int)Key.Slash, KeyCode.Slash },
|
||||
{ (int)Key.Digit0, KeyCode.Alpha0 },
|
||||
{ (int)Key.Digit1, KeyCode.Alpha1 },
|
||||
{ (int)Key.Digit2, KeyCode.Alpha2 },
|
||||
{ (int)Key.Digit3, KeyCode.Alpha3 },
|
||||
{ (int)Key.Digit4, KeyCode.Alpha4 },
|
||||
{ (int)Key.Digit5, KeyCode.Alpha5 },
|
||||
{ (int)Key.Digit6, KeyCode.Alpha6 },
|
||||
{ (int)Key.Digit7, KeyCode.Alpha7 },
|
||||
{ (int)Key.Digit8, KeyCode.Alpha8 },
|
||||
{ (int)Key.Digit9, KeyCode.Alpha9 },
|
||||
{ (int)Key.Semicolon, KeyCode.Semicolon },
|
||||
{ (int)Key.Equals, KeyCode.Equals },
|
||||
{ (int)Key.LeftBracket, KeyCode.LeftBracket },
|
||||
{ (int)Key.Backslash, KeyCode.Backslash },
|
||||
{ (int)Key.RightBracket, KeyCode.RightBracket },
|
||||
{ (int)Key.Backquote, KeyCode.BackQuote },
|
||||
{ (int)Key.Quote, KeyCode.Quote },
|
||||
{ (int)Key.A, KeyCode.A },
|
||||
{ (int)Key.B, KeyCode.B },
|
||||
{ (int)Key.C, KeyCode.C },
|
||||
{ (int)Key.D, KeyCode.D },
|
||||
{ (int)Key.E, KeyCode.E },
|
||||
{ (int)Key.F, KeyCode.F },
|
||||
{ (int)Key.G, KeyCode.G },
|
||||
{ (int)Key.H, KeyCode.H },
|
||||
{ (int)Key.I, KeyCode.I },
|
||||
{ (int)Key.J, KeyCode.J },
|
||||
{ (int)Key.K, KeyCode.K },
|
||||
{ (int)Key.L, KeyCode.L },
|
||||
{ (int)Key.M, KeyCode.M },
|
||||
{ (int)Key.N, KeyCode.N },
|
||||
{ (int)Key.O, KeyCode.O },
|
||||
{ (int)Key.P, KeyCode.P },
|
||||
{ (int)Key.Q, KeyCode.Q },
|
||||
{ (int)Key.R, KeyCode.R },
|
||||
{ (int)Key.S, KeyCode.S },
|
||||
{ (int)Key.T, KeyCode.T },
|
||||
{ (int)Key.U, KeyCode.U },
|
||||
{ (int)Key.V, KeyCode.V },
|
||||
{ (int)Key.W, KeyCode.W },
|
||||
{ (int)Key.X, KeyCode.X },
|
||||
{ (int)Key.Y, KeyCode.Y },
|
||||
{ (int)Key.Z, KeyCode.Z },
|
||||
{ (int)Key.F1, KeyCode.F1 },
|
||||
{ (int)Key.F2, KeyCode.F2 },
|
||||
{ (int)Key.F3, KeyCode.F3 },
|
||||
{ (int)Key.F4, KeyCode.F4 },
|
||||
{ (int)Key.F5, KeyCode.F5 },
|
||||
{ (int)Key.F6, KeyCode.F6 },
|
||||
{ (int)Key.F7, KeyCode.F7 },
|
||||
{ (int)Key.F8, KeyCode.F8 },
|
||||
{ (int)Key.F9, KeyCode.F9 },
|
||||
{ (int)Key.F10, KeyCode.F10 },
|
||||
{ (int)Key.F11, KeyCode.F11 },
|
||||
{ (int)Key.F12, KeyCode.F12 },
|
||||
{ (int)Key.None, KeyCode.None },
|
||||
{ (int)Key.LeftArrow, KeyCode.LeftArrow },
|
||||
{ (int)Key.RightArrow, KeyCode.RightArrow },
|
||||
{ (int)Key.UpArrow, KeyCode.UpArrow },
|
||||
{ (int)Key.DownArrow, KeyCode.DownArrow },
|
||||
{ (int)Key.LeftShift, KeyCode.LeftShift },
|
||||
{ (int)Key.RightShift, KeyCode.RightShift },
|
||||
{ (int)Key.Delete, KeyCode.Delete },
|
||||
{ (int)Key.Escape, KeyCode.Escape },
|
||||
{ (int)Key.LeftAlt, KeyCode.LeftAlt },
|
||||
{ (int)Key.RightAlt, KeyCode.RightAlt },
|
||||
{ (int)Key.LeftApple, KeyCode.LeftApple },
|
||||
{ (int)Key.RightApple, KeyCode.RightApple }
|
||||
};
|
||||
|
||||
interface IInputField
|
||||
{
|
||||
void ProcessEvent(Event e);
|
||||
void ForceLabelUpdate();
|
||||
void AppendText(char character);
|
||||
}
|
||||
|
||||
class UGUIInputField : IInputField
|
||||
{
|
||||
InputField m_field;
|
||||
public UGUIInputField(InputField field)
|
||||
{
|
||||
m_field = field;
|
||||
}
|
||||
|
||||
public void ProcessEvent(Event e)
|
||||
{
|
||||
m_field.ProcessEvent(e);
|
||||
}
|
||||
public void ForceLabelUpdate()
|
||||
{
|
||||
m_field.ForceLabelUpdate();
|
||||
}
|
||||
public void AppendText(char character)
|
||||
{
|
||||
m_field.text += character;
|
||||
}
|
||||
}
|
||||
|
||||
#if URS_USE_TEXTMESHPRO
|
||||
class TMProInputField : IInputField
|
||||
{
|
||||
TMP_InputField m_field;
|
||||
public TMProInputField(TMP_InputField field)
|
||||
{
|
||||
m_field = field;
|
||||
}
|
||||
|
||||
public void ProcessEvent(Event e)
|
||||
{
|
||||
m_field.ProcessEvent(e);
|
||||
}
|
||||
public void ForceLabelUpdate()
|
||||
{
|
||||
m_field.ForceLabelUpdate();
|
||||
}
|
||||
public void AppendText(char character)
|
||||
{
|
||||
m_field.text += character;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IInputField FindInputField(GameObject obj)
|
||||
{
|
||||
var field = obj.GetComponent<InputField>();
|
||||
if (field != null)
|
||||
return new UGUIInputField(field);
|
||||
|
||||
#if URS_USE_TEXTMESHPRO
|
||||
var tmpField = obj.GetComponent<TMP_InputField>();
|
||||
if (tmpField != null)
|
||||
return new TMProInputField(tmpField);
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
(EventModifiers, KeyCode) GetEventModifiersAndKeyCode(InputEventPtr ptr)
|
||||
{
|
||||
EventModifiers modifiers = EventModifiers.None;
|
||||
KeyCode keyCode = KeyCode.None;
|
||||
foreach (var control in ptr.GetAllButtonPresses())
|
||||
{
|
||||
if (control is KeyControl keyControl)
|
||||
{
|
||||
var key = keyControl.keyCode;
|
||||
if (key == Key.LeftShift || key == Key.RightShift)
|
||||
{
|
||||
modifiers |= EventModifiers.Shift;
|
||||
}
|
||||
else if (key == Key.LeftCtrl || key == Key.RightCtrl)
|
||||
{
|
||||
modifiers |= EventModifiers.Control;
|
||||
}
|
||||
else if (key == Key.LeftAlt || key == Key.RightAlt)
|
||||
{
|
||||
modifiers |= EventModifiers.Alt;
|
||||
}
|
||||
else if (key == Key.LeftCommand || key == Key.RightCommand)
|
||||
{
|
||||
modifiers |= EventModifiers.Command;
|
||||
}
|
||||
else if (key == Key.CapsLock)
|
||||
{
|
||||
modifiers |= EventModifiers.CapsLock;
|
||||
}
|
||||
else if (s_KeyMap.TryGetValue((int)key, out var value))
|
||||
{
|
||||
keyCode = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (modifiers, keyCode);
|
||||
}
|
||||
|
||||
unsafe Event CreateEvent(InputEventPtr ptr)
|
||||
{
|
||||
var (modifiers, keyCode) = GetEventModifiersAndKeyCode(ptr);
|
||||
|
||||
if (ptr.type == TextEvent.Type)
|
||||
{
|
||||
var textEventPtr = (TextEvent*)ptr.ToPointer();
|
||||
var utf32Char = textEventPtr->character;
|
||||
if (utf32Char >= 0x10000)
|
||||
{
|
||||
// todo: not supported multibyte character.
|
||||
return null;
|
||||
}
|
||||
if (utf32Char < 0x100)
|
||||
{
|
||||
// ignore control
|
||||
if (char.IsControl((char)utf32Char))
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Event
|
||||
{
|
||||
type = EventType.KeyDown,
|
||||
character = (char)utf32Char,
|
||||
keyCode = keyCode,
|
||||
modifiers = modifiers
|
||||
};
|
||||
}
|
||||
|
||||
return new Event
|
||||
{
|
||||
type = EventType.KeyDown,
|
||||
keyCode = keyCode,
|
||||
modifiers = modifiers
|
||||
};
|
||||
}
|
||||
|
||||
private void EmulateInputFieldEvent(InputEventPtr ptr)
|
||||
{
|
||||
var obj = UnityEngine.EventSystems.EventSystem.current?.currentSelectedGameObject;
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
var field = FindInputField(obj);
|
||||
if (field == null)
|
||||
return;
|
||||
Event e = CreateEvent(ptr);
|
||||
if (e != null)
|
||||
{
|
||||
field.ProcessEvent(e);
|
||||
field.ForceLabelUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3d7722788597434b979a31af26668c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
// todo(kazuki)::Avoid to use reflection
|
||||
static class InputDeviceExtension
|
||||
{
|
||||
private static Type typeInputDevice;
|
||||
private static FieldInfo fieldInfoParticipantId;
|
||||
private static FieldInfo fieldInfoDescription;
|
||||
private static FieldInfo fieldInfoDeviceFlags;
|
||||
|
||||
static InputDeviceExtension()
|
||||
{
|
||||
typeInputDevice = typeof(InputDevice);
|
||||
fieldInfoParticipantId = typeInputDevice.GetField("m_ParticipantId",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
fieldInfoDescription = typeInputDevice.GetField("m_Description",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
fieldInfoDeviceFlags = typeInputDevice.GetField("m_DeviceFlags",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
public static void SetParticipantId(this InputDevice device, int value)
|
||||
{
|
||||
fieldInfoParticipantId.SetValue(device, value);
|
||||
}
|
||||
|
||||
public static int GetParticipantId(this InputDevice device)
|
||||
{
|
||||
return (int)fieldInfoParticipantId.GetValue(device);
|
||||
}
|
||||
|
||||
public static void SetDescription(this InputDevice device, InputDeviceDescription value)
|
||||
{
|
||||
fieldInfoDescription.SetValue(device, value);
|
||||
}
|
||||
|
||||
public static void SetDeviceFlags(this InputDevice device, int value)
|
||||
{
|
||||
fieldInfoDeviceFlags.SetValue(device, value);
|
||||
}
|
||||
public static int GetDeviceFlags(this InputDevice device)
|
||||
{
|
||||
return (int)fieldInfoDeviceFlags.GetValue(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9703223ad9b06848b11ed494a9871a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
#if UNITY_EDITOR && !INPUTSYSTEM_1_1_OR_NEWER
|
||||
// todo(kazuki)::Avoid to use reflection
|
||||
static class InputEditorUserSettings
|
||||
{
|
||||
private static Type type;
|
||||
private static PropertyInfo propertyLockInputToGameView;
|
||||
private static MethodInfo methodLoad;
|
||||
private static FieldInfo fieldFilePath;
|
||||
|
||||
static InputEditorUserSettings()
|
||||
{
|
||||
type = Type.GetType("UnityEngine.InputSystem.Editor.InputEditorUserSettings, Unity.InputSystem");
|
||||
propertyLockInputToGameView = type.GetProperty("lockInputToGameView");
|
||||
methodLoad = type.GetMethod("Load",
|
||||
BindingFlags.NonPublic | BindingFlags.Static);
|
||||
fieldFilePath = type.GetField("kSavePath",
|
||||
BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
|
||||
public static bool lockInputToGameView
|
||||
{
|
||||
get { return (bool)propertyLockInputToGameView.GetValue(null); }
|
||||
set { propertyLockInputToGameView.SetValue(null, value); }
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
methodLoad.Invoke(null, null);
|
||||
}
|
||||
|
||||
public static void Delete()
|
||||
{
|
||||
string filePath = (string)fieldFilePath.GetValue(null);
|
||||
if(File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b8555e3da10e49e99f0af56f4512955
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
178
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputManager.cs
vendored
Normal file
178
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputManager.cs
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
using InputSystem = UnityEngine.InputSystem.InputSystem;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IInputManager
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<InputRemoting.Message> onMessage;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<InputEventPtr, InputDevice> onEvent;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
event Action<string, InputControlLayoutChange> onLayoutChange;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
ReadOnlyArray<InputDevice> devices { get; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
IEnumerable<string> layouts { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="deviceId"></param>
|
||||
/// <returns></returns>
|
||||
InputDevice GetDeviceById(int deviceId);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="layout"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="variants"></param>
|
||||
/// <returns></returns>
|
||||
InputDevice AddDevice(string layout, string name = null, string variants = null);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
void RemoveDevice(InputDevice device);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="usage"></param>
|
||||
void AddDeviceUsage(InputDevice device, string usage);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="usage"></param>
|
||||
void RemoveDeviceUsage(InputDevice device, string usage);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
InputControlLayout LoadLayout(string name);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="json"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="matches"></param>
|
||||
void RegisterControlLayout(string json, string name = null, bool isOverride = false);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
void RemoveLayout(string name);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="eventPtr"></param>
|
||||
void QueueEvent(InputEventPtr eventPtr);
|
||||
}
|
||||
|
||||
public abstract class InputManager : IInputManager
|
||||
{
|
||||
//todo(kazuki):: remove warning CS0067
|
||||
#pragma warning disable 0067
|
||||
public virtual event Action<InputRemoting.Message> onMessage;
|
||||
public virtual event Action<InputEventPtr, InputDevice> onEvent;
|
||||
public virtual event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
public virtual event Action<string, InputControlLayoutChange> onLayoutChange;
|
||||
#pragma warning restore 0067
|
||||
|
||||
public virtual ReadOnlyArray<InputDevice> devices
|
||||
{
|
||||
get
|
||||
{
|
||||
return InputSystem.devices;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> layouts
|
||||
{
|
||||
get
|
||||
{
|
||||
return InputSystem.ListLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual InputDevice GetDeviceById(int deviceId)
|
||||
{
|
||||
return InputSystem.GetDeviceById(deviceId);
|
||||
}
|
||||
|
||||
public virtual InputDevice AddDevice(string layout, string name = null, string variants = null)
|
||||
{
|
||||
foreach (var device_ in InputSystem.devices.Where(device => device.enabled))
|
||||
InputSystem.ResetDevice(device_);
|
||||
return InputSystem.AddDevice(layout, name, variants);
|
||||
}
|
||||
|
||||
public virtual void RemoveDevice(InputDevice device)
|
||||
{
|
||||
foreach (var device_ in InputSystem.devices.Where(device => device.enabled))
|
||||
InputSystem.ResetDevice(device_);
|
||||
InputSystem.RemoveDevice(device);
|
||||
}
|
||||
|
||||
public virtual void AddDeviceUsage(InputDevice device, string usage)
|
||||
{
|
||||
InputSystem.AddDeviceUsage(device, usage);
|
||||
}
|
||||
public virtual void RemoveDeviceUsage(InputDevice device, string usage)
|
||||
{
|
||||
InputSystem.RemoveDeviceUsage(device, usage);
|
||||
}
|
||||
|
||||
public virtual InputControlLayout LoadLayout(string name)
|
||||
{
|
||||
return InputSystem.LoadLayout(name);
|
||||
}
|
||||
|
||||
public virtual void RegisterControlLayout(string json, string name = null, bool isOverride = false)
|
||||
{
|
||||
if (isOverride)
|
||||
InputSystem.RegisterLayoutOverride(json, name);
|
||||
else
|
||||
InputSystem.RegisterLayout(json, name);
|
||||
}
|
||||
|
||||
public virtual void RemoveLayout(string name)
|
||||
{
|
||||
InputSystem.RemoveLayout(name);
|
||||
}
|
||||
|
||||
public virtual void QueueEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
InputSystem.QueueEvent(eventPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputManager.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputManager.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab747f6e991890d4996dd8a432b7bd5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
861
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputRemoting.cs
vendored
Normal file
861
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputRemoting.cs
vendored
Normal file
@@ -0,0 +1,861 @@
|
||||
// note:: This script is using code snippets in InputSystem.
|
||||
// https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Devices/Remote/InputRemoting.cs
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: show remote device IDs in the debugger
|
||||
|
||||
////TODO: remote timestamps need to be translated to local timestamps; doesn't make sense for remote events getting
|
||||
//// processed on the local timeline as is when the originating timeline may be quite different
|
||||
|
||||
////TODO: support actions
|
||||
|
||||
////TODO: support input users
|
||||
|
||||
////TODO: text input events
|
||||
|
||||
////REVIEW: it seems that the various XXXMsg struct should be public; ATM doesn't seem like working with the message interface is practical
|
||||
|
||||
////REVIEW: the namespacing mechanism for layouts which changes base layouts means that layouts can't be played
|
||||
//// around with on the editor side but will only be changed once they're updated in the player
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the activity and data of an InputManager observable in message form.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can act as both the sender and Receiver of these message so the flow is fully bidirectional,
|
||||
/// i.e. the InputManager on either end can mirror its layouts, devices, and events over
|
||||
/// to the other end. This permits streaming input not just from the player to the editor but
|
||||
/// also feeding input from the editor back into the player.
|
||||
///
|
||||
/// Remoting sits entirely on top of the input system as an optional piece of functionality.
|
||||
/// In development players and the editor, we enable it automatically but in non-development
|
||||
/// players it has to be explicitly requested by the user.
|
||||
///
|
||||
/// To see devices and input from players in the editor, open the Input Debugger through
|
||||
/// "Windows >> Input Debugger".
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputSystem.remoting"/>
|
||||
/// \todo Reuse memory allocated for messages instead of allocating separately for each message.
|
||||
/// \todo Inteface to determine what to mirror from the local manager to the remote system.
|
||||
public sealed class InputRemoting : IObservable<InputRemoting.Message>, IObserver<InputRemoting.Message>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of possible types of messages exchanged between two InputRemoting instances.
|
||||
/// </summary>
|
||||
public enum MessageType
|
||||
{
|
||||
Connect,
|
||||
Disconnect,
|
||||
NewLayout,
|
||||
NewDevice,
|
||||
NewEvents,
|
||||
RemoveDevice,
|
||||
RemoveLayout,
|
||||
ChangeUsages,
|
||||
StartSending,
|
||||
StopSending,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message exchanged between two InputRemoting instances.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Message
|
||||
{
|
||||
/// <summary>
|
||||
/// For messages coming in, numeric ID of the sender of the message. For messages
|
||||
/// going out, numeric ID of the targeted Receiver of the message.
|
||||
/// </summary>
|
||||
public int participantId;
|
||||
public MessageType type;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
public bool sending
|
||||
{
|
||||
get => (m_Flags & Flags.Sending) == Flags.Sending;
|
||||
private set
|
||||
{
|
||||
if (value)
|
||||
m_Flags |= Flags.Sending;
|
||||
else
|
||||
m_Flags &= ~Flags.Sending;
|
||||
}
|
||||
}
|
||||
|
||||
static InputRemoting()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
//
|
||||
// note: This lines are for avoiding issues when running the editor
|
||||
// on background. When moved the focus from the editor, input events
|
||||
// from another process are ignored.
|
||||
// Please attention behaviours are difference several platforms when
|
||||
// moving focus from Unity Editor.
|
||||
//
|
||||
// Additionally, The behaviour is changed Unity2021.2 later (using
|
||||
// InputSystem 1.1). Please see "Background behaviour".
|
||||
// https://docs.unity3d.com/Packages/com.unity.inputsystem@1.1/manual/Settings.html#background-behavior
|
||||
#if INPUTSYSTEM_1_1_OR_NEWER
|
||||
// todo(kazuki):
|
||||
#else
|
||||
// Make sure we're not affected by the user giving focus away from the
|
||||
// game view.
|
||||
//
|
||||
InputEditorUserSettings.lockInputToGameView = true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
internal InputRemoting(IInputManager manager, bool startSendingOnConnect = false)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException("manager");
|
||||
|
||||
m_LocalManager = manager;
|
||||
|
||||
if (startSendingOnConnect)
|
||||
m_Flags |= Flags.StartSendingOnConnect;
|
||||
|
||||
//when listening for newly added layouts, must filter out ones we've added from remote
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start sending messages for data and activity in the local input system
|
||||
/// to observers.
|
||||
/// </summary>
|
||||
/// <seealso cref="sending"/>
|
||||
/// <seealso cref="StopSending"/>
|
||||
public void StartSending()
|
||||
{
|
||||
if (sending)
|
||||
return;
|
||||
|
||||
////TODO: send events in bulk rather than one-by-one
|
||||
m_LocalManager.onMessage += Send;
|
||||
m_LocalManager.onEvent += SendEvent;
|
||||
m_LocalManager.onDeviceChange += SendDeviceChange;
|
||||
m_LocalManager.onLayoutChange += SendLayoutChange;
|
||||
|
||||
sending = true;
|
||||
|
||||
SendInitialMessages();
|
||||
}
|
||||
|
||||
public void StopSending()
|
||||
{
|
||||
if (!sending)
|
||||
return;
|
||||
|
||||
m_LocalManager.onMessage -= Send;
|
||||
m_LocalManager.onEvent -= SendEvent;
|
||||
m_LocalManager.onDeviceChange -= SendDeviceChange;
|
||||
m_LocalManager.onLayoutChange -= SendLayoutChange;
|
||||
|
||||
sending = false;
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnNext(Message msg)
|
||||
{
|
||||
switch (msg.type)
|
||||
{
|
||||
case MessageType.Connect:
|
||||
ConnectMsg.Process(this);
|
||||
break;
|
||||
case MessageType.Disconnect:
|
||||
DisconnectMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewLayout:
|
||||
NewLayoutMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.RemoveLayout:
|
||||
RemoveLayoutMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewDevice:
|
||||
NewDeviceMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewEvents:
|
||||
NewEventsMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.ChangeUsages:
|
||||
ChangeUsageMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.RemoveDevice:
|
||||
RemoveDeviceMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.StartSending:
|
||||
StartSendingMsg.Process(this);
|
||||
break;
|
||||
case MessageType.StopSending:
|
||||
StopSendingMsg.Process(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnError(Exception error)
|
||||
{
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<Message> observer)
|
||||
{
|
||||
if (observer == null)
|
||||
throw new ArgumentNullException("observer");
|
||||
|
||||
var subscriber = new Subscriber { owner = this, observer = observer };
|
||||
ArrayHelpers.Append(ref m_Subscribers, subscriber);
|
||||
|
||||
return subscriber;
|
||||
}
|
||||
|
||||
private void SendInitialMessages()
|
||||
{
|
||||
SendAllGeneratedLayouts();
|
||||
SendAllDevices();
|
||||
}
|
||||
|
||||
private void SendAllGeneratedLayouts()
|
||||
{
|
||||
// todo(kazuki)::
|
||||
// layputBuilders property is not published from InputSystem
|
||||
//
|
||||
//foreach (var entry in m_LocalManager.m_Layouts.layoutBuilders)
|
||||
// SendLayout(entry.Key);
|
||||
|
||||
foreach (var layout in m_LocalManager.layouts)
|
||||
SendLayout(layout);
|
||||
}
|
||||
|
||||
private void SendLayout(string layoutName)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
var message = NewLayoutMsg.Create(this, layoutName);
|
||||
if (message != null)
|
||||
Send(message.Value);
|
||||
}
|
||||
|
||||
private void SendAllDevices()
|
||||
{
|
||||
var devices = m_LocalManager.devices;
|
||||
foreach (var device in devices)
|
||||
SendDevice(device);
|
||||
}
|
||||
|
||||
private void SendDevice(InputDevice device)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
// Don't mirror remote devices to other remotes.
|
||||
if (device.remote)
|
||||
return;
|
||||
|
||||
var newDeviceMessage = NewDeviceMsg.Create(device);
|
||||
Send(newDeviceMessage);
|
||||
|
||||
// Send current state. We do this here in this case as the device
|
||||
// may have been added some time ago and thus have already received events.
|
||||
var stateEventMessage = NewEventsMsg.CreateStateEvent(device);
|
||||
Send(stateEventMessage);
|
||||
}
|
||||
|
||||
private unsafe void SendEvent(InputEventPtr eventPtr, InputDevice device)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
////REVIEW: we probably want to have better control over this and allow producing local events
|
||||
//// against remote devices which *are* indeed sent across the wire
|
||||
// Don't send events that came in from remote devices.
|
||||
if (device != null && device.remote)
|
||||
return;
|
||||
|
||||
var message = NewEventsMsg.Create(eventPtr.data, 1);
|
||||
Send(message);
|
||||
}
|
||||
|
||||
private void SendDeviceChange(InputDevice device, InputDeviceChange change)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
// Don't mirror remoted devices to other remotes.
|
||||
if (device.remote)
|
||||
return;
|
||||
|
||||
Message msg;
|
||||
switch (change)
|
||||
{
|
||||
case InputDeviceChange.Added:
|
||||
msg = NewDeviceMsg.Create(device);
|
||||
break;
|
||||
case InputDeviceChange.Removed:
|
||||
msg = RemoveDeviceMsg.Create(device);
|
||||
break;
|
||||
case InputDeviceChange.UsageChanged:
|
||||
msg = ChangeUsageMsg.Create(device);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
private void SendLayoutChange(string layout, InputControlLayoutChange change)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
Message msg;
|
||||
switch (change)
|
||||
{
|
||||
case InputControlLayoutChange.Added:
|
||||
case InputControlLayoutChange.Replaced:
|
||||
var message = NewLayoutMsg.Create(this, layout);
|
||||
if (message == null)
|
||||
return;
|
||||
msg = message.Value;
|
||||
break;
|
||||
case InputControlLayoutChange.Removed:
|
||||
msg = RemoveLayoutMsg.Create(layout);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
private void Send(Message msg)
|
||||
{
|
||||
foreach (var subscriber in m_Subscribers)
|
||||
subscriber.observer.OnNext(msg);
|
||||
}
|
||||
|
||||
////TODO: with C#7 this should be a ref return
|
||||
private int FindOrCreateSenderRecord(int senderId)
|
||||
{
|
||||
// Try to find existing.
|
||||
if (m_Senders != null)
|
||||
{
|
||||
var senderCount = m_Senders.Length;
|
||||
for (var i = 0; i < senderCount; ++i)
|
||||
if (m_Senders[i].senderId == senderId)
|
||||
return i;
|
||||
}
|
||||
|
||||
// Create new.
|
||||
var sender = new RemoteSender
|
||||
{
|
||||
senderId = senderId,
|
||||
};
|
||||
return ArrayHelpers.Append(ref m_Senders, sender);
|
||||
}
|
||||
|
||||
private int FindLocalDeviceId(int remoteDeviceId, int senderIndex)
|
||||
{
|
||||
var localDevices = m_Senders[senderIndex].devices;
|
||||
if (localDevices != null)
|
||||
{
|
||||
var numLocalDevices = localDevices.Length;
|
||||
|
||||
for (var i = 0; i < numLocalDevices; ++i)
|
||||
{
|
||||
if (localDevices[i].remoteId == remoteDeviceId)
|
||||
return localDevices[i].localId;
|
||||
}
|
||||
}
|
||||
|
||||
return InputDevice.InvalidDeviceId;
|
||||
}
|
||||
|
||||
private InputDevice TryGetDeviceByRemoteId(int remoteDeviceId, int senderIndex)
|
||||
{
|
||||
var localId = FindLocalDeviceId(remoteDeviceId, senderIndex);
|
||||
return m_LocalManager.GetDeviceById(localId);
|
||||
}
|
||||
|
||||
private Flags m_Flags;
|
||||
private IInputManager m_LocalManager; // Input system we mirror input from and to.
|
||||
private Subscriber[] m_Subscribers; // Receivers we send input to.
|
||||
private RemoteSender[] m_Senders; // Senders we receive input from.
|
||||
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
Sending = 1 << 0,
|
||||
StartSendingOnConnect = 1 << 1
|
||||
}
|
||||
|
||||
// Data we keep about a unique sender that we receive input data
|
||||
// from. We keep track of the layouts and devices we added to
|
||||
// the local system.
|
||||
[Serializable]
|
||||
internal struct RemoteSender
|
||||
{
|
||||
public int senderId;
|
||||
public string[] layouts;
|
||||
public RemoteInputDevice[] devices;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal struct RemoteInputDevice
|
||||
{
|
||||
public int remoteId; // Device ID used by sender.
|
||||
public int localId; // Device ID used by us in local system.
|
||||
|
||||
public InputDeviceDescription description;
|
||||
}
|
||||
|
||||
internal class Subscriber : IDisposable
|
||||
{
|
||||
public InputRemoting owner;
|
||||
public IObserver<Message> observer;
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayHelpers.Erase(ref owner.m_Subscribers, this);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConnectMsg
|
||||
{
|
||||
public static void Process(InputRemoting Receiver)
|
||||
{
|
||||
if (Receiver.sending)
|
||||
{
|
||||
Receiver.SendAllDevices();
|
||||
}
|
||||
else if ((Receiver.m_Flags & Flags.StartSendingOnConnect) == Flags.StartSendingOnConnect)
|
||||
Receiver.StartSending();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StartSendingMsg
|
||||
{
|
||||
public static void Process(InputRemoting Receiver)
|
||||
{
|
||||
Receiver.StartSending();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StopSendingMsg
|
||||
{
|
||||
public static void Process(InputRemoting Receiver)
|
||||
{
|
||||
Receiver.StopSending();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveRemoteDevices(int participantId)
|
||||
{
|
||||
var senderIndex = FindOrCreateSenderRecord(participantId);
|
||||
|
||||
// Remove devices added by remote.
|
||||
var devices = m_Senders[senderIndex].devices;
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (var remoteDevice in devices)
|
||||
{
|
||||
var device = m_LocalManager.GetDeviceById(remoteDevice.localId);
|
||||
if (device != null)
|
||||
m_LocalManager.RemoveDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
////TODO: remove layouts added by remote
|
||||
ArrayHelpers.EraseAt(ref m_Senders, senderIndex);
|
||||
}
|
||||
|
||||
private static class DisconnectMsg
|
||||
{
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
Receiver.RemoveRemoteDevices(msg.participantId);
|
||||
Receiver.StopSending();
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote input system that there's a new layout.
|
||||
private static class NewLayoutMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public string name;
|
||||
public string layoutJson;
|
||||
public bool isOverride;
|
||||
}
|
||||
|
||||
public static Message? Create(InputRemoting sender, string layoutName)
|
||||
{
|
||||
// Try to load the layout. Ignore the layout if it couldn't
|
||||
// be loaded.
|
||||
InputControlLayout layout;
|
||||
try
|
||||
{
|
||||
layout = sender.m_LocalManager.LoadLayout(new InternedString(layoutName));
|
||||
if (layout == null)
|
||||
{
|
||||
RenderStreaming.Logger.Log(string.Format(
|
||||
"Could not find layout '{0}' meant to be sent through remote connection; this should not happen",
|
||||
layoutName));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
RenderStreaming.Logger.Log(string.Format(
|
||||
"Could not load layout '{0}'; not sending to remote listeners (exception: {1})", layoutName,
|
||||
exception));
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = new Data
|
||||
{
|
||||
name = layoutName,
|
||||
layoutJson = layout.ToJson(),
|
||||
isOverride = layout.isOverride
|
||||
};
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewLayout,
|
||||
data = SerializeData(data)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
var senderIndex = Receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
|
||||
var internedLayoutName = new InternedString(data.name);
|
||||
Receiver.m_LocalManager.RegisterControlLayout(data.layoutJson, data.name, data.isOverride);
|
||||
ArrayHelpers.Append(ref Receiver.m_Senders[senderIndex].layouts, internedLayoutName);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemoveLayoutMsg
|
||||
{
|
||||
public static Message Create(string layoutName)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(layoutName);
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.RemoveLayout,
|
||||
data = bytes
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
////REVIEW: we probably don't want to do this blindly
|
||||
var layoutName = Encoding.UTF8.GetString(msg.data);
|
||||
Receiver.m_LocalManager.RemoveLayout(layoutName);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote input system that there's a new device.
|
||||
private static class NewDeviceMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public string name;
|
||||
public string layout;
|
||||
public int deviceId;
|
||||
public string[] usages;
|
||||
public InputDeviceDescription description;
|
||||
}
|
||||
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
Debug.Assert(!device.remote, "Device being sent to remotes should be a local device, not a remote one");
|
||||
|
||||
var data = new Data
|
||||
{
|
||||
name = device.name,
|
||||
layout = device.layout,
|
||||
deviceId = device.deviceId,
|
||||
description = device.description,
|
||||
usages = device.usages.Select(x => x.ToString()).ToArray()
|
||||
};
|
||||
|
||||
var json = JsonUtility.ToJson(data);
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewDevice,
|
||||
data = bytes
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
var senderIndex = Receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
|
||||
// Make sure we haven't already seen the device.
|
||||
var devices = Receiver.m_Senders[senderIndex].devices;
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (var entry in devices)
|
||||
if (entry.remoteId == data.deviceId)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, string.Format(
|
||||
"Already received device with id {0} (layout '{1}', description '{3}) from remote {2}",
|
||||
data.deviceId,
|
||||
data.layout, msg.participantId, data.description));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create device.
|
||||
InputDevice device;
|
||||
try
|
||||
{
|
||||
////REVIEW: this gives remote devices names the same way that local devices receive them; should we make remote status visible in the name?
|
||||
device = Receiver.m_LocalManager.AddDevice(data.layout, data.name);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error,
|
||||
$"Could not create remote device '{data.description}' with layout '{data.layout}' locally (exception: {exception})");
|
||||
return;
|
||||
}
|
||||
// todo(kazuki)::Avoid to use reflection
|
||||
// device.m_ParticipantId = msg.participantId;
|
||||
device.SetParticipantId(msg.participantId);
|
||||
|
||||
// todo(kazuki)::Avoid to use reflection
|
||||
// device.m_Description = data.description;
|
||||
// device.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
|
||||
device.SetDescription(data.description);
|
||||
|
||||
var deviceFlagsRemote = 1 << 3;
|
||||
device.SetDeviceFlags(device.GetDeviceFlags() | deviceFlagsRemote);
|
||||
|
||||
if (data.usages != null)
|
||||
foreach (var usage in data.usages)
|
||||
Receiver.m_LocalManager.AddDeviceUsage(device, usage);
|
||||
|
||||
// Remember it.
|
||||
var record = new RemoteInputDevice
|
||||
{
|
||||
remoteId = data.deviceId,
|
||||
localId = device.deviceId,
|
||||
description = data.description
|
||||
};
|
||||
ArrayHelpers.Append(ref Receiver.m_Senders[senderIndex].devices, record);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote system there's new input events.
|
||||
private static class NewEventsMsg
|
||||
{
|
||||
// todo(kazuki):: not found DeviceResetEvent
|
||||
//public static unsafe Message CreateResetEvent(InputDevice device, bool isHardReset)
|
||||
//{
|
||||
// var resetEvent = DeviceResetEvent.Create(device.deviceId, isHardReset);
|
||||
// return Create((InputEvent*)UnsafeUtility.AddressOf(ref resetEvent), 1);
|
||||
//}
|
||||
|
||||
public static unsafe Message CreateStateEvent(InputDevice device)
|
||||
{
|
||||
using (StateEvent.From(device, out var eventPtr))
|
||||
return Create(eventPtr.data, 1);
|
||||
}
|
||||
|
||||
public static unsafe Message Create(InputEvent* events, int eventCount)
|
||||
{
|
||||
// Find total size of event buffer we need.
|
||||
var totalSize = 0u;
|
||||
var eventPtr = new InputEventPtr(events);
|
||||
for (var i = 0; i < eventCount; ++i, eventPtr = eventPtr.Next())
|
||||
{
|
||||
totalSize += eventPtr.sizeInBytes;
|
||||
}
|
||||
|
||||
// Copy event data to buffer. Would be nice if we didn't have to do that
|
||||
// but unfortunately we need a byte[] and can't just pass the 'events' IntPtr
|
||||
// directly.
|
||||
var data = new byte[totalSize];
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
UnsafeUtility.MemCpy(dataPtr, events, totalSize);
|
||||
}
|
||||
|
||||
// Done.
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewEvents,
|
||||
data = data
|
||||
};
|
||||
}
|
||||
|
||||
public static unsafe void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
var manager = Receiver.m_LocalManager;
|
||||
|
||||
fixed (byte* dataPtr = msg.data)
|
||||
{
|
||||
var dataEndPtr = new IntPtr(dataPtr + msg.data.Length);
|
||||
var eventCount = 0;
|
||||
var eventPtr = new InputEventPtr((InputEvent*)dataPtr);
|
||||
var senderIndex = Receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
|
||||
while ((Int64)eventPtr.data < dataEndPtr.ToInt64())
|
||||
{
|
||||
// Patch up device ID to refer to local device and send event.
|
||||
var remoteDeviceId = eventPtr.deviceId;
|
||||
var localDeviceId = Receiver.FindLocalDeviceId(remoteDeviceId, senderIndex);
|
||||
eventPtr.deviceId = localDeviceId;
|
||||
|
||||
if (localDeviceId != InputDevice.InvalidDeviceId)
|
||||
{
|
||||
////TODO: add API to send events in bulk rather than one by one
|
||||
manager.QueueEvent(eventPtr);
|
||||
}
|
||||
|
||||
++eventCount;
|
||||
eventPtr = eventPtr.Next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChangeUsageMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public int deviceId;
|
||||
public string[] usages;
|
||||
}
|
||||
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
var data = new Data
|
||||
{
|
||||
deviceId = device.deviceId,
|
||||
usages = device.usages.Select(x => x.ToString()).ToArray()
|
||||
};
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.ChangeUsages,
|
||||
data = SerializeData(data)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
var senderIndex = Receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
|
||||
var device = Receiver.TryGetDeviceByRemoteId(data.deviceId, senderIndex);
|
||||
if (device != null)
|
||||
{
|
||||
foreach (var deviceUsage in device.usages)
|
||||
{
|
||||
if (!data.usages.Contains(deviceUsage))
|
||||
Receiver.m_LocalManager.RemoveDeviceUsage(device, new InternedString(deviceUsage));
|
||||
}
|
||||
|
||||
if (data.usages.Length == 1)
|
||||
Receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(data.usages[0]));
|
||||
foreach (var dataUsage in data.usages)
|
||||
{
|
||||
var internedDataUsage = new InternedString(dataUsage);
|
||||
if (!device.usages.Contains(internedDataUsage))
|
||||
Receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(dataUsage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemoveDeviceMsg
|
||||
{
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.RemoveDevice,
|
||||
data = BitConverter.GetBytes(device.deviceId)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting Receiver, Message msg)
|
||||
{
|
||||
var senderIndex = Receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var remoteDeviceId = BitConverter.ToInt32(msg.data, 0);
|
||||
|
||||
var device = Receiver.TryGetDeviceByRemoteId(remoteDeviceId, senderIndex);
|
||||
if (device != null)
|
||||
Receiver.m_LocalManager.RemoveDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] SerializeData<TData>(TData data)
|
||||
{
|
||||
var json = JsonUtility.ToJson(data);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
private static TData DeserializeData<TData>(byte[] data)
|
||||
{
|
||||
var json = Encoding.UTF8.GetString(data);
|
||||
return JsonUtility.FromJson<TData>(json);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
// State we want to take across domain reloads. We can only take some of the
|
||||
// state across. Subscriptions will be lost and have to be manually restored.
|
||||
[Serializable]
|
||||
internal struct SerializedState
|
||||
{
|
||||
public int senderId;
|
||||
public RemoteSender[] senders;
|
||||
|
||||
// We can't take these across domain reloads but we want to take them across
|
||||
// InputSystem.Save/Restore.
|
||||
[NonSerialized] public Subscriber[] subscribers;
|
||||
}
|
||||
|
||||
internal SerializedState SaveState()
|
||||
{
|
||||
return new SerializedState
|
||||
{
|
||||
senders = m_Senders,
|
||||
subscribers = m_Subscribers
|
||||
};
|
||||
}
|
||||
|
||||
internal void RestoreState(SerializedState state, IInputManager manager)
|
||||
{
|
||||
m_LocalManager = manager;
|
||||
m_Senders = state.senders;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputRemoting.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/InputRemoting.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90f809b07b8c64c4e99f2e64cfe0db92
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/MessageSerializer.cs
vendored
Normal file
45
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/MessageSerializer.cs
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
static class MessageSerializer
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] Serialize(ref InputRemoting.Message message)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = new BinaryWriter(stream);
|
||||
|
||||
writer.Write(message.participantId);
|
||||
writer.Write((int)message.type);
|
||||
writer.Write(message.data.Length);
|
||||
writer.Write(message.data);
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="message"></param>
|
||||
public static void Deserialize(byte[] bytes, out InputRemoting.Message message)
|
||||
{
|
||||
var reader = new BinaryReader(new MemoryStream(bytes));
|
||||
|
||||
message = default;
|
||||
message.participantId = reader.ReadInt32();
|
||||
message.type = (InputRemoting.MessageType)reader.ReadInt32();
|
||||
int length = reader.ReadInt32();
|
||||
message.data = reader.ReadBytes(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a1c240f9ad55b045884a8e8dc77358b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
211
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Receiver.cs
vendored
Normal file
211
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Receiver.cs
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
using InputSystem = UnityEngine.InputSystem.InputSystem;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
partial class Receiver : InputManager, IDisposable
|
||||
{
|
||||
public override event Action<InputRemoting.Message> onMessage;
|
||||
public new event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
public new event Action<string, InputControlLayoutChange> onLayoutChange;
|
||||
|
||||
private RTCDataChannel _channel;
|
||||
private readonly List<InputDevice> _remoteDevices = new List<InputDevice>();
|
||||
|
||||
private readonly Dictionary<string, string> _remoteLayouts = new Dictionary<string, string>();
|
||||
private readonly List<string> _registeredRemoteLayout = new List<string>();
|
||||
private InputPositionCorrector _corrector;
|
||||
private Action<InputEventPtr, InputDevice> _onEvent;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool EnableInputPositionCorrection { set; get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
public Receiver(RTCDataChannel channel)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException("channel is null");
|
||||
_channel.OnMessage += OnMessage;
|
||||
|
||||
_onEvent = (InputEventPtr ptr, InputDevice device) => { base.QueueEvent(ptr); };
|
||||
_corrector = new InputPositionCorrector(_onEvent);
|
||||
}
|
||||
|
||||
~Receiver()
|
||||
{
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RemoveAllRemoteDevices();
|
||||
RemoveAllRemoteLayouts();
|
||||
}
|
||||
|
||||
private void OnMessage(byte[] bytes)
|
||||
{
|
||||
MessageSerializer.Deserialize(bytes, out var message);
|
||||
onMessage?.Invoke(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override ReadOnlyArray<InputDevice> devices
|
||||
{
|
||||
get
|
||||
{
|
||||
// note:: InputRemoting class rejects remote devices when sending device information to the remote peer.
|
||||
// Avoid to get assert "Device being sent to remotes should be a local device, not a remote one"
|
||||
return new ReadOnlyArray<InputDevice>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override IEnumerable<string> layouts
|
||||
{
|
||||
get
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public ReadOnlyArray<InputDevice> remoteDevices
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ReadOnlyArray<InputDevice>(_remoteDevices.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyArray<string> remoteLayouts
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ReadOnlyArray<string>(_remoteLayouts.Keys.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public void RemoveAllRemoteDevices()
|
||||
{
|
||||
while (_remoteDevices.Count > 0)
|
||||
{
|
||||
RemoveDevice(_remoteDevices[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAllRemoteLayouts()
|
||||
{
|
||||
while (_remoteLayouts.Count > 0)
|
||||
{
|
||||
RemoveLayout(_remoteLayouts.First().Key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override InputDevice AddDevice(string layout, string name = null, string variants = null)
|
||||
{
|
||||
if (InputSystem.ListLayouts().Count(_ => _ == layout) == 0)
|
||||
{
|
||||
if (!_remoteLayouts.TryGetValue(layout, out string json))
|
||||
throw new InvalidOperationException();
|
||||
base.RegisterControlLayout(json, layout);
|
||||
_registeredRemoteLayout.Add(layout);
|
||||
}
|
||||
var device = base.AddDevice(layout, name, variants);
|
||||
_remoteDevices.Add(device);
|
||||
onDeviceChange?.Invoke(device, InputDeviceChange.Added);
|
||||
return device;
|
||||
}
|
||||
|
||||
public override void RemoveDevice(InputDevice device)
|
||||
{
|
||||
base.RemoveDevice(device);
|
||||
_remoteDevices.Remove(device);
|
||||
onDeviceChange?.Invoke(device, InputDeviceChange.Removed);
|
||||
}
|
||||
|
||||
public override void RegisterControlLayout(string json, string name = null, bool isOverride = false)
|
||||
{
|
||||
// todo(kazuki):: not call base class
|
||||
// base.RegisterControlLayout(json, name, isOverride);
|
||||
|
||||
_remoteLayouts.Add(name, json);
|
||||
onLayoutChange?.Invoke(name, InputControlLayoutChange.Added);
|
||||
}
|
||||
|
||||
public override void RemoveLayout(string name)
|
||||
{
|
||||
if (_registeredRemoteLayout.Contains(name))
|
||||
{
|
||||
base.RemoveLayout(name);
|
||||
_registeredRemoteLayout.Remove(name);
|
||||
}
|
||||
_remoteLayouts.Remove(name);
|
||||
onLayoutChange?.Invoke(name, InputControlLayoutChange.Removed);
|
||||
}
|
||||
|
||||
public override void QueueEvent(InputEventPtr ptr)
|
||||
{
|
||||
InputDevice device = InputSystem.GetDeviceById(ptr.deviceId);
|
||||
|
||||
// mapping sender coordinate system to receiver one.
|
||||
if (EnableInputPositionCorrection && device is Pointer && ptr.IsA<StateEvent>())
|
||||
{
|
||||
_corrector.Invoke(ptr, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.QueueEvent(ptr);
|
||||
}
|
||||
|
||||
// workaround:
|
||||
// UnityEngine.UI.InputField and TMP_InputField depends on Event.PopEvent.
|
||||
// Event.PopEvent is old event API, therefore EventSystem.QueueEvent doesn't queue events.
|
||||
var eventType = ptr.type;
|
||||
if (device is Keyboard &&
|
||||
(eventType == StateEvent.Type ||
|
||||
eventType == DeltaStateEvent.Type ||
|
||||
eventType == TextEvent.Type))
|
||||
{
|
||||
EmulateInputFieldEvent(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="size">Texture Size.</param>
|
||||
/// <param name="region">Region of the texture in world coordinate system.</param>
|
||||
public void CalculateInputRegion(Rect inputRegion, Rect outputRegion)
|
||||
{
|
||||
_corrector.inputRegion = inputRegion;
|
||||
_corrector.outputRegion = outputRegion;
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Receiver.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Receiver.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 834a3bd628e331943b599618b7309d1f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Sender.cs
vendored
Normal file
109
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Sender.cs
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace Unity.RenderStreaming.InputSystem
|
||||
{
|
||||
using InputSystem = UnityEngine.InputSystem.InputSystem;
|
||||
|
||||
class Sender : InputManager, IDisposable
|
||||
{
|
||||
public override event Action<InputEventPtr, InputDevice> onEvent;
|
||||
public override event Action<InputDevice, InputDeviceChange> onDeviceChange;
|
||||
public override event Action<string, InputControlLayoutChange> onLayoutChange;
|
||||
|
||||
private InputPositionCorrector _corrector;
|
||||
private Action<InputEventPtr, InputDevice> _onEvent;
|
||||
|
||||
public Sender()
|
||||
{
|
||||
InputSystem.onEvent += OnEvent;
|
||||
InputSystem.onDeviceChange += OnDeviceChange;
|
||||
InputSystem.onLayoutChange += OnLayoutChange;
|
||||
|
||||
_onEvent = (InputEventPtr ptr, InputDevice device) => { onEvent?.Invoke(ptr, device); };
|
||||
_corrector = new InputPositionCorrector(_onEvent);
|
||||
}
|
||||
|
||||
~Sender()
|
||||
{
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
InputSystem.onEvent -= OnEvent;
|
||||
InputSystem.onDeviceChange -= OnDeviceChange;
|
||||
InputSystem.onLayoutChange -= OnLayoutChange;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool EnableInputPositionCorrection { set; get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="inputRegion"></param>
|
||||
/// <param name="outputRegion"></param>
|
||||
public void CalculateInputRegion(Rect inputRegion, Rect outputRegion)
|
||||
{
|
||||
_corrector.inputRegion = inputRegion;
|
||||
_corrector.outputRegion = outputRegion;
|
||||
}
|
||||
|
||||
private void OnEvent(InputEventPtr ptr, InputDevice device)
|
||||
{
|
||||
// mapping sender coordinate system to receiver one.
|
||||
if (EnableInputPositionCorrection && device is Pointer && ptr.IsA<StateEvent>())
|
||||
{
|
||||
_corrector.Invoke(ptr, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
onEvent?.Invoke(ptr, device);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeviceChange(InputDevice device, InputDeviceChange change)
|
||||
{
|
||||
onDeviceChange?.Invoke(device, change);
|
||||
}
|
||||
private void OnLayoutChange(string name, InputControlLayoutChange change)
|
||||
{
|
||||
onLayoutChange?.Invoke(name, change);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
class Observer : IObserver<InputRemoting.Message>
|
||||
{
|
||||
private RTCDataChannel _channel;
|
||||
public Observer(RTCDataChannel channel)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException("channel is null");
|
||||
}
|
||||
public void OnNext(InputRemoting.Message value)
|
||||
{
|
||||
if (_channel.ReadyState != RTCDataChannelState.Open)
|
||||
return;
|
||||
byte[] bytes = MessageSerializer.Serialize(ref value);
|
||||
_channel.Send(bytes);
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
}
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Sender.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/InputSystem/Sender.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ba4fd50aea9ff3459e460141a546b3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
303
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/PeerConnection.cs
vendored
Normal file
303
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/PeerConnection.cs
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal class PeerConnection : IDisposable
|
||||
{
|
||||
public delegate void OnConnectEvent();
|
||||
|
||||
public delegate void OnDisconnectEvent();
|
||||
|
||||
public delegate void OnDataChannelEvent(RTCDataChannel channel);
|
||||
|
||||
public delegate void OnTrackEvent(RTCTrackEvent trackEvent);
|
||||
|
||||
public delegate void SendOfferEvent(RTCSessionDescription description);
|
||||
|
||||
public delegate void SendAnswerEvent(RTCSessionDescription description);
|
||||
|
||||
public delegate void SendCandidateEvent(RTCIceCandidate candidate);
|
||||
|
||||
public OnConnectEvent OnConnectHandler;
|
||||
public OnDisconnectEvent OnDisconnectHandler;
|
||||
public OnDataChannelEvent OnDataChannelHandler;
|
||||
public OnTrackEvent OnTrackEventHandler;
|
||||
public SendOfferEvent SendOfferHandler;
|
||||
public SendAnswerEvent SendAnswerHandler;
|
||||
public SendCandidateEvent SendCandidateHandler;
|
||||
|
||||
public RTCPeerConnection peer => _peer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool waitingAnswer
|
||||
{
|
||||
get => _waitingAnswer;
|
||||
private set
|
||||
{
|
||||
_waitingAnswer = value;
|
||||
_timeSinceStartWaitingAnswer =
|
||||
_waitingAnswer ? Time.realtimeSinceStartup : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly RTCPeerConnection _peer;
|
||||
private readonly bool _polite;
|
||||
private readonly Func<IEnumerator, Coroutine> _startCoroutine;
|
||||
private readonly Action<Coroutine> _stopCoroutine;
|
||||
private readonly HashSet<WeakReference<Coroutine>> _processingCoroutineList = new HashSet<WeakReference<Coroutine>>();
|
||||
|
||||
// resend offer
|
||||
private readonly float _resendInterval;
|
||||
private bool _waitingAnswer;
|
||||
private float _timeSinceStartWaitingAnswer;
|
||||
|
||||
// processing set description
|
||||
private bool _processingSetDescription;
|
||||
|
||||
// processing got description
|
||||
private bool _ignoreOffer;
|
||||
private bool _srdAnswerPending;
|
||||
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
public PeerConnection(bool polite, RTCConfiguration config, float resendInterval, Func<IEnumerator, Coroutine> startCoroutine, Action<Coroutine> stopCoroutine)
|
||||
{
|
||||
_polite = polite;
|
||||
_resendInterval = resendInterval;
|
||||
_startCoroutine = startCoroutine;
|
||||
_stopCoroutine = stopCoroutine;
|
||||
|
||||
_peer = new RTCPeerConnection(ref config);
|
||||
_peer.OnDataChannel = channel => OnDataChannelHandler?.Invoke(channel);
|
||||
_peer.OnIceCandidate = candidate => SendCandidateHandler?.Invoke(candidate);
|
||||
_peer.OnTrack = trackEvent => OnTrackEventHandler?.Invoke(trackEvent);
|
||||
_peer.OnConnectionStateChange = state =>
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case RTCPeerConnectionState.Connected:
|
||||
OnConnectHandler?.Invoke();
|
||||
break;
|
||||
case RTCPeerConnectionState.Failed:
|
||||
OnDisconnectHandler?.Invoke();
|
||||
break;
|
||||
}
|
||||
};
|
||||
_peer.OnNegotiationNeeded = () => StartCoroutine(OnNegotiationNeeded());
|
||||
}
|
||||
|
||||
private void StartCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
var co = _startCoroutine(enumerator);
|
||||
_processingCoroutineList.RemoveWhere(x => !x.TryGetTarget(out _));
|
||||
_processingCoroutineList.Add(new WeakReference<Coroutine>(co));
|
||||
}
|
||||
|
||||
~PeerConnection()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = _polite ? "polite" : "impolite";
|
||||
return
|
||||
$"[{str}-{nameof(PeerConnection)} {nameof(_peer.ConnectionState)}:{_peer.ConnectionState} {nameof(_peer.IceConnectionState)}:{_peer.IceConnectionState} {nameof(_peer.SignalingState)}:{_peer.SignalingState} {nameof(_peer.GatheringState)}:{_peer.GatheringState}]";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
foreach (var weakCo in _processingCoroutineList)
|
||||
{
|
||||
if (weakCo.TryGetTarget(out var co))
|
||||
{
|
||||
_stopCoroutine?.Invoke(co);
|
||||
}
|
||||
}
|
||||
_processingCoroutineList.Clear();
|
||||
|
||||
if (_peer != null)
|
||||
{
|
||||
_peer.OnTrack = null;
|
||||
_peer.OnDataChannel = null;
|
||||
_peer.OnIceCandidate = null;
|
||||
_peer.OnNegotiationNeeded = null;
|
||||
_peer.OnConnectionStateChange = null;
|
||||
_peer.OnIceConnectionChange = null;
|
||||
_peer.OnIceGatheringStateChange = null;
|
||||
_peer.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private IEnumerator OnNegotiationNeeded()
|
||||
{
|
||||
var waitProcessSetDescription = new WaitWhile(() => _processingSetDescription);
|
||||
yield return waitProcessSetDescription;
|
||||
SendOffer();
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
return _peer.ConnectionState == RTCPeerConnectionState.Connected;
|
||||
}
|
||||
|
||||
public bool IsStable()
|
||||
{
|
||||
return _peer.SignalingState == RTCSignalingState.Stable ||
|
||||
(_peer.SignalingState == RTCSignalingState.HaveLocalOffer && _srdAnswerPending);
|
||||
}
|
||||
|
||||
public void SendOffer()
|
||||
{
|
||||
if (_processingSetDescription)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"{this} already processing other set description");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsStable())
|
||||
{
|
||||
if (!_waitingAnswer)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{this} sendoffer needs in stable state, current state is {_peer.SignalingState}");
|
||||
}
|
||||
|
||||
var timeout = _timeSinceStartWaitingAnswer + _resendInterval;
|
||||
|
||||
if (timeout < Time.realtimeSinceStartup)
|
||||
{
|
||||
SendOfferHandler?.Invoke(_peer.LocalDescription);
|
||||
_timeSinceStartWaitingAnswer = Time.realtimeSinceStartup;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(SendOfferCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator SendOfferCoroutine()
|
||||
{
|
||||
Assert.AreEqual(_peer.SignalingState, RTCSignalingState.Stable);
|
||||
Assert.AreEqual(_processingSetDescription, false);
|
||||
Assert.AreEqual(waitingAnswer, false);
|
||||
|
||||
_processingSetDescription = true;
|
||||
|
||||
var opLocalDesc = _peer.SetLocalDescription();
|
||||
yield return opLocalDesc;
|
||||
|
||||
if (opLocalDesc.IsError)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"{this} {opLocalDesc.Error.message}");
|
||||
_processingSetDescription = false;
|
||||
yield break;
|
||||
}
|
||||
if (_peer.SignalingState != RTCSignalingState.HaveLocalOffer)
|
||||
{
|
||||
_processingSetDescription = false;
|
||||
yield break;
|
||||
}
|
||||
Assert.AreEqual(_peer.LocalDescription.type, RTCSdpType.Offer);
|
||||
Assert.AreEqual(_peer.SignalingState, RTCSignalingState.HaveLocalOffer);
|
||||
_processingSetDescription = false;
|
||||
waitingAnswer = true;
|
||||
|
||||
SendOfferHandler?.Invoke(_peer.LocalDescription);
|
||||
}
|
||||
|
||||
public void SendAnswer()
|
||||
{
|
||||
if (_processingSetDescription)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"{this} already processing other set description");
|
||||
return;
|
||||
}
|
||||
|
||||
StartCoroutine(SendAnswerCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator SendAnswerCoroutine()
|
||||
{
|
||||
Assert.AreEqual(_peer.SignalingState, RTCSignalingState.HaveRemoteOffer);
|
||||
Assert.AreEqual(_processingSetDescription, false);
|
||||
|
||||
_processingSetDescription = true;
|
||||
|
||||
var opLocalDesc = _peer.SetLocalDescription();
|
||||
yield return opLocalDesc;
|
||||
|
||||
if (opLocalDesc.IsError)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"{this} {opLocalDesc.Error.message}");
|
||||
_processingSetDescription = false;
|
||||
yield break;
|
||||
}
|
||||
|
||||
Assert.AreEqual(_peer.LocalDescription.type, RTCSdpType.Answer);
|
||||
Assert.AreEqual(_peer.SignalingState, RTCSignalingState.Stable);
|
||||
_processingSetDescription = false;
|
||||
|
||||
SendAnswerHandler?.Invoke(_peer.LocalDescription);
|
||||
}
|
||||
|
||||
public IEnumerator OnGotDescription(RTCSessionDescription description, Action onComplete)
|
||||
{
|
||||
var waitOtherProcess = new WaitWhile(() => _processingSetDescription);
|
||||
yield return waitOtherProcess;
|
||||
|
||||
_ignoreOffer = description.type == RTCSdpType.Offer && !_polite && (_processingSetDescription || !IsStable());
|
||||
|
||||
if (_ignoreOffer)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"{this} glare - ignoreOffer.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
waitingAnswer = false;
|
||||
_srdAnswerPending = description.type == RTCSdpType.Answer;
|
||||
_processingSetDescription = true;
|
||||
|
||||
var remoteDescOp = _peer.SetRemoteDescription(ref description);
|
||||
yield return remoteDescOp;
|
||||
if (remoteDescOp.IsError)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"{this} {remoteDescOp.Error.message}");
|
||||
_srdAnswerPending = false;
|
||||
_processingSetDescription = false;
|
||||
yield break;
|
||||
}
|
||||
|
||||
_srdAnswerPending = false;
|
||||
_processingSetDescription = false;
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
|
||||
public bool OnGotIceCandidate(RTCIceCandidate candidate)
|
||||
{
|
||||
if (!_peer.AddIceCandidate(candidate))
|
||||
{
|
||||
if (!_ignoreOffer)
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"{this} this candidate can't accept on state.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/PeerConnection.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/PeerConnection.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbcbecf9d04e490bae7326f66971399d
|
||||
timeCreated: 1620856244
|
||||
198
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreaming.cs
vendored
Normal file
198
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreaming.cs
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoad]
|
||||
#endif
|
||||
public static class RenderStreaming
|
||||
{
|
||||
internal const string EditorBuildSettingsConfigKey = "com.unity.renderstreaming.settings";
|
||||
internal const string DefaultRenderStreamingSettingsPath =
|
||||
"Packages/com.unity.renderstreaming/Runtime/RenderStreamingSettings.asset";
|
||||
|
||||
private static RenderStreamingSettings s_settings;
|
||||
private static GameObject s_automaticStreamingObject;
|
||||
private static ILogger s_logger;
|
||||
|
||||
private static bool m_running;
|
||||
|
||||
internal static RenderStreamingSettings Settings
|
||||
{
|
||||
get => s_settings;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (s_settings == value)
|
||||
return;
|
||||
|
||||
// In the editor, we keep track of the settings asset through EditorBuildSettings.
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(value)))
|
||||
{
|
||||
EditorBuildSettings.AddConfigObject(EditorBuildSettingsConfigKey, value, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_running && s_settings.signalingSettings != value.signalingSettings)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, "Signaling settings doesn't change on already started signaling instance.");
|
||||
}
|
||||
|
||||
s_settings = value;
|
||||
ApplySettings();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AutomaticStreaming
|
||||
{
|
||||
get => s_settings.automaticStreaming;
|
||||
set
|
||||
{
|
||||
if (s_settings.automaticStreaming == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_settings.automaticStreaming = value;
|
||||
ApplySettings();
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetSignalingSettings<T>() where T : SignalingSettings
|
||||
{
|
||||
return s_settings.signalingSettings as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get & set the logger to use when logging debug messages inside the RenderStreaming package.
|
||||
/// By default will use Debug.unityLogger.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">Throws if setting a null logger.</exception>
|
||||
public static ILogger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_logger == null)
|
||||
{
|
||||
return Debug.unityLogger;
|
||||
}
|
||||
|
||||
return s_logger;
|
||||
}
|
||||
set
|
||||
{
|
||||
s_logger = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
static RenderStreaming()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
InitializeInEditor();
|
||||
#else
|
||||
m_running = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static void InitializeInEditor()
|
||||
{
|
||||
if (EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings settingsAsset))
|
||||
{
|
||||
s_settings = settingsAsset;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_settings = AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(DefaultRenderStreamingSettingsPath);
|
||||
}
|
||||
|
||||
EditorApplication.playModeStateChanged += change =>
|
||||
{
|
||||
m_running = change == PlayModeStateChange.EnteredPlayMode;
|
||||
};
|
||||
|
||||
EditorApplication.projectChanged += () =>
|
||||
{
|
||||
if (EditorBuildSettings.TryGetConfigObject(EditorBuildSettingsConfigKey, out RenderStreamingSettings _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var value = AssetDatabase.LoadAssetAtPath<RenderStreamingSettings>(DefaultRenderStreamingSettingsPath);
|
||||
if (value != null)
|
||||
{
|
||||
Settings = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void LoadSettings()
|
||||
{
|
||||
if (s_settings == null)
|
||||
{
|
||||
s_settings = Resources.FindObjectsOfTypeAll<RenderStreamingSettings>().FirstOrDefault() ??
|
||||
ScriptableObject.CreateInstance<RenderStreamingSettings>();
|
||||
}
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void RunInitialize()
|
||||
{
|
||||
if (AutomaticStreaming)
|
||||
{
|
||||
CreateAutomaticStreaming();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ApplySettings()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorUtility.SetDirty(s_settings);
|
||||
#endif
|
||||
|
||||
if (!m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_settings.automaticStreaming && s_automaticStreamingObject == null)
|
||||
{
|
||||
CreateAutomaticStreaming();
|
||||
}
|
||||
|
||||
if (!s_settings.automaticStreaming)
|
||||
{
|
||||
CleanUpAutomaticStreaming();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateAutomaticStreaming()
|
||||
{
|
||||
if (s_automaticStreamingObject != null)
|
||||
{
|
||||
Object.DestroyImmediate(s_automaticStreamingObject);
|
||||
}
|
||||
|
||||
s_automaticStreamingObject = new GameObject("AutomaticStreaming");
|
||||
s_automaticStreamingObject.AddComponent<AutomaticStreaming>();
|
||||
Object.DontDestroyOnLoad(s_automaticStreamingObject);
|
||||
}
|
||||
|
||||
private static void CleanUpAutomaticStreaming()
|
||||
{
|
||||
Object.DestroyImmediate(s_automaticStreamingObject);
|
||||
s_automaticStreamingObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreaming.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreaming.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a5616a55e14772b19ab62fe697eeee
|
||||
timeCreated: 1674112709
|
||||
22
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreamingSettings.cs
vendored
Normal file
22
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderStreamingSettings.cs
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class RenderStreamingSettings : ScriptableObject
|
||||
{
|
||||
internal const string AutomaticStreamingPropertyName = nameof(automaticStreaming);
|
||||
internal const string SignalingSettingsPropertyName = nameof(signalingSettings);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Automatically performs the necessary setup for streaming and starts streaming.")]
|
||||
public bool automaticStreaming;
|
||||
|
||||
[SerializeReference, SignalingSettings]
|
||||
public SignalingSettings signalingSettings = new WebSocketSignalingSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc8ba17751ad4107a50fa1415017b6b1
|
||||
timeCreated: 1674111941
|
||||
79
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderTextureBlitter.cs
vendored
Normal file
79
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderTextureBlitter.cs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
#if UNITY_2019_1 || UNITY_2019_2 //HDRP 5.x, 6.x
|
||||
using UnityEngine.Experimental.Rendering.HDPipeline;
|
||||
#else //HDRP 7.x and above
|
||||
using UnityEngine.Rendering.HighDefinition;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
[RequireComponent(typeof(Camera))]
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
[RequireComponent(typeof(HDAdditionalCameraData))]
|
||||
#endif
|
||||
internal class RenderTextureBlitter : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Camera m_rtCamera = null;
|
||||
|
||||
Camera m_cam;
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
HDAdditionalCameraData m_hdData;
|
||||
#endif
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
m_cam = GetComponent<Camera>();
|
||||
//Render nothing
|
||||
m_cam.clearFlags = CameraClearFlags.Nothing;
|
||||
m_cam.cullingMask = 0;
|
||||
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
m_hdData = GetComponent<HDAdditionalCameraData>();
|
||||
m_hdData.fullscreenPassthrough = true;
|
||||
m_hdData.customRender += BlitRenderStreamingRT;
|
||||
#elif URS_USE_URP_RUNTIME
|
||||
UnityEngine.Rendering.RenderPipelineManager.endCameraRendering += OnEndCameraRendering;
|
||||
#else
|
||||
Camera.onPreRender += BlitTexture;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
m_hdData.customRender -= BlitRenderStreamingRT;
|
||||
#elif URS_USE_URP_RUNTIME
|
||||
UnityEngine.Rendering.RenderPipelineManager.endCameraRendering -= OnEndCameraRendering;
|
||||
#else
|
||||
Camera.onPreRender -= BlitTexture;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if URS_USE_HDRP_RUNTIME
|
||||
public void BlitRenderStreamingRT(UnityEngine.Rendering.ScriptableRenderContext context, HDCamera cam)
|
||||
{
|
||||
Graphics.Blit(m_rtCamera.targetTexture, (RenderTexture)null);
|
||||
}
|
||||
#elif URS_USE_URP_RUNTIME
|
||||
void OnEndCameraRendering(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
if (cam == m_cam && null != m_rtCamera.targetTexture)
|
||||
{
|
||||
//This seems to work only if we have setup PostProcessing Stack V2
|
||||
Graphics.Blit(m_rtCamera.targetTexture, (RenderTexture)null);
|
||||
}
|
||||
}
|
||||
#else
|
||||
private void BlitTexture(Camera cam)
|
||||
{
|
||||
if (m_cam == cam && null != m_rtCamera.targetTexture)
|
||||
{
|
||||
Graphics.Blit(m_rtCamera.targetTexture, (RenderTexture)null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderTextureBlitter.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/RenderTextureBlitter.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88167b61ef2361446aefab18c3c7a843
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa3d0c210b91472b80c4158cb9459cab
|
||||
timeCreated: 1586218012
|
||||
413
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/HttpSignaling.cs
vendored
Normal file
413
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/HttpSignaling.cs
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming.Signaling
|
||||
{
|
||||
public class HttpSignaling : ISignaling
|
||||
{
|
||||
private static HashSet<HttpSignaling> instances = new HashSet<HttpSignaling>();
|
||||
|
||||
private readonly string m_url;
|
||||
private readonly int m_timeout;
|
||||
private readonly SynchronizationContext m_mainThreadContext;
|
||||
private bool m_running;
|
||||
private Thread m_signalingThread;
|
||||
|
||||
private string m_sessionId;
|
||||
private long m_lastTimeGetAllRequest;
|
||||
public string participantId;
|
||||
public string Url { get { return m_url; } }
|
||||
|
||||
public HttpSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext)
|
||||
{
|
||||
if (signalingSettings == null)
|
||||
throw new ArgumentNullException(nameof(signalingSettings));
|
||||
if (!(signalingSettings is HttpSignalingSettings settings))
|
||||
throw new ArgumentException("signalingSettings is not HttpSignalingSettings");
|
||||
m_url = settings.url;
|
||||
m_timeout = settings.interval;
|
||||
m_mainThreadContext = mainThreadContext;
|
||||
|
||||
if (m_url.StartsWith("https"))
|
||||
{
|
||||
ServicePointManager.ServerCertificateValidationCallback =
|
||||
(sender, certificate, chain, errors) => true;
|
||||
}
|
||||
|
||||
if (instances.Any(x => x.Url == m_url))
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"Other {nameof(HttpSignaling)} exists with same URL:{m_url}. Signaling process may be in conflict.");
|
||||
}
|
||||
|
||||
instances.Add(this);
|
||||
}
|
||||
|
||||
~HttpSignaling()
|
||||
{
|
||||
Stop();
|
||||
instances.Remove(this);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (m_running)
|
||||
throw new InvalidOperationException("This object is already started.");
|
||||
m_running = true;
|
||||
m_signalingThread = new Thread(HTTPPolling);
|
||||
m_signalingThread.IsBackground = true;
|
||||
m_signalingThread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
m_running = false;
|
||||
|
||||
if (m_signalingThread != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Note: Allow for twice the configured m_timeout duration when joining to account for the polling sleep
|
||||
// and the time it takes to send a disconnect to the signaling server.
|
||||
if (!m_signalingThread.Join(m_timeout * 2))
|
||||
{
|
||||
m_signalingThread.Abort();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, "Signaling: HTTP stopping thread error : " + e);
|
||||
}
|
||||
|
||||
m_signalingThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public event OnStartHandler OnStart;
|
||||
public event OnConnectHandler OnCreateConnection;
|
||||
public event OnDisconnectHandler OnDestroyConnection;
|
||||
public event OnOfferHandler OnOffer;
|
||||
public event OnAnswerHandler OnAnswer;
|
||||
public event OnIceCandidateHandler OnIceCandidate;
|
||||
|
||||
public void SendOffer(string connectionId, RTCSessionDescription offer)
|
||||
{
|
||||
DescData data = new DescData();
|
||||
data.connectionId = connectionId;
|
||||
data.sdp = offer.sdp;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ => { HTTPPost("signaling/offer", data); });
|
||||
}
|
||||
|
||||
public void SendAnswer(string connectionId, RTCSessionDescription answer)
|
||||
{
|
||||
DescData data = new DescData();
|
||||
data.connectionId = connectionId;
|
||||
data.sdp = answer.sdp;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ => { HTTPPost("signaling/answer", data); });
|
||||
}
|
||||
|
||||
public void SendCandidate(string connectionId, RTCIceCandidate candidate)
|
||||
{
|
||||
CandidateData data = new CandidateData();
|
||||
data.connectionId = connectionId;
|
||||
data.candidate = candidate.Candidate;
|
||||
data.sdpMLineIndex = candidate.SdpMLineIndex.GetValueOrDefault(0);
|
||||
data.sdpMid = candidate.SdpMid;
|
||||
|
||||
ThreadPool.QueueUserWorkItem(_ => { HTTPPost("signaling/candidate", data); });
|
||||
}
|
||||
|
||||
public void OpenConnection(string connectionId)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => { HTTPConnect(connectionId); });
|
||||
}
|
||||
|
||||
public void CloseConnection(string connectionId)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => { HTTPDisonnect(connectionId); });
|
||||
}
|
||||
|
||||
private void HTTPPolling()
|
||||
{
|
||||
// ignore messages arrived before 30 secs ago
|
||||
m_lastTimeGetAllRequest = DateTime.UtcNow.Millisecond - 30000;
|
||||
|
||||
while (m_running && string.IsNullOrEmpty(m_sessionId))
|
||||
{
|
||||
HTTPCreate();
|
||||
try
|
||||
{
|
||||
Thread.Sleep(m_timeout);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
// Thread.Abort() called from main thread. Ignore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
try
|
||||
{
|
||||
HTTPGetAll();
|
||||
Thread.Sleep(m_timeout);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
// Thread.Abort() called from main thread. Ignore
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, "Signaling: HTTP polling error : " + e);
|
||||
}
|
||||
}
|
||||
HTTPDelete();
|
||||
|
||||
RenderStreaming.Logger.Log("Signaling: HTTP polling thread ended");
|
||||
}
|
||||
|
||||
private static HttpWebResponse HTTPGetResponse(HttpWebRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"Signaling: {response.ResponseUri} HTTP request failed ({response.StatusCode})");
|
||||
response.Close();
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
// Thread.Abort() called from main thread. Ignore
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"Signaling: HTTP request error. url:{request.RequestUri} exception:{e}");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static T HTTPParseJsonResponse<T>(HttpWebResponse response) where T : class
|
||||
{
|
||||
if (response == null) return null;
|
||||
|
||||
T obj = null;
|
||||
|
||||
using (Stream dataStream = response.GetResponseStream())
|
||||
{
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
string responseFromServer = reader.ReadToEnd();
|
||||
obj = JsonUtility.FromJson<T>(responseFromServer);
|
||||
}
|
||||
|
||||
response.Close();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static string HTTPParseTextResponse(HttpWebResponse response)
|
||||
{
|
||||
if (response == null) return null;
|
||||
|
||||
string str = null;
|
||||
|
||||
using (Stream dataStream = response.GetResponseStream())
|
||||
{
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
str = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
response.Close();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
private bool HTTPCreate()
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{m_url}/signaling");
|
||||
request.Method = "PUT";
|
||||
request.ContentType = "application/json";
|
||||
request.KeepAlive = false;
|
||||
request.ContentLength = 0;
|
||||
|
||||
RenderStreaming.Logger.Log($"Signaling: Connecting HTTP {m_url}");
|
||||
|
||||
OpenSessionData resp = HTTPParseJsonResponse<OpenSessionData>(HTTPGetResponse(request));
|
||||
|
||||
if (resp != null)
|
||||
{
|
||||
m_sessionId = resp.sessionId;
|
||||
RenderStreaming.Logger.Log("Signaling: HTTP connected, sessionId : " + m_sessionId);
|
||||
|
||||
m_mainThreadContext.Post(d => OnStart?.Invoke(this), null);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HTTPDelete()
|
||||
{
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{m_url}/signaling");
|
||||
request.Method = "DELETE";
|
||||
request.ContentType = "application/json";
|
||||
request.KeepAlive = false;
|
||||
request.Headers.Add("Session-Id", m_sessionId);
|
||||
|
||||
RenderStreaming.Logger.Log($"Signaling: Removing HTTP connection from {m_url}");
|
||||
|
||||
return (HTTPParseTextResponse(HTTPGetResponse(request)) != null);
|
||||
}
|
||||
|
||||
private bool HTTPPost(string path, object data)
|
||||
{
|
||||
string str = JsonUtility.ToJson(data);
|
||||
byte[] bytes = new System.Text.UTF8Encoding().GetBytes(str);
|
||||
|
||||
RenderStreaming.Logger.Log("Signaling: Posting HTTP data: " + str);
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"{m_url}/{path}");
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/json";
|
||||
request.Headers.Add("Session-Id", m_sessionId);
|
||||
request.KeepAlive = false;
|
||||
|
||||
using (Stream dataStream = request.GetRequestStream())
|
||||
{
|
||||
dataStream.Write(bytes, 0, bytes.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
|
||||
return (HTTPParseTextResponse(HTTPGetResponse(request)) != null);
|
||||
}
|
||||
|
||||
private bool HTTPConnect(string connectionId)
|
||||
{
|
||||
HttpWebRequest request =
|
||||
(HttpWebRequest)WebRequest.Create($"{m_url}/signaling/connection");
|
||||
request.Method = "PUT";
|
||||
request.ContentType = "application/json";
|
||||
request.Headers.Add("Session-Id", m_sessionId);
|
||||
request.KeepAlive = false;
|
||||
|
||||
using (Stream dataStream = request.GetRequestStream())
|
||||
{
|
||||
byte[] bytes = new System.Text.UTF8Encoding().GetBytes($"{{\"connectionId\":\"{connectionId}\"}}");
|
||||
dataStream.Write(bytes, 0, bytes.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
|
||||
HttpWebResponse response = HTTPGetResponse(request);
|
||||
CreateConnectionResData data = HTTPParseJsonResponse<CreateConnectionResData>(response);
|
||||
|
||||
if (data == null) return false;
|
||||
|
||||
RenderStreaming.Logger.Log($"Signaling: HTTP create connection, connectionId: {connectionId}, polite:{data.polite}");
|
||||
m_mainThreadContext.Post(d => OnCreateConnection?.Invoke(this, data.connectionId, data.polite), null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HTTPDisonnect(string connectionId)
|
||||
{
|
||||
HttpWebRequest request =
|
||||
(HttpWebRequest)WebRequest.Create($"{m_url}/signaling/connection");
|
||||
request.Method = "Delete";
|
||||
request.ContentType = "application/json";
|
||||
request.Headers.Add("Session-Id", m_sessionId);
|
||||
request.KeepAlive = false;
|
||||
|
||||
using (Stream dataStream = request.GetRequestStream())
|
||||
{
|
||||
byte[] bytes = new System.Text.UTF8Encoding().GetBytes($"{{\"connectionId\":\"{connectionId}\"}}");
|
||||
dataStream.Write(bytes, 0, bytes.Length);
|
||||
dataStream.Close();
|
||||
}
|
||||
|
||||
var data = HTTPParseTextResponse(HTTPGetResponse(request));
|
||||
|
||||
if (data == null) return false;
|
||||
|
||||
RenderStreaming.Logger.Log("Signaling: HTTP delete connection, connectionId : " + connectionId);
|
||||
m_mainThreadContext.Post(d => OnDestroyConnection?.Invoke(this, connectionId), null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HTTPGetAll()
|
||||
{
|
||||
HttpWebRequest request =
|
||||
(HttpWebRequest)WebRequest.Create($"{m_url}/signaling?fromtime={m_lastTimeGetAllRequest}");
|
||||
request.Method = "GET";
|
||||
request.ContentType = "application/json";
|
||||
request.Headers.Add("Session-Id", m_sessionId);
|
||||
request.KeepAlive = false;
|
||||
|
||||
HttpWebResponse response = HTTPGetResponse(request);
|
||||
AllResData data = HTTPParseJsonResponse<AllResData>(response);
|
||||
|
||||
if (data == null) return false;
|
||||
|
||||
m_lastTimeGetAllRequest =
|
||||
long.TryParse(data.datetime, out var result) ? result : DateTime.Now.ToJsMilliseconds();
|
||||
|
||||
foreach (var msg in data.messages)
|
||||
{
|
||||
if (string.IsNullOrEmpty(msg.type))
|
||||
continue;
|
||||
|
||||
if (msg.type == "disconnect")
|
||||
{
|
||||
m_mainThreadContext.Post(d => OnDestroyConnection?.Invoke(this, msg.connectionId), null);
|
||||
}
|
||||
else if (msg.type == "offer")
|
||||
{
|
||||
DescData offer = new DescData();
|
||||
offer.connectionId = msg.connectionId;
|
||||
offer.sdp = msg.sdp;
|
||||
offer.polite = msg.polite;
|
||||
m_mainThreadContext.Post(d => OnOffer?.Invoke(this, offer), null);
|
||||
}
|
||||
else if (msg.type == "answer")
|
||||
{
|
||||
DescData answer = new DescData
|
||||
{
|
||||
connectionId = msg.connectionId,
|
||||
sdp = msg.sdp
|
||||
};
|
||||
m_mainThreadContext.Post(d => OnAnswer?.Invoke(this, answer), null);
|
||||
}
|
||||
else if (msg.type == "candidate")
|
||||
{
|
||||
CandidateData candidate = new CandidateData
|
||||
{
|
||||
connectionId = msg.connectionId,
|
||||
candidate = msg.candidate,
|
||||
sdpMLineIndex = msg.sdpMLineIndex,
|
||||
sdpMid = msg.sdpMid
|
||||
};
|
||||
m_mainThreadContext.Post(d => OnIceCandidate?.Invoke(this, candidate), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4673647ba304609a37e2762bb1fae36
|
||||
timeCreated: 1586218423
|
||||
130
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/HttpSignalingSettings.cs
vendored
Normal file
130
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/HttpSignalingSettings.cs
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.RenderStreaming.Signaling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
[Serializable, SignalingType("http")]
|
||||
public class HttpSignalingSettings : SignalingSettings
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override Type signalingClass => typeof(HttpSignaling);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override IReadOnlyCollection<IceServer> iceServers => m_iceServers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string url => m_url;
|
||||
|
||||
/// <summary>
|
||||
/// Polling interval
|
||||
/// </summary>
|
||||
public int interval => m_interval;
|
||||
|
||||
[SerializeField, Tooltip("Set the polling frequency (in milliseconds) to the signaling server.")]
|
||||
private int m_interval;
|
||||
[SerializeField, Tooltip("Set the signaling server URL. you should specify a URL starting with \"http\" or \"https\".")]
|
||||
protected string m_url;
|
||||
[SerializeField, Tooltip("Set a list of STUN/TURN servers.")]
|
||||
protected IceServer[] m_iceServers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="iceServers"></param>
|
||||
/// <param name="interval"></param>
|
||||
public HttpSignalingSettings(string url, IceServer[] iceServers = null, int interval = 5000)
|
||||
{
|
||||
if (url == null)
|
||||
throw new ArgumentNullException("url");
|
||||
if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
|
||||
throw new ArgumentException("url is not well formed Uri");
|
||||
|
||||
m_url = url;
|
||||
m_iceServers = iceServers == null ? Array.Empty<IceServer>() : iceServers.Select(server => server.Clone()).ToArray();
|
||||
m_interval = interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public HttpSignalingSettings()
|
||||
{
|
||||
m_url = "http://127.0.0.1";
|
||||
m_iceServers = new[]
|
||||
{
|
||||
new IceServer (urls: new[] {"stun:stun.l.google.com:19302"})
|
||||
};
|
||||
m_interval = 5000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="argumetns"></param>
|
||||
/// <returns></returns>
|
||||
public override bool ParseArguments(string[] arguments)
|
||||
{
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException("arguments");
|
||||
if (arguments.Length == 0)
|
||||
throw new ArgumentException("arguments is empty");
|
||||
if (!CommandLineParser.TryParse(arguments))
|
||||
return false;
|
||||
|
||||
if (CommandLineParser.ImportJson.Value != null)
|
||||
{
|
||||
CommandLineInfo info = CommandLineParser.ImportJson.Value.Value;
|
||||
|
||||
if (info.signalingUrl != null)
|
||||
m_url = info.signalingUrl;
|
||||
if (info.iceServers != null && info.iceServers.Length != 0)
|
||||
m_iceServers = info.iceServers.Select(v => new IceServer(v)).ToArray();
|
||||
}
|
||||
if (CommandLineParser.SignalingUrl.Value != null)
|
||||
m_url = CommandLineParser.SignalingUrl.Value;
|
||||
|
||||
var username = CommandLineParser.IceServerUsername != null
|
||||
? CommandLineParser.IceServerUsername.Value
|
||||
: null;
|
||||
var credential = CommandLineParser.IceServerCredential != null
|
||||
? CommandLineParser.IceServerCredential.Value
|
||||
: null;
|
||||
var credentialType = CommandLineParser.IceServerCredentialType != null
|
||||
? CommandLineParser.IceServerCredentialType.Value
|
||||
: null;
|
||||
var urls = CommandLineParser.IceServerUrls != null
|
||||
? CommandLineParser.IceServerUrls.Value
|
||||
: null;
|
||||
|
||||
if (m_iceServers.Length > 0)
|
||||
m_iceServers[0] = m_iceServers[0].Clone(
|
||||
username: username,
|
||||
credential: credential,
|
||||
credentialType: credentialType,
|
||||
urls: urls);
|
||||
else
|
||||
m_iceServers = new IceServer[]
|
||||
{
|
||||
new IceServer(
|
||||
username: username,
|
||||
credential: credential,
|
||||
credentialType: credentialType.GetValueOrDefault(),
|
||||
urls: urls)
|
||||
};
|
||||
|
||||
if (CommandLineParser.PollingInterval.Value != null)
|
||||
m_interval = CommandLineParser.PollingInterval.Value.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6fb58902f9c27e4688c3a6fe70d9560
|
||||
timeCreated: 1674112040
|
||||
32
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/ISignaling.cs
vendored
Normal file
32
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/ISignaling.cs
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
using Unity.WebRTC;
|
||||
|
||||
namespace Unity.RenderStreaming.Signaling
|
||||
{
|
||||
public delegate void OnStartHandler(ISignaling signaling);
|
||||
public delegate void OnConnectHandler(ISignaling signaling, string connectionId, bool polite);
|
||||
public delegate void OnDisconnectHandler(ISignaling signaling, string connectionId);
|
||||
public delegate void OnOfferHandler(ISignaling signaling, DescData e);
|
||||
public delegate void OnAnswerHandler(ISignaling signaling, DescData e);
|
||||
public delegate void OnIceCandidateHandler(ISignaling signaling, CandidateData e);
|
||||
|
||||
public interface ISignaling
|
||||
{
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
event OnStartHandler OnStart;
|
||||
event OnConnectHandler OnCreateConnection;
|
||||
event OnDisconnectHandler OnDestroyConnection;
|
||||
event OnOfferHandler OnOffer;
|
||||
event OnAnswerHandler OnAnswer;
|
||||
event OnIceCandidateHandler OnIceCandidate;
|
||||
|
||||
string Url { get; }
|
||||
|
||||
void OpenConnection(string connectionId);
|
||||
void CloseConnection(string connectionId);
|
||||
void SendOffer(string connectionId, RTCSessionDescription offer);
|
||||
void SendAnswer(string connectionId, RTCSessionDescription answer);
|
||||
void SendCandidate(string connectionId, RTCIceCandidate candidate);
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/ISignaling.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/ISignaling.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac843bc417af42759d8bd83db0c33d76
|
||||
timeCreated: 1586218157
|
||||
110
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/SignalingMessage.cs
vendored
Normal file
110
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/SignalingMessage.cs
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
[Serializable]
|
||||
public class DescData
|
||||
{
|
||||
public string connectionId;
|
||||
public string sdp;
|
||||
public bool polite;
|
||||
public DateTime dateTime;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CandidateData
|
||||
{
|
||||
public string connectionId;
|
||||
public string participantId;
|
||||
public string candidate;
|
||||
public string sdpMid;
|
||||
public int sdpMLineIndex;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class SignalingMessage
|
||||
{
|
||||
public string status;
|
||||
public string message;
|
||||
public string sessionId;
|
||||
public string connectionId;
|
||||
public string participantId;
|
||||
public bool polite;
|
||||
public string sdp;
|
||||
public string type;
|
||||
public string candidate;
|
||||
public string sdpMid;
|
||||
public int sdpMLineIndex;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class RoutedMessage<T>
|
||||
{
|
||||
public string from;
|
||||
public string to;
|
||||
public string type;
|
||||
public string participantId;
|
||||
public T data;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class OpenSessionData
|
||||
{
|
||||
public string sessionId;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class CreateConnectionResData
|
||||
{
|
||||
public string connectionId;
|
||||
public bool polite;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class DestroyConnectionResData
|
||||
{
|
||||
public string connectionId;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class ConnectionResDataList
|
||||
{
|
||||
public DescData[] connections;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class OfferResDataList
|
||||
{
|
||||
public DescData[] offers;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class AnswerResDataList
|
||||
{
|
||||
public DescData[] answers;
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
class CandidateContainerResDataList
|
||||
{
|
||||
public CandidateContainerResData[] candidates;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class CandidateContainerResData
|
||||
{
|
||||
public string connectionId;
|
||||
public CandidateData[] candidates;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class AllResData
|
||||
{
|
||||
public SignalingMessage[] messages;
|
||||
public string datetime;
|
||||
}
|
||||
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ee3c31b4dd467b47a7050eff711f2c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
187
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/SignalingSettings.cs
vendored
Normal file
187
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/SignalingSettings.cs
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// The attribute is used for commandline argument of "-signalingType".
|
||||
/// </summary>
|
||||
public sealed class SignalingTypeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string typename => m_typename;
|
||||
|
||||
private string m_typename;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="typename"></param>
|
||||
public SignalingTypeAttribute(string name)
|
||||
{
|
||||
m_typename = name;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SignalingSettingsAttribute : PropertyAttribute { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum IceCredentialType
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Password = 0,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
OAuth = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class IceServer
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> urls => m_urls;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string username => m_username;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public IceCredentialType credentialType => m_credentialType;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string credential => m_credential;
|
||||
|
||||
[SerializeField]
|
||||
private string[] m_urls;
|
||||
[SerializeField]
|
||||
private string m_username;
|
||||
[SerializeField]
|
||||
private IceCredentialType m_credentialType;
|
||||
[SerializeField]
|
||||
private string m_credential;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
public static explicit operator RTCIceServer(IceServer server)
|
||||
{
|
||||
var iceServer = new RTCIceServer
|
||||
{
|
||||
urls = server.urls.ToArray(),
|
||||
username = server.username,
|
||||
credential = server.credential,
|
||||
credentialType = (RTCIceCredentialType)server.credentialType
|
||||
};
|
||||
return iceServer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IceServer Clone()
|
||||
{
|
||||
return new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential);
|
||||
}
|
||||
|
||||
public IceServer Clone(string[] urls = null, string username = null, IceCredentialType? credentialType = null, string credential = null)
|
||||
{
|
||||
var server = new IceServer(this.urls.ToArray(), this.username, this.credentialType, this.credential);
|
||||
if (urls != null)
|
||||
server.m_urls = urls;
|
||||
if (username != null)
|
||||
server.m_username = username;
|
||||
if (credentialType != null)
|
||||
server.m_credentialType = credentialType.Value;
|
||||
if (credential != null)
|
||||
server.m_credential = credential;
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="urls"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="credentialType"></param>
|
||||
/// <param name="credential"></param>
|
||||
public IceServer(string[] urls = null, string username = null, IceCredentialType credentialType = IceCredentialType.Password, string credential = null)
|
||||
{
|
||||
m_urls = urls?.ToArray();
|
||||
m_username = username;
|
||||
m_credential = credential;
|
||||
m_credentialType = credentialType;
|
||||
}
|
||||
|
||||
internal IceServer(RTCIceServer server)
|
||||
{
|
||||
m_urls = server.urls.ToArray();
|
||||
m_username = server.username;
|
||||
m_credential = server.credential;
|
||||
m_credentialType = (IceCredentialType)server.credentialType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class SignalingSettings
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract IReadOnlyCollection<IceServer> iceServers { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract Type signalingClass { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="arguments"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool ParseArguments(string[] arguments);
|
||||
}
|
||||
|
||||
internal static class RuntimeTypeCache<T> where T : class
|
||||
{
|
||||
private static Type[] s_types;
|
||||
|
||||
public static Type[] GetTypesDerivedFrom()
|
||||
{
|
||||
if (s_types != null)
|
||||
return s_types;
|
||||
|
||||
s_types = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(domainAssembly => domainAssembly.GetTypes())
|
||||
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsAbstract).ToArray();
|
||||
return s_types;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff254cc8ca9e409bbfe92d5c717e71ab
|
||||
timeCreated: 1674112040
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal class SignalingSettingsObject : ScriptableObject
|
||||
{
|
||||
[SerializeReference]
|
||||
public SignalingSettings settings;
|
||||
|
||||
public static SignalingSettingsObject Create()
|
||||
{
|
||||
var instance = CreateInstance<SignalingSettingsObject>();
|
||||
instance.settings = new WebSocketSignalingSettings();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22c294fb286b37446a86664f4fb41023
|
||||
timeCreated: 1674112040
|
||||
304
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/WebSocketSignaling.cs
vendored
Normal file
304
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/Signaling/WebSocketSignaling.cs
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using WebSocketSharp;
|
||||
|
||||
namespace Unity.RenderStreaming.Signaling
|
||||
{
|
||||
public class WebSocketSignaling : ISignaling
|
||||
{
|
||||
private static HashSet<WebSocketSignaling> instances = new HashSet<WebSocketSignaling>();
|
||||
|
||||
private readonly string m_url;
|
||||
private readonly float m_timeout;
|
||||
private readonly SynchronizationContext m_mainThreadContext;
|
||||
private bool m_running;
|
||||
private Thread m_signalingThread;
|
||||
private readonly AutoResetEvent m_wsCloseEvent;
|
||||
private WebSocket m_webSocket;
|
||||
public string participantId;
|
||||
public string Url { get { return m_url; } }
|
||||
|
||||
public WebSocketSignaling(SignalingSettings signalingSettings, SynchronizationContext mainThreadContext)
|
||||
{
|
||||
if (signalingSettings == null)
|
||||
throw new ArgumentNullException(nameof(signalingSettings));
|
||||
if (!(signalingSettings is WebSocketSignalingSettings settings))
|
||||
throw new ArgumentException("signalingSettings is not WebSocketSignalingSettings");
|
||||
m_url = settings.url;
|
||||
m_timeout = 5.0f;
|
||||
m_mainThreadContext = mainThreadContext;
|
||||
m_wsCloseEvent = new AutoResetEvent(false);
|
||||
|
||||
if (instances.Any(x => x.Url == m_url))
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Warning, $"Other {nameof(WebSocketSignaling)} exists with same URL:{m_url}. Signaling process may be in conflict.");
|
||||
}
|
||||
|
||||
instances.Add(this);
|
||||
}
|
||||
|
||||
~WebSocketSignaling()
|
||||
{
|
||||
if (m_running)
|
||||
Stop();
|
||||
|
||||
instances.Remove(this);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (m_running)
|
||||
throw new InvalidOperationException("This object is already started.");
|
||||
m_running = true;
|
||||
m_signalingThread = new Thread(WSManage);
|
||||
m_signalingThread.Start();
|
||||
}
|
||||
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
m_running = false;
|
||||
m_webSocket?.Close();
|
||||
|
||||
if (m_signalingThread.ThreadState == ThreadState.WaitSleepJoin)
|
||||
{
|
||||
m_signalingThread.Abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_signalingThread.Join(1000);
|
||||
}
|
||||
m_signalingThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public event OnStartHandler OnStart;
|
||||
public event OnConnectHandler OnCreateConnection;
|
||||
public event OnDisconnectHandler OnDestroyConnection;
|
||||
public event OnOfferHandler OnOffer;
|
||||
#pragma warning disable 0067
|
||||
// this event is never used in this class
|
||||
public event OnAnswerHandler OnAnswer;
|
||||
#pragma warning restore 0067
|
||||
public event OnIceCandidateHandler OnIceCandidate;
|
||||
|
||||
public void SendOffer(string connectionId, RTCSessionDescription offer)
|
||||
{
|
||||
DescData data = new DescData();
|
||||
data.connectionId = connectionId;
|
||||
data.sdp = offer.sdp;
|
||||
data.dateTime = DateTime.Now;
|
||||
|
||||
RoutedMessage<DescData> routedMessage = new RoutedMessage<DescData>();
|
||||
routedMessage.from = connectionId;
|
||||
routedMessage.data = data;
|
||||
routedMessage.type = "offer";
|
||||
routedMessage.participantId =participantId;
|
||||
WSSend(routedMessage);
|
||||
}
|
||||
|
||||
public void SendAnswer(string connectionId, RTCSessionDescription answer)
|
||||
{
|
||||
DescData data = new DescData();
|
||||
data.connectionId = connectionId;
|
||||
data.sdp = answer.sdp;
|
||||
|
||||
RoutedMessage<DescData> routedMessage = new RoutedMessage<DescData>();
|
||||
routedMessage.from = connectionId;
|
||||
routedMessage.data = data;
|
||||
routedMessage.type = "answer";
|
||||
routedMessage.participantId =participantId;
|
||||
WSSend(routedMessage);
|
||||
}
|
||||
|
||||
public void SendCandidate(string connectionId, RTCIceCandidate candidate)
|
||||
{
|
||||
CandidateData data = new CandidateData();
|
||||
data.connectionId = connectionId;
|
||||
data.candidate = candidate.Candidate;
|
||||
data.sdpMLineIndex = candidate.SdpMLineIndex.GetValueOrDefault(0);
|
||||
data.sdpMid = candidate.SdpMid;
|
||||
|
||||
RoutedMessage<CandidateData> routedMessage = new RoutedMessage<CandidateData>();
|
||||
routedMessage.from = connectionId;
|
||||
routedMessage.data = data;
|
||||
routedMessage.type = "candidate";
|
||||
|
||||
WSSend(routedMessage);
|
||||
}
|
||||
|
||||
public void OpenConnection(string connectionId)
|
||||
{
|
||||
this.WSSend($"{{\"type\":\"connect\", \"connectionId\":\"{connectionId}\"}}");
|
||||
}
|
||||
|
||||
public void CloseConnection(string connectionId)
|
||||
{
|
||||
this.WSSend($"{{\"type\":\"disconnect\", \"connectionId\":\"{connectionId}\"}}");
|
||||
}
|
||||
|
||||
private void WSManage()
|
||||
{
|
||||
while (m_running)
|
||||
{
|
||||
WSCreate();
|
||||
|
||||
try
|
||||
{
|
||||
m_wsCloseEvent.WaitOne();
|
||||
|
||||
Thread.Sleep((int)(m_timeout * 1000));
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
// Thread.Abort() called from main thread. Ignore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RenderStreaming.Logger.Log("Signaling: WS managing thread ended");
|
||||
}
|
||||
|
||||
private void WSCreate()
|
||||
{
|
||||
m_webSocket = new WebSocket(m_url);
|
||||
if (m_url.StartsWith("wss"))
|
||||
{
|
||||
m_webSocket.SslConfiguration.EnabledSslProtocols =
|
||||
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
|
||||
}
|
||||
|
||||
m_webSocket.OnOpen += WSConnected;
|
||||
m_webSocket.OnMessage += WSProcessMessage;
|
||||
m_webSocket.OnError += WSError;
|
||||
m_webSocket.OnClose += WSClosed;
|
||||
|
||||
Monitor.Enter(m_webSocket);
|
||||
|
||||
RenderStreaming.Logger.Log($"Signaling: Connecting WS {m_url}");
|
||||
m_webSocket.ConnectAsync();
|
||||
}
|
||||
|
||||
private void WSProcessMessage(object sender, MessageEventArgs e)
|
||||
{
|
||||
var content = Encoding.UTF8.GetString(e.RawData);
|
||||
RenderStreaming.Logger.Log($"Signaling: Receiving message: {content}");
|
||||
|
||||
try
|
||||
{
|
||||
var routedMessage = JsonUtility.FromJson<RoutedMessage<SignalingMessage>>(content);
|
||||
|
||||
SignalingMessage msg;
|
||||
if (!string.IsNullOrEmpty(routedMessage.type))
|
||||
{
|
||||
msg = routedMessage.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = JsonUtility.FromJson<SignalingMessage>(content);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(routedMessage.type))
|
||||
{
|
||||
if (routedMessage.type == "connect")
|
||||
{
|
||||
msg = JsonUtility.FromJson<SignalingMessage>(content);
|
||||
m_mainThreadContext.Post(d => OnCreateConnection?.Invoke(this, msg.connectionId, msg.polite), null);
|
||||
participantId=msg.participantId;
|
||||
}
|
||||
else if (routedMessage.type == "disconnect")
|
||||
{
|
||||
msg = JsonUtility.FromJson<SignalingMessage>(content);
|
||||
m_mainThreadContext.Post(d => OnDestroyConnection?.Invoke(this, msg.connectionId), null);
|
||||
}
|
||||
else if (routedMessage.type == "offer")
|
||||
{
|
||||
DescData offer = new DescData();
|
||||
offer.connectionId = routedMessage.from;
|
||||
offer.sdp = msg.sdp;
|
||||
offer.polite = msg.polite;
|
||||
m_mainThreadContext.Post(d => OnOffer?.Invoke(this, offer), null);
|
||||
}
|
||||
else if (routedMessage.type == "answer")
|
||||
{
|
||||
DescData answer = new DescData
|
||||
{
|
||||
connectionId = routedMessage.from,
|
||||
sdp = msg.sdp
|
||||
};
|
||||
m_mainThreadContext.Post(d => OnAnswer?.Invoke(this, answer), null);
|
||||
}
|
||||
else if (routedMessage.type == "candidate")
|
||||
{
|
||||
CandidateData candidate = new CandidateData
|
||||
{
|
||||
connectionId = routedMessage.@from,
|
||||
candidate = msg.candidate,
|
||||
sdpMLineIndex = msg.sdpMLineIndex,
|
||||
sdpMid = msg.sdpMid
|
||||
};
|
||||
m_mainThreadContext.Post(d => OnIceCandidate?.Invoke(this, candidate), null);
|
||||
}
|
||||
else if (routedMessage.type == "error")
|
||||
{
|
||||
msg = JsonUtility.FromJson<SignalingMessage>(content);
|
||||
RenderStreaming.Logger.Log(LogType.Error, msg.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, "Signaling: Failed to parse message: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void WSConnected(object sender, EventArgs e)
|
||||
{
|
||||
RenderStreaming.Logger.Log("Signaling: WS connected.");
|
||||
m_mainThreadContext.Post(d => OnStart?.Invoke(this), null);
|
||||
}
|
||||
|
||||
|
||||
private void WSError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, $"Signaling: WS connection error: {e.Message}");
|
||||
}
|
||||
|
||||
private void WSClosed(object sender, CloseEventArgs e)
|
||||
{
|
||||
RenderStreaming.Logger.Log($"Signaling: WS connection closed, code: {e.Code}");
|
||||
|
||||
m_wsCloseEvent.Set();
|
||||
m_webSocket = null;
|
||||
}
|
||||
|
||||
private void WSSend(object data)
|
||||
{
|
||||
if (m_webSocket == null || m_webSocket.ReadyState != WebSocketState.Open)
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, "Signaling: WS is not connected. Unable to send message");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data is string s)
|
||||
{
|
||||
RenderStreaming.Logger.Log("Signaling: Sending WS data: " + s);
|
||||
m_webSocket.Send(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
string str = JsonUtility.ToJson(data);
|
||||
RenderStreaming.Logger.Log("Signaling: Sending WS data: " + str);
|
||||
m_webSocket.Send(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad8d529710094c2895227a94d10eb8a6
|
||||
timeCreated: 1586218434
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.RenderStreaming.Signaling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Serializable, SignalingType("websocket")]
|
||||
public class WebSocketSignalingSettings : SignalingSettings
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override Type signalingClass => typeof(WebSocketSignaling);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override IReadOnlyCollection<IceServer> iceServers => m_iceServers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string url => m_url;
|
||||
|
||||
[SerializeField, Tooltip("Set the signaling server URL. you should specify a URL starting with \"ws\" or \"wss\".")]
|
||||
protected string m_url;
|
||||
|
||||
[SerializeField, Tooltip("Set a list of STUN/TURN servers.")]
|
||||
protected IceServer[] m_iceServers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="iceServers"></param>
|
||||
public WebSocketSignalingSettings(string url, IceServer[] iceServers = null)
|
||||
{
|
||||
if (url == null)
|
||||
throw new ArgumentNullException("url");
|
||||
if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
|
||||
throw new ArgumentException("url is not well formed Uri");
|
||||
|
||||
m_url = url;
|
||||
m_iceServers = iceServers == null ? Array.Empty<IceServer>() : iceServers.Select(server => server.Clone()).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public WebSocketSignalingSettings()
|
||||
{
|
||||
m_url = "ws://127.0.0.1";
|
||||
m_iceServers = new[]
|
||||
{
|
||||
new IceServer (urls: new[] {"stun:stun.l.google.com:19302"})
|
||||
};
|
||||
}
|
||||
|
||||
public override bool ParseArguments(string[] arguments)
|
||||
{
|
||||
if (arguments == null)
|
||||
throw new ArgumentNullException("arguments");
|
||||
if (arguments.Length == 0)
|
||||
throw new ArgumentException("arguments is empty");
|
||||
|
||||
if (!CommandLineParser.TryParse(arguments))
|
||||
return false;
|
||||
|
||||
if (CommandLineParser.ImportJson.Value != null)
|
||||
{
|
||||
CommandLineInfo info = CommandLineParser.ImportJson.Value.Value;
|
||||
|
||||
if (info.signalingUrl != null)
|
||||
m_url = info.signalingUrl;
|
||||
if (info.iceServers != null && info.iceServers.Length != 0)
|
||||
m_iceServers = info.iceServers.Select(v => new IceServer(v)).ToArray();
|
||||
}
|
||||
if (CommandLineParser.SignalingUrl.Value != null)
|
||||
m_url = CommandLineParser.SignalingUrl.Value;
|
||||
|
||||
var username = CommandLineParser.IceServerUsername != null
|
||||
? CommandLineParser.IceServerUsername.Value
|
||||
: null;
|
||||
var credential = CommandLineParser.IceServerCredential != null
|
||||
? CommandLineParser.IceServerCredential.Value
|
||||
: null;
|
||||
var credentialType = CommandLineParser.IceServerCredentialType != null
|
||||
? CommandLineParser.IceServerCredentialType.Value
|
||||
: null;
|
||||
var urls = CommandLineParser.IceServerUrls != null
|
||||
? CommandLineParser.IceServerUrls.Value
|
||||
: null;
|
||||
|
||||
if (m_iceServers.Length > 0)
|
||||
m_iceServers[0] = m_iceServers[0].Clone(
|
||||
username: username,
|
||||
credential: credential,
|
||||
credentialType: credentialType,
|
||||
urls: urls);
|
||||
else
|
||||
m_iceServers = new IceServer[]
|
||||
{
|
||||
new IceServer(
|
||||
username: username,
|
||||
credential: credential,
|
||||
credentialType: credentialType.GetValueOrDefault(),
|
||||
urls: urls)
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb4c44f4a1edb4d4a9a787ca2046cd1c
|
||||
timeCreated: 1674112040
|
||||
39
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventData.cs
vendored
Normal file
39
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventData.cs
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class SignalingEventData : BaseEventData
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string connectionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public RTCDataChannel channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public RTCRtpTransceiver transceiver { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string sdp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="eventSystem"></param>
|
||||
public SignalingEventData(EventSystem eventSystem) : base(eventSystem)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventData.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventData.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7a7cfbd9ec88fc4b92797186a89e28c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
141
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventProvider.cs
vendored
Normal file
141
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventProvider.cs
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
internal class SignalingEventProvider
|
||||
{
|
||||
private List<WeakReference<GameObject>> m_list = new List<WeakReference<GameObject>>();
|
||||
|
||||
public SignalingEventProvider(IRenderStreamingDelegate handler)
|
||||
{
|
||||
handler.onCreatedConnection += OnCreatedConnection;
|
||||
handler.onDeletedConnection += OnDeletedConnection;
|
||||
handler.onConnect += OnConnect;
|
||||
handler.onDisconnect += OnDisconnect;
|
||||
handler.onGotOffer += OnGotOffer;
|
||||
handler.onGotAnswer += OnGotAnswer;
|
||||
handler.onAddChannel += OnAddChannel;
|
||||
handler.onAddTransceiver += OnAddReceiver;
|
||||
}
|
||||
|
||||
public bool Subscribe(Component comp)
|
||||
{
|
||||
if (Find(comp.gameObject) != null)
|
||||
return false;
|
||||
m_list.Add(new WeakReference<GameObject>(comp.gameObject));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unsubscribe(Component comp)
|
||||
{
|
||||
var a = Find(comp.gameObject);
|
||||
if (a == null)
|
||||
return false;
|
||||
|
||||
m_list.Remove(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
private WeakReference<GameObject> Find(GameObject obj)
|
||||
{
|
||||
return m_list.Find(r =>
|
||||
{
|
||||
if (!r.TryGetTarget(out var _obj))
|
||||
return false;
|
||||
return obj == _obj;
|
||||
});
|
||||
}
|
||||
|
||||
private void ExecuteEventToAllTargets<T>(
|
||||
BaseEventData data, ExecuteEvents.EventFunction<T> functor)
|
||||
where T : IEventSystemHandler
|
||||
{
|
||||
foreach (var r in m_list)
|
||||
{
|
||||
if (!r.TryGetTarget(out var obj))
|
||||
continue;
|
||||
ExecuteEvents.Execute(obj, data, functor);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCreatedConnection(string connectionId)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.createdConnectionHandler);
|
||||
}
|
||||
|
||||
private void OnDeletedConnection(string connectionId)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.deletedConnectionHandler);
|
||||
}
|
||||
|
||||
private void OnGotOffer(string connectionId, string sdp)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId,
|
||||
sdp = sdp
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.offerHandler);
|
||||
}
|
||||
|
||||
private void OnGotAnswer(string connectionId, string sdp)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId,
|
||||
sdp = sdp
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.answerHandler);
|
||||
}
|
||||
|
||||
private void OnConnect(string connectionId)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.connectHandler);
|
||||
}
|
||||
|
||||
private void OnDisconnect(string connectionId)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.disconnectHandler);
|
||||
}
|
||||
|
||||
private void OnAddChannel(string connectionId, RTCDataChannel channel)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId,
|
||||
channel = channel
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.addChannelHandler);
|
||||
}
|
||||
|
||||
private void OnAddReceiver(string connectionId, RTCRtpTransceiver transceiver)
|
||||
{
|
||||
var data = new SignalingEventData(EventSystem.current)
|
||||
{
|
||||
connectionId = connectionId,
|
||||
transceiver = transceiver
|
||||
};
|
||||
ExecuteEventToAllTargets(data, ExecuteSignalingEvents.addReceiverHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventProvider.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingEventProvider.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9c377bd11a4e8547a7105c7defe5612
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
397
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingHandlerBase.cs
vendored
Normal file
397
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingHandlerBase.cs
vendored
Normal file
@@ -0,0 +1,397 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class SignalingHandlerBase : MonoBehaviour
|
||||
{
|
||||
private IRenderStreamingHandler m_handler;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public virtual IEnumerable<Component> Streams => null;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
public virtual void CreateConnection(string connectionId)
|
||||
{
|
||||
m_handler.CreateConnection(connectionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
public virtual void DeleteConnection(string connectionId)
|
||||
{
|
||||
m_handler.DeleteConnection(connectionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool ExistConnection(string connectionId)
|
||||
{
|
||||
return m_handler.ExistConnection(connectionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool IsConnected(string connectionId)
|
||||
{
|
||||
return m_handler.IsConnected(connectionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool IsStable(string connectionId)
|
||||
{
|
||||
return m_handler.IsStable(connectionId);
|
||||
}
|
||||
|
||||
static RTCRtpTransceiverInit GetTransceiverInit(IStreamSender sender)
|
||||
{
|
||||
RTCRtpTransceiverInit init = new RTCRtpTransceiverInit()
|
||||
{
|
||||
direction = RTCRtpTransceiverDirection.SendOnly,
|
||||
};
|
||||
if (sender is VideoStreamSender videoStreamSender)
|
||||
{
|
||||
init.sendEncodings = new RTCRtpEncodingParameters[]
|
||||
{
|
||||
new RTCRtpEncodingParameters()
|
||||
{
|
||||
active = true,
|
||||
minBitrate = (ulong?)videoStreamSender.minBitrate * 1000,
|
||||
maxBitrate = (ulong?)videoStreamSender.maxBitrate * 1000,
|
||||
maxFramerate = (uint?)videoStreamSender.frameRate,
|
||||
scaleResolutionDownBy = videoStreamSender.scaleResolutionDown
|
||||
}
|
||||
};
|
||||
}
|
||||
if (sender is AudioStreamSender audioStreamSender)
|
||||
{
|
||||
init.sendEncodings = new RTCRtpEncodingParameters[]
|
||||
{
|
||||
new RTCRtpEncodingParameters()
|
||||
{
|
||||
active = true,
|
||||
minBitrate = (ulong?)audioStreamSender.minBitrate * 1000,
|
||||
maxBitrate = (ulong?)audioStreamSender.maxBitrate * 1000,
|
||||
}
|
||||
};
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
internal static VideoCodecInfo[] GetVideoCodecInfo(IStreamSender sender)
|
||||
{
|
||||
if (sender is VideoStreamSender videoStreamSender)
|
||||
{
|
||||
if (videoStreamSender.codec == null)
|
||||
return new VideoCodecInfo[] { };
|
||||
return new VideoCodecInfo[] { videoStreamSender.codec };
|
||||
}
|
||||
throw new ArgumentException("sender is not for video streaming.", "sender");
|
||||
}
|
||||
|
||||
internal static AudioCodecInfo[] GetAudioCodecInfo(IStreamSender sender)
|
||||
{
|
||||
if (sender is AudioStreamSender audioStreamSender)
|
||||
{
|
||||
if (audioStreamSender.codec == null)
|
||||
return new AudioCodecInfo[] { };
|
||||
return new AudioCodecInfo[] { audioStreamSender.codec };
|
||||
}
|
||||
throw new ArgumentException("sender is not for audio streaming.", "sender");
|
||||
}
|
||||
|
||||
internal static VideoCodecInfo[] GetVideoCodecInfo(IStreamReceiver receiver)
|
||||
{
|
||||
if (receiver is VideoStreamReceiver videoStreamReceiver)
|
||||
{
|
||||
if (videoStreamReceiver.codec == null)
|
||||
return new VideoCodecInfo[] { };
|
||||
return new VideoCodecInfo[] { videoStreamReceiver.codec };
|
||||
}
|
||||
throw new ArgumentException("receiver is not for video streaming.", "receiver");
|
||||
}
|
||||
|
||||
internal static AudioCodecInfo[] GetAudioCodecInfo(IStreamReceiver receiver)
|
||||
{
|
||||
if (receiver is AudioStreamReceiver audioStreamReceiver)
|
||||
{
|
||||
if (audioStreamReceiver.codec == null)
|
||||
return new AudioCodecInfo[] { };
|
||||
return new AudioCodecInfo[] { audioStreamReceiver.codec };
|
||||
}
|
||||
throw new ArgumentException("receiver is not for audio streaming.", "receiver");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <returns></returns>
|
||||
public virtual void AddSender(string connectionId, IStreamSender sender)
|
||||
{
|
||||
StartCoroutine(AddSenderCoroutine(connectionId, sender));
|
||||
}
|
||||
|
||||
private IEnumerator AddSenderCoroutine(string connectionId, IStreamSender sender)
|
||||
{
|
||||
if (sender.Track == null && sender is StreamSenderBase senderBase)
|
||||
{
|
||||
var op = senderBase.CreateTrack();
|
||||
if (op.Track == null)
|
||||
yield return op;
|
||||
senderBase.SetTrack(op.Track);
|
||||
}
|
||||
if (sender.Track == null)
|
||||
throw new InvalidOperationException("sender.Track is null");
|
||||
|
||||
RTCRtpTransceiverInit init = GetTransceiverInit(sender);
|
||||
var transceiver = m_handler.AddTransceiver(connectionId, sender.Track, init);
|
||||
if (sender is VideoStreamSender videoStreamSender)
|
||||
{
|
||||
var codecs = GetVideoCodecInfo(sender);
|
||||
transceiver.SetCodecPreferences(videoStreamSender.SelectCodecCapabilities(codecs).ToArray());
|
||||
}
|
||||
else if (sender is AudioStreamSender audioStreamSender)
|
||||
{
|
||||
var codecs = GetAudioCodecInfo(sender);
|
||||
transceiver.SetCodecPreferences(audioStreamSender.SelectCodecCapabilities(codecs).ToArray());
|
||||
}
|
||||
sender.SetTransceiver(connectionId, transceiver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="sender"></param>
|
||||
public virtual void RemoveSender(string connectionId, IStreamSender sender)
|
||||
{
|
||||
if (ExistConnection(connectionId))
|
||||
RemoveTrack(connectionId, sender.Track);
|
||||
sender.SetTransceiver(connectionId, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="receiver"></param>
|
||||
/// <param name="transceiver"></param>
|
||||
public virtual void SetReceiver(string connectionId, IStreamReceiver receiver, RTCRtpTransceiver transceiver)
|
||||
{
|
||||
if (receiver is VideoStreamReceiver videoStreamReceiver)
|
||||
{
|
||||
var codecs = GetVideoCodecInfo(receiver);
|
||||
transceiver.SetCodecPreferences(videoStreamReceiver.SelectCodecCapabilities(codecs).ToArray());
|
||||
}
|
||||
else if (receiver is AudioStreamReceiver audioStreamReceiver)
|
||||
{
|
||||
var codecs = GetAudioCodecInfo(receiver);
|
||||
transceiver.SetCodecPreferences(audioStreamReceiver.SelectCodecCapabilities(codecs).ToArray());
|
||||
}
|
||||
receiver.SetTransceiver(connectionId, transceiver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="receiver"></param>
|
||||
public virtual void RemoveReceiver(string connectionId, IStreamReceiver receiver)
|
||||
{
|
||||
receiver.SetTransceiver(connectionId, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="channel"></param>
|
||||
public virtual void AddChannel(string connectionId, IDataChannel channel)
|
||||
{
|
||||
if (channel.IsLocal)
|
||||
{
|
||||
var _channel = m_handler.CreateChannel(connectionId, channel.Label);
|
||||
channel.SetChannel(connectionId, _channel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="channel"></param>
|
||||
public virtual void RemoveChannel(string connectionId, IDataChannel channel)
|
||||
{
|
||||
channel.SetChannel(connectionId, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="track"></param>
|
||||
protected virtual void RemoveTrack(string connectionId, MediaStreamTrack track)
|
||||
{
|
||||
m_handler.RemoveSenderTrack(connectionId, track);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
public virtual void SendOffer(string connectionId)
|
||||
{
|
||||
m_handler.SendOffer(connectionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
public virtual void SendAnswer(string connectionId)
|
||||
{
|
||||
m_handler.SendAnswer(connectionId);
|
||||
}
|
||||
|
||||
internal void SetHandler(IRenderStreamingHandler handler)
|
||||
{
|
||||
m_handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public delegate void OnStartedStreamHandler(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public delegate void OnStoppedStreamHandler(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public delegate void OnStartedChannelHandler(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public delegate void OnStoppedChannelHandler(string connectionId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IStreamSender
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
MediaStreamTrack Track { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<string, RTCRtpTransceiver> Transceivers { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="transceiver"></param>
|
||||
void SetTransceiver(string connectionId, RTCRtpTransceiver transceiver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IStreamReceiver
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
MediaStreamTrack Track { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
RTCRtpTransceiver Transceiver { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="transceiver"></param>
|
||||
void SetTransceiver(string connectionId, RTCRtpTransceiver transceiver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IDataChannel
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool IsLocal { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
string Label { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
string ConnectionId { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
RTCDataChannel Channel { get; }
|
||||
|
||||
///// <summary>
|
||||
/////
|
||||
///// </summary>
|
||||
///// <param name="track"></param>
|
||||
void SetChannel(string connectionId, RTCDataChannel channel);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
void SetChannel(SignalingEventData data);
|
||||
}
|
||||
}
|
||||
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingHandlerBase.cs.meta
vendored
Normal file
3
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingHandlerBase.cs.meta
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d41336fa3d46fd4180ea7aa15e89be7
|
||||
timeCreated: 1606988906
|
||||
351
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManager.cs
vendored
Normal file
351
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManager.cs
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Unity.RenderStreaming.Signaling;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the signaling process for Unity RenderStreaming.
|
||||
/// </summary>
|
||||
/// <seealso cref="ISignaling"/>
|
||||
/// <seealso cref="SignalingSettings"/>
|
||||
/// <seealso cref="SignalingHandlerBase"/>
|
||||
[AddComponentMenu("Render Streaming/Signaling Manager")]
|
||||
public sealed class SignalingManager : MonoBehaviour
|
||||
{
|
||||
internal const string UseDefaultPropertyName = nameof(m_useDefault);
|
||||
internal const string SignalingSettingsObjectPropertyName = nameof(signalingSettingsObject);
|
||||
internal const string SignalingSettingsPropertyName = nameof(signalingSettings);
|
||||
internal const string HandlersPropertyName = nameof(handlers);
|
||||
internal const string RunOnAwakePropertyName = nameof(runOnAwake);
|
||||
internal const string EvaluateCommandlineArgumentsPropertyName = nameof(evaluateCommandlineArguments);
|
||||
|
||||
#pragma warning disable 0649
|
||||
[SerializeField, Tooltip("Use settings in Project Settings Window.")]
|
||||
private bool m_useDefault = true;
|
||||
|
||||
[SerializeField]
|
||||
internal SignalingSettingsObject signalingSettingsObject;
|
||||
|
||||
[SerializeReference, SignalingSettings]
|
||||
private SignalingSettings signalingSettings = new WebSocketSignalingSettings();
|
||||
|
||||
[SerializeField, Tooltip("List of handlers of signaling process.")]
|
||||
private List<SignalingHandlerBase> handlers = new List<SignalingHandlerBase>();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the signaling process should automatically start when the Awake method is called.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Automatically started when called Awake method.")]
|
||||
public bool runOnAwake = true;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether to evaluate command line arguments if launching runtime on the command line.
|
||||
/// </summary>
|
||||
[SerializeField, Tooltip("Evaluate commandline arguments if launching runtime on the command line.")]
|
||||
public bool evaluateCommandlineArguments = true;
|
||||
|
||||
#pragma warning restore 0649
|
||||
|
||||
private SignalingManagerInternal m_instance;
|
||||
private SignalingEventProvider m_provider;
|
||||
private bool m_running;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the signaling process is running.
|
||||
/// </summary>
|
||||
public bool Running => m_running;
|
||||
|
||||
static ISignaling CreateSignaling(SignalingSettings settings, SynchronizationContext context)
|
||||
{
|
||||
if (settings.signalingClass == null)
|
||||
{
|
||||
throw new ArgumentException($"Signaling type is undefined. {settings.signalingClass}");
|
||||
}
|
||||
object[] args = { settings, context };
|
||||
return (ISignaling)Activator.CreateInstance(settings.signalingClass, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use settings in Project Settings.
|
||||
/// </summary>
|
||||
public bool useDefaultSettings
|
||||
{
|
||||
get { return m_useDefault; }
|
||||
set { m_useDefault = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the signaling settings.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var settings = new WebSocketSignalingSettings("ws://example.com", new[]
|
||||
/// {
|
||||
/// new IceServer (urls: new[] {"stun:stun.l.google.com:19302"})
|
||||
/// });
|
||||
/// signalingManager.SetSignalingSettings(settings);
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="settings">The signaling settings.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the signaling process has already started.</exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the settings are null.</exception>
|
||||
public void SetSignalingSettings(SignalingSettings settings)
|
||||
{
|
||||
if (m_running)
|
||||
throw new InvalidOperationException("The Signaling process has already started.");
|
||||
|
||||
if (settings == null)
|
||||
throw new ArgumentNullException("settings");
|
||||
|
||||
signalingSettings = settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the signaling settings.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var settings = signalingManager.GetSignalingSettings();
|
||||
/// if (settings is WebSocketSignalingSettings webSocketSettings)
|
||||
/// {
|
||||
/// Debug.Log($"WebSocket URL: {webSocketSettings.url}");
|
||||
/// }
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <returns>The signaling settings.</returns>
|
||||
public SignalingSettings GetSignalingSettings()
|
||||
{
|
||||
return signalingSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signaling handler.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var handler = instance.GetComponent<Multiplay>();
|
||||
/// signalingManager.AddSignalingHandler(handler);
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="handlerBase">The signaling handler to add.</param>
|
||||
public void AddSignalingHandler(SignalingHandlerBase handlerBase)
|
||||
{
|
||||
if (handlers.Contains(handlerBase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
handlers.Add(handlerBase);
|
||||
|
||||
if (!m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
handlerBase.SetHandler(m_instance);
|
||||
m_provider.Subscribe(handlerBase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a signaling handler.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var handler = instance.GetComponent<Multiplay>();
|
||||
/// signalingManager.RemoveSignalingHandler(handler);
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="handlerBase">The signaling handler to remove.</param>
|
||||
public void RemoveSignalingHandler(SignalingHandlerBase handlerBase)
|
||||
{
|
||||
handlers.Remove(handlerBase);
|
||||
|
||||
if (!m_running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
handlerBase.SetHandler(null);
|
||||
m_provider.Unsubscribe(handlerBase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the signaling process.
|
||||
/// </summary>
|
||||
/// <param name="signaling">The signaling instance to use. If null, a new instance will be created.</param>
|
||||
/// <param name="handlers">The signaling handlers to use. If null, the existing handlers will be used.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// signalingManager.Run();
|
||||
///</code>
|
||||
/// </example>
|
||||
public void Run(
|
||||
ISignaling signaling = null,
|
||||
SignalingHandlerBase[] handlers = null)
|
||||
{
|
||||
_Run(null, signaling, handlers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the signaling process with the specified RTC configuration.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var rtcConfig = new RTCConfiguration
|
||||
/// {
|
||||
/// iceServers = new[] { new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } } }
|
||||
/// };
|
||||
/// signalingManager.Run(rtcConfig);
|
||||
///</code>
|
||||
/// </example>
|
||||
/// <param name="conf">The RTC configuration.</param>
|
||||
/// <param name="signaling">The signaling instance to use. If null, a new instance will be created.</param>
|
||||
/// <param name="handlers">The signaling handlers to use. If null, the existing handlers will be used.</param>
|
||||
/// <remarks>To use this method, the WebRTC package is required.</remarks>
|
||||
public void Run(
|
||||
RTCConfiguration conf,
|
||||
ISignaling signaling = null,
|
||||
SignalingHandlerBase[] handlers = null
|
||||
)
|
||||
{
|
||||
_Run(conf, signaling, handlers);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
bool IsValidSignalingSettingsObject(SignalingSettingsObject asset)
|
||||
{
|
||||
if (asset == null)
|
||||
return false;
|
||||
if (AssetDatabase.GetAssetPath(asset).IndexOf("Assets", StringComparison.Ordinal) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
private void _Run(
|
||||
RTCConfiguration? conf = null,
|
||||
ISignaling signaling = null,
|
||||
SignalingHandlerBase[] handlers = null
|
||||
)
|
||||
{
|
||||
var settings = m_useDefault ? RenderStreaming.GetSignalingSettings<SignalingSettings>() : signalingSettings;
|
||||
#if !UNITY_EDITOR
|
||||
var arguments = Environment.GetCommandLineArgs();
|
||||
if (evaluateCommandlineArguments && arguments.Length > 1)
|
||||
{
|
||||
if (!EvaluateCommandlineArguments(ref settings, arguments))
|
||||
{
|
||||
RenderStreaming.Logger.Log(LogType.Error, "Command line arguments are invalid.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int i = 0;
|
||||
RTCIceServer[] iceServers = new RTCIceServer[settings.iceServers.Count()];
|
||||
foreach (var iceServer in settings.iceServers)
|
||||
{
|
||||
iceServers[i] = (RTCIceServer)iceServer;
|
||||
i++;
|
||||
}
|
||||
RTCConfiguration _conf =
|
||||
conf.GetValueOrDefault(new RTCConfiguration { iceServers = iceServers });
|
||||
|
||||
ISignaling _signaling = signaling ?? CreateSignaling(settings, SynchronizationContext.Current);
|
||||
RenderStreamingDependencies dependencies = new RenderStreamingDependencies
|
||||
{
|
||||
config = _conf,
|
||||
signaling = _signaling,
|
||||
startCoroutine = StartCoroutine,
|
||||
stopCoroutine = StopCoroutine,
|
||||
resentOfferInterval = 5.0f,
|
||||
};
|
||||
var _handlers = (handlers ?? this.handlers.AsEnumerable()).Where(_ => _ != null);
|
||||
if (_handlers.Count() == 0)
|
||||
throw new InvalidOperationException("Handler list is empty.");
|
||||
|
||||
m_instance = new SignalingManagerInternal(ref dependencies);
|
||||
m_provider = new SignalingEventProvider(m_instance);
|
||||
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
handler.SetHandler(m_instance);
|
||||
m_provider.Subscribe(handler);
|
||||
}
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
internal static bool EvaluateCommandlineArguments(ref SignalingSettings settings, string[] arguments)
|
||||
{
|
||||
if (!CommandLineParser.TryParse(arguments))
|
||||
return false;
|
||||
|
||||
string signalingTypeName = null;
|
||||
if (CommandLineParser.SignalingType.Value != null)
|
||||
{
|
||||
signalingTypeName = CommandLineParser.SignalingType;
|
||||
}
|
||||
else if (CommandLineParser.ImportJson.Value != null)
|
||||
{
|
||||
signalingTypeName = CommandLineParser.ImportJson.Value.Value.signalingType;
|
||||
}
|
||||
if (signalingTypeName != null)
|
||||
{
|
||||
Type[] types = RuntimeTypeCache<SignalingSettings>.GetTypesDerivedFrom();
|
||||
Dictionary<string, Type> map =
|
||||
types.Where(type => type.GetCustomAttribute<SignalingTypeAttribute>() != null)
|
||||
.ToDictionary(type => type.GetCustomAttribute<SignalingTypeAttribute>().typename, type => type);
|
||||
|
||||
if (map.ContainsKey(signalingTypeName))
|
||||
{
|
||||
var type = map[signalingTypeName];
|
||||
settings = (SignalingSettings)Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
return settings.ParseArguments(arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the signaling process.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// signalingManager.Stop();
|
||||
///</code>
|
||||
/// </example>
|
||||
public void Stop()
|
||||
{
|
||||
m_instance?.Dispose();
|
||||
m_instance = null;
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!runOnAwake || m_running || handlers.Count == 0)
|
||||
return;
|
||||
|
||||
var settings = m_useDefault ? RenderStreaming.GetSignalingSettings<SignalingSettings>() : signalingSettings;
|
||||
int i = 0;
|
||||
RTCIceServer[] iceServers = new RTCIceServer[settings.iceServers.Count()];
|
||||
foreach (var iceServer in settings.iceServers)
|
||||
{
|
||||
iceServers[i] = (RTCIceServer)iceServer;
|
||||
i++;
|
||||
}
|
||||
RTCConfiguration conf = new RTCConfiguration { iceServers = iceServers };
|
||||
ISignaling signaling = CreateSignaling(settings, SynchronizationContext.Current);
|
||||
_Run(conf, signaling, handlers.ToArray());
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManager.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManager.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 045786cf504bd7347842d6948241cbd0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -200
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
432
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManagerInternal.cs
vendored
Normal file
432
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManagerInternal.cs
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
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;
|
||||
|
||||
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.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;
|
||||
|
||||
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 += () =>
|
||||
{
|
||||
_signaling?.CloseConnection(connectionId);
|
||||
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);
|
||||
}
|
||||
|
||||
RTCSessionDescription description = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = e.sdp };
|
||||
_startCoroutine(pc.OnGotDescription(description, () => onGotOffer?.Invoke(connectionId, e.sdp)));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManagerInternal.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SignalingManagerInternal.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df5c53a4914cce44cb61fe0983c72587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
117
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SingleConnection.cs
vendored
Normal file
117
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SingleConnection.cs
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
public class SingleConnection : SignalingHandlerBase,
|
||||
ICreatedConnectionHandler, IDeletedConnectionHandler,
|
||||
IAddReceiverHandler, IOfferHandler, IAddChannelHandler
|
||||
{
|
||||
[SerializeField] private List<Component> streams = new List<Component>();
|
||||
|
||||
private string connectionId;
|
||||
|
||||
public override IEnumerable<Component> Streams => streams;
|
||||
|
||||
public void AddComponent(Component component)
|
||||
{
|
||||
streams.Add(component);
|
||||
}
|
||||
|
||||
public void RemoveComponent(Component component)
|
||||
{
|
||||
streams.Remove(component);
|
||||
}
|
||||
|
||||
public override void CreateConnection(string connectionId)
|
||||
{
|
||||
this.connectionId = connectionId;
|
||||
base.CreateConnection(connectionId);
|
||||
}
|
||||
|
||||
public override void DeleteConnection(string connectionId)
|
||||
{
|
||||
if (this.connectionId != connectionId)
|
||||
return;
|
||||
Disconnect(connectionId);
|
||||
base.DeleteConnection(connectionId);
|
||||
this.connectionId = null;
|
||||
}
|
||||
|
||||
public void OnCreatedConnection(SignalingEventData data)
|
||||
{
|
||||
if (data.connectionId != connectionId)
|
||||
return;
|
||||
|
||||
foreach (var sender in streams.OfType<IStreamSender>())
|
||||
{
|
||||
AddSender(data.connectionId, sender);
|
||||
}
|
||||
foreach (var channel in streams.OfType<IDataChannel>().Where(c => c.IsLocal))
|
||||
{
|
||||
AddChannel(connectionId, channel);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDeletedConnection(SignalingEventData data)
|
||||
{
|
||||
if (data.connectionId != connectionId)
|
||||
return;
|
||||
Disconnect(connectionId);
|
||||
connectionId = null;
|
||||
}
|
||||
|
||||
private void Disconnect(string 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>())
|
||||
{
|
||||
RemoveChannel(connectionId, channel);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOffer(SignalingEventData data)
|
||||
{
|
||||
if (data.connectionId != connectionId)
|
||||
return;
|
||||
SendAnswer(data.connectionId);
|
||||
}
|
||||
|
||||
public void OnAddReceiver(SignalingEventData data)
|
||||
{
|
||||
if (data.connectionId != connectionId)
|
||||
return;
|
||||
|
||||
var track = data.transceiver.Receiver.Track;
|
||||
IStreamReceiver receiver = GetReceiver(track.Kind);
|
||||
SetReceiver(data.connectionId, receiver, data.transceiver);
|
||||
}
|
||||
|
||||
public void OnAddChannel(SignalingEventData data)
|
||||
{
|
||||
if (data.connectionId != connectionId)
|
||||
return;
|
||||
var channel = streams.OfType<IDataChannel>().FirstOrDefault(r => !r.IsConnected && !r.IsLocal);
|
||||
channel?.SetChannel(connectionId, data.channel);
|
||||
}
|
||||
|
||||
IStreamReceiver GetReceiver(WebRTC.TrackKind kind)
|
||||
{
|
||||
if (kind == WebRTC.TrackKind.Audio)
|
||||
return streams.OfType<AudioStreamReceiver>().First();
|
||||
if (kind == WebRTC.TrackKind.Video)
|
||||
return streams.OfType<VideoStreamReceiver>().First();
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SingleConnection.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/SingleConnection.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ae253ffca93b1b44a471a07cde60141
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamReceiverBase.cs
vendored
Normal file
75
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamReceiverBase.cs
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class StreamReceiverBase : MonoBehaviour, IStreamReceiver
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public RTCRtpTransceiver Transceiver => m_transceiver;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStartedStreamHandler OnStartedStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStoppedStreamHandler OnStoppedStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public MediaStreamTrack Track => m_track;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool isPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(Transceiver.Mid))
|
||||
return false;
|
||||
if (Transceiver.Sender.Track.ReadyState == TrackState.Ended)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private RTCRtpTransceiver m_transceiver;
|
||||
private MediaStreamTrack m_track;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="receiver"></param>
|
||||
public virtual void SetTransceiver(string connectionId, RTCRtpTransceiver transceiver)
|
||||
{
|
||||
if (connectionId == null)
|
||||
throw new ArgumentNullException("connectionId", "connectionId is null");
|
||||
|
||||
m_transceiver = transceiver;
|
||||
m_track = m_transceiver?.Receiver.Track;
|
||||
|
||||
if (m_transceiver == null)
|
||||
OnStoppedStream?.Invoke(connectionId);
|
||||
else
|
||||
OnStartedStream?.Invoke(connectionId);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
m_track?.Dispose();
|
||||
m_track = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamReceiverBase.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamReceiverBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12caa12a46f768a4ebde5979f000a8c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
176
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamSenderBase.cs
vendored
Normal file
176
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamSenderBase.cs
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.WebRTC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.RenderStreaming
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public abstract class StreamSenderBase : MonoBehaviour, IStreamSender
|
||||
{
|
||||
internal class WaitForCreateTrack : CustomYieldInstruction
|
||||
{
|
||||
public MediaStreamTrack Track { get { return m_track; } }
|
||||
|
||||
MediaStreamTrack m_track;
|
||||
|
||||
bool m_keepWaiting = true;
|
||||
|
||||
public override bool keepWaiting { get { return m_keepWaiting; } }
|
||||
|
||||
public WaitForCreateTrack() { }
|
||||
|
||||
public void Done(MediaStreamTrack track)
|
||||
{
|
||||
m_track = track;
|
||||
m_keepWaiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal Coroutine StartCoroutineWithCallback<T>(T coroutine, Action<T> callback) where T : IEnumerator
|
||||
{
|
||||
if (coroutine == null)
|
||||
throw new ArgumentNullException("coroutine");
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
return StartCoroutine(_Coroutine(coroutine, callback));
|
||||
}
|
||||
|
||||
internal IEnumerator _Coroutine<T>(T coroutine, Action<T> callback) where T : IEnumerator
|
||||
{
|
||||
yield return StartCoroutine(coroutine);
|
||||
callback(coroutine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, RTCRtpTransceiver> Transceivers => m_transceivers;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStartedStreamHandler OnStartedStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public OnStoppedStreamHandler OnStoppedStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal abstract WaitForCreateTrack CreateTrack();
|
||||
|
||||
|
||||
internal virtual void ReplaceTrack(MediaStreamTrack newTrack)
|
||||
{
|
||||
if (newTrack == null)
|
||||
throw new ArgumentNullException("track", "This argument must be not null.");
|
||||
|
||||
if (m_track == newTrack)
|
||||
throw new ArgumentException("track", "The value of this argument has already been set.");
|
||||
|
||||
/// todo:: If not disposing the old track here, the app will crash.
|
||||
/// This problem is caused by the MediaStreamTrack when it is destroyed on the thread other than the main thread.
|
||||
m_track?.Dispose();
|
||||
m_track = newTrack;
|
||||
foreach (var transceiver in Transceivers.Values)
|
||||
{
|
||||
transceiver.Sender.ReplaceTrack(m_track);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetTrack(MediaStreamTrack newTrack)
|
||||
{
|
||||
if (newTrack == null)
|
||||
throw new ArgumentNullException("track", "This argument must be not null.");
|
||||
|
||||
if (m_track != null)
|
||||
throw new InvalidOperationException("Track is not null. Use ReplaceTrack method.");
|
||||
m_track = newTrack;
|
||||
}
|
||||
|
||||
private MediaStreamTrack m_track;
|
||||
|
||||
private Dictionary<string, RTCRtpTransceiver> m_transceivers =
|
||||
new Dictionary<string, RTCRtpTransceiver>();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public MediaStreamTrack Track => m_track;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool isPlaying
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var transceiver in Transceivers.Values)
|
||||
{
|
||||
if (string.IsNullOrEmpty(transceiver.Mid))
|
||||
continue;
|
||||
if (transceiver.Sender.Track.ReadyState == TrackState.Ended)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private protected virtual void OnDestroy()
|
||||
{
|
||||
m_track?.Dispose();
|
||||
m_track = null;
|
||||
}
|
||||
|
||||
private protected virtual void OnEnable()
|
||||
{
|
||||
if (m_track?.ReadyState == TrackState.Live)
|
||||
{
|
||||
m_track.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private protected virtual void OnDisable()
|
||||
{
|
||||
if (m_track?.ReadyState == TrackState.Live)
|
||||
{
|
||||
m_track.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connectionId"></param>
|
||||
/// <param name="sender"></param>
|
||||
public virtual void SetTransceiver(string connectionId, RTCRtpTransceiver transceiver)
|
||||
{
|
||||
if (connectionId == null)
|
||||
throw new ArgumentNullException("connectionId is null");
|
||||
if (transceiver == null)
|
||||
{
|
||||
m_transceivers.Remove(connectionId);
|
||||
OnStoppedStream?.Invoke(connectionId);
|
||||
if (!m_transceivers.Any())
|
||||
{
|
||||
m_track.Dispose();
|
||||
m_track = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_transceivers.Add(connectionId, transceiver);
|
||||
OnStartedStream?.Invoke(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamSenderBase.cs.meta
vendored
Normal file
11
Packages/com.unity.renderstreaming@3.1.0-exp.9/Runtime/Scripts/StreamSenderBase.cs.meta
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: efb037253f83cd74d8da87f533785bf6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user