diff --git a/obs-websocket-dotnet/Events.cs b/obs-websocket-dotnet/Events.cs index 4a3504f..e25b543 100644 --- a/obs-websocket-dotnet/Events.cs +++ b/obs-websocket-dotnet/Events.cs @@ -2,10 +2,12 @@ using Newtonsoft.Json.Linq; using OBSWebsocketDotNet.Communication; using OBSWebsocketDotNet.Types; +using OBSWebsocketDotNet.Types.Events; using System; using System.Collections.Generic; using System.Diagnostics; -using OBSWebsocketDotNet.Types.Events; +using System.Net.WebSockets; +using Websocket.Client; namespace OBSWebsocketDotNet { @@ -268,7 +270,27 @@ public partial class OBSWebsocket /// /// A high-volume event providing volume levels of all active inputs every 50 milliseconds. /// - public event EventHandler InputVolumeMeters; + public event EventHandler InputVolumeMeters + { + // This event needs special subscription, handle that here + add + { + if (inputVolumeMeters == null || inputVolumeMeters.GetInvocationList().Length == 0) + { + RegisterEvent(EventSubscription.InputVolumeMeters); + } + inputVolumeMeters += value; + } + remove + { + inputVolumeMeters -= value; + if (inputVolumeMeters == null || inputVolumeMeters.GetInvocationList().Length == 0) + { + UnRegisterEvent(EventSubscription.InputVolumeMeters); + } + } + } + private event EventHandler inputVolumeMeters; /// /// The replay buffer has been saved. @@ -292,6 +314,57 @@ public partial class OBSWebsocket #endregion + + #region EventSubscription + + private EventSubscription registeredEvents = EventSubscription.All; + + private void RegisterEvent(EventSubscription newSubscription) + { + registeredEvents |= newSubscription; + SendReidentify(); + } + + private void UnRegisterEvent(EventSubscription removeSubscription) + { + registeredEvents &= ~removeSubscription; + SendReidentify(); + } + + /// + /// Send a Reidentify with new event subscriptions + /// + /// true if we sent the reidentify, false if failed + protected bool SendReidentify() + { + if (wsConnection == null || !wsConnection.IsStarted) return false; // #TODO check if we are in Identified/Connected state, if we didn't send our Identify yet, we shouldn't send Reidentify now + + var requestFields = new JObject + { + { "eventSubscriptions", (uint)registeredEvents } + }; + + try + { + // Throws ErrorResponseException if auth fails + SendRequest(MessageTypes.ReIdentify, null, requestFields, false); + } + catch (ErrorResponseException ex) + { + Disconnected?.Invoke(this, new ObsDisconnectionInfo( + ObsCloseCodes.UnknownReason, + "Reidentify Failed", + new DisconnectionInfo(DisconnectionType.Error, WebSocketCloseStatus.ProtocolError, "Reidentify Failed", String.Empty, new AuthFailureException()) + )); + Disconnect(); + return false; + } + + return true; + } + + #endregion + #region EventProcessing /// @@ -500,7 +573,7 @@ protected void ProcessEventType(string eventType, JObject body) break; case nameof(InputVolumeMeters): - InputVolumeMeters?.Invoke(this, new InputVolumeMetersEventArgs(JsonConvert.DeserializeObject>((string)body["inputs"]))); + inputVolumeMeters?.Invoke(this, new InputVolumeMetersEventArgs(body["inputs"].ToObject>())); break; case nameof(ReplayBufferSaved): diff --git a/obs-websocket-dotnet/OBSWebsocket.cs b/obs-websocket-dotnet/OBSWebsocket.cs index b3c3770..8c488de 100644 --- a/obs-websocket-dotnet/OBSWebsocket.cs +++ b/obs-websocket-dotnet/OBSWebsocket.cs @@ -287,7 +287,8 @@ protected void SendIdentify(string password, OBSAuthInfo authInfo = null) { var requestFields = new JObject { - { "rpcVersion", SUPPORTED_RPC_VERSION } + { "rpcVersion", SUPPORTED_RPC_VERSION }, + { "eventSubscriptions", (uint)registeredEvents } }; if (authInfo != null) diff --git a/obs-websocket-dotnet/Types/EventSubscription.cs b/obs-websocket-dotnet/Types/EventSubscription.cs new file mode 100644 index 0000000..629a501 --- /dev/null +++ b/obs-websocket-dotnet/Types/EventSubscription.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OBSWebsocketDotNet.Types +{ + [Flags] + internal enum EventSubscription + { + None = 0, + General = (1 << 0), + Config = (1 << 1), + Scenes = (1 << 2), + Inputs = (1 << 3), + Transitions = (1 << 4), + Filters = (1 << 5), + Outputs = (1 << 6), + SceneItems = (1 << 7), + MediaInputs = (1 << 8), + Vendors = (1 << 9), + Ui = (1 << 10), + All = (General | Config | Scenes | Inputs | Transitions | Filters | Outputs | SceneItems | MediaInputs | Vendors | Ui), + + // High volume event need separate subscription + + InputVolumeMeters = (1 << 16), + InputActiveStateChanged = (1 << 17), + InputShowStateChanged = (1 << 18), + SceneItemTransformChanged = (1 << 19) + } +} diff --git a/obs-websocket-dotnet/Types/Events/InputVolumeMetersEventArgs.cs b/obs-websocket-dotnet/Types/Events/InputVolumeMetersEventArgs.cs index 647929c..11dace7 100644 --- a/obs-websocket-dotnet/Types/Events/InputVolumeMetersEventArgs.cs +++ b/obs-websocket-dotnet/Types/Events/InputVolumeMetersEventArgs.cs @@ -12,13 +12,13 @@ public class InputVolumeMetersEventArgs : EventArgs /// /// Array of active inputs with their associated volume levels /// - public List inputs { get; } + public List inputs { get; } /// /// Default Constructor /// /// Collection inputs as JObjects - public InputVolumeMetersEventArgs(List inputs) + public InputVolumeMetersEventArgs(List inputs) { this.inputs = inputs; } diff --git a/obs-websocket-dotnet/Types/InputVolumeMeter.cs b/obs-websocket-dotnet/Types/InputVolumeMeter.cs new file mode 100644 index 0000000..2f5c076 --- /dev/null +++ b/obs-websocket-dotnet/Types/InputVolumeMeter.cs @@ -0,0 +1,69 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace OBSWebsocketDotNet.Types +{ + public class InputVolumeMeter + { + /// + /// Name of the input + /// + [JsonProperty(PropertyName = "inputName")] + public string InputName { set; get; } + + // Convert json Array of 3 scalars, into a struct + private class ChannelLevelConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ChannelLevel); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.StartArray) + throw new ProtocolViolationException("Expected InputVolumeMeter/inputLevelsMul to be an array"); + + JToken token = JToken.Load(reader); + var items = token.ToObject(); + + if (items.Length != 3) + throw new ProtocolViolationException($"Expected InputVolumeMeter/inputLevelsMul to be an 3 element array, but instead got {items.Length} element array"); + + ChannelLevel contentStruct; + contentStruct.PeakRaw = items[0]; + contentStruct.PeakWithVolume = items[1]; + contentStruct.magnitudeWithVolume = items[2]; + return contentStruct; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + [JsonConverter(typeof(ChannelLevelConverter))] + public struct ChannelLevel + { + // https://github.com/obsproject/obs-websocket/blob/f4b72b69ce7f9ec6a5fdb1b06971e00d2b091bec/src/utils/Obs_VolumeMeter.cpp#L87 + + + public float magnitudeWithVolume; + public float PeakWithVolume; + public float PeakRaw; + } + + /// + /// Array of channels on this input + /// + [JsonProperty(PropertyName = "inputLevelsMul")] + public List InputLevels { set; get; } + + + } +}