Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented high-volume events, dynamic reidentify, new event protocol #92

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 76 additions & 3 deletions obs-websocket-dotnet/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -268,7 +270,27 @@ public partial class OBSWebsocket
/// <summary>
/// A high-volume event providing volume levels of all active inputs every 50 milliseconds.
/// </summary>
public event EventHandler<InputVolumeMetersEventArgs> InputVolumeMeters;
public event EventHandler<InputVolumeMetersEventArgs> 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<InputVolumeMetersEventArgs> inputVolumeMeters;

/// <summary>
/// The replay buffer has been saved.
Expand All @@ -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();
}

/// <summary>
/// Send a Reidentify with new event subscriptions
/// </summary>
/// <returns>true if we sent the reidentify, false if failed</returns>
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

/// <summary>
Expand Down Expand Up @@ -500,7 +573,7 @@ protected void ProcessEventType(string eventType, JObject body)
break;

case nameof(InputVolumeMeters):
InputVolumeMeters?.Invoke(this, new InputVolumeMetersEventArgs(JsonConvert.DeserializeObject<List<JObject>>((string)body["inputs"])));
inputVolumeMeters?.Invoke(this, new InputVolumeMetersEventArgs(body["inputs"].ToObject<List<InputVolumeMeter>>()));
break;

case nameof(ReplayBufferSaved):
Expand Down
3 changes: 2 additions & 1 deletion obs-websocket-dotnet/OBSWebsocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions obs-websocket-dotnet/Types/EventSubscription.cs
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ public class InputVolumeMetersEventArgs : EventArgs
/// <summary>
/// Array of active inputs with their associated volume levels
/// </summary>
public List<JObject> inputs { get; }
public List<InputVolumeMeter> inputs { get; }

/// <summary>
/// Default Constructor
/// </summary>
/// <param name="inputs">Collection inputs as JObjects</param>
public InputVolumeMetersEventArgs(List<JObject> inputs)
public InputVolumeMetersEventArgs(List<InputVolumeMeter> inputs)
{
this.inputs = inputs;
}
Expand Down
69 changes: 69 additions & 0 deletions obs-websocket-dotnet/Types/InputVolumeMeter.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Name of the input
/// </summary>
[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<float[]>();

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;
}

/// <summary>
/// Array of channels on this input
/// </summary>
[JsonProperty(PropertyName = "inputLevelsMul")]
public List<ChannelLevel> InputLevels { set; get; }


}
}