Deduplicate OBS source state traffic
This commit is contained in:
parent
27ae8dcb81
commit
fc5a9029ea
4
TODO.md
4
TODO.md
@ -59,6 +59,10 @@ audio and initial OBS source inventory their own soft gateway budgets, so normal
|
|||||||
or pathological capture cadence cannot rate-limit and disconnect the Companion
|
or pathological capture cadence cannot rate-limit and disconnect the Companion
|
||||||
control connection.
|
control connection.
|
||||||
|
|
||||||
|
Experimental.10 deduplicates OBS source inventories and unchanged source-state
|
||||||
|
notifications at both ends, removing the high-volume source-update feedback seen
|
||||||
|
with large scene collections while retaining the non-disconnecting safety budget.
|
||||||
|
|
||||||
Release-blocking work remains:
|
Release-blocking work remains:
|
||||||
|
|
||||||
- Package the native whisper.cpp worker as the Windows CUDA runtime, then benchmark
|
- Package the native whisper.cpp worker as the Windows CUDA runtime, then benchmark
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef AppVersion
|
#ifndef AppVersion
|
||||||
#define AppVersion "0.1.0-experimental.9"
|
#define AppVersion "0.1.0-experimental.10"
|
||||||
#endif
|
#endif
|
||||||
#ifndef SourceRoot
|
#ifndef SourceRoot
|
||||||
#error SourceRoot must point at the self-contained Companion publish directory.
|
#error SourceRoot must point at the self-contained Companion publish directory.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
param(
|
param(
|
||||||
[string]$Version = "0.1.0-experimental.9",
|
[string]$Version = "0.1.0-experimental.10",
|
||||||
[string]$BridgeVersion = "0.1.0-experimental.5"
|
[string]$BridgeVersion = "0.1.0-experimental.5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -564,7 +564,10 @@ public sealed class CompanionRuntime : IAsyncDisposable
|
|||||||
ReadString(source, "display_name") ?? ReadString(source, "name") ?? "OBS source",
|
ReadString(source, "display_name") ?? ReadString(source, "name") ?? "OBS source",
|
||||||
ReadBoolean(source, "program_active") || ReadBoolean(source, "active"),
|
ReadBoolean(source, "program_active") || ReadBoolean(source, "active"),
|
||||||
ReadBoolean(source, "source_missing") || ReadBoolean(source, "missing")))
|
ReadBoolean(source, "source_missing") || ReadBoolean(source, "missing")))
|
||||||
.Where(source => Guid.TryParse(source.Uuid, out _)).ToArray();
|
.Where(source => Guid.TryParse(source.Uuid, out _))
|
||||||
|
.GroupBy(source => source.Uuid)
|
||||||
|
.Select(group => group.Last())
|
||||||
|
.ToArray();
|
||||||
_bridgeSelectionAttached = null;
|
_bridgeSelectionAttached = null;
|
||||||
ObsSourcesChanged?.Invoke(ObsSources);
|
ObsSourcesChanged?.Invoke(ObsSources);
|
||||||
foreach (var source in ObsSources) await SendSourceUpdateAsync(source);
|
foreach (var source in ObsSources) await SendSourceUpdateAsync(source);
|
||||||
@ -576,6 +579,8 @@ public sealed class CompanionRuntime : IAsyncDisposable
|
|||||||
var source = new ObsSource(ReadString(message, "source_uuid") ?? string.Empty, ReadString(message, "display_name") ?? "OBS source", ReadBoolean(message, "program_active"), ReadBoolean(message, "source_missing"));
|
var source = new ObsSource(ReadString(message, "source_uuid") ?? string.Empty, ReadString(message, "display_name") ?? "OBS source", ReadBoolean(message, "program_active"), ReadBoolean(message, "source_missing"));
|
||||||
if (Guid.TryParse(source.Uuid, out _))
|
if (Guid.TryParse(source.Uuid, out _))
|
||||||
{
|
{
|
||||||
|
var previous = ObsSources.FirstOrDefault(item => item.Uuid == source.Uuid);
|
||||||
|
if (previous == source) return;
|
||||||
ObsSources = ObsSources.Where(item => item.Uuid != source.Uuid).Append(source).OrderBy(item => item.Name).ToArray();
|
ObsSources = ObsSources.Where(item => item.Uuid != source.Uuid).Append(source).OrderBy(item => item.Name).ToArray();
|
||||||
ObsSourcesChanged?.Invoke(ObsSources);
|
ObsSourcesChanged?.Invoke(ObsSources);
|
||||||
await SendSourceUpdateAsync(source);
|
await SendSourceUpdateAsync(source);
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<Version>0.1.0-experimental.9</Version>
|
<Version>0.1.0-experimental.10</Version>
|
||||||
<AssemblyVersion>0.1.0.0</AssemblyVersion>
|
<AssemblyVersion>0.1.0.0</AssemblyVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -36,6 +36,7 @@ class CompanionGateway {
|
|||||||
let sourceMessagesInWindow = 0;
|
let sourceMessagesInWindow = 0;
|
||||||
let audioMessagesDropped = 0;
|
let audioMessagesDropped = 0;
|
||||||
let sourceMessagesDropped = 0;
|
let sourceMessagesDropped = 0;
|
||||||
|
const sourceStates = new Map();
|
||||||
let messageChain = Promise.resolve();
|
let messageChain = Promise.resolve();
|
||||||
const send = (type, payload, sessionId = session?.id || null) => {
|
const send = (type, payload, sessionId = session?.id || null) => {
|
||||||
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(envelope(type, payload, sessionId)));
|
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(envelope(type, payload, sessionId)));
|
||||||
@ -65,6 +66,13 @@ class CompanionGateway {
|
|||||||
}
|
}
|
||||||
const message = parseEnvelope(body);
|
const message = parseEnvelope(body);
|
||||||
if (helloComplete && message.type === "source_update") {
|
if (helloComplete && message.type === "source_update") {
|
||||||
|
const sourceId = String(message.payload?.source_uuid || "");
|
||||||
|
const signature = JSON.stringify(message.payload || {});
|
||||||
|
if (sourceId && sourceStates.get(sourceId) === signature) return;
|
||||||
|
if (sourceId) {
|
||||||
|
sourceStates.set(sourceId, signature);
|
||||||
|
if (sourceStates.size > 2048) sourceStates.delete(sourceStates.keys().next().value);
|
||||||
|
}
|
||||||
sourceMessagesInWindow += 1;
|
sourceMessagesInWindow += 1;
|
||||||
if (sourceMessagesInWindow > MAX_SOURCE_MESSAGES_PER_SECOND) { sourceMessagesDropped += 1; return; }
|
if (sourceMessagesInWindow > MAX_SOURCE_MESSAGES_PER_SECOND) { sourceMessagesDropped += 1; return; }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"version": "0.1.0-experimental.9",
|
"version": "0.1.0-experimental.10",
|
||||||
"signed": false,
|
"signed": false,
|
||||||
"release_notes": "Prevents OBS audio and initial source-inventory bursts from rate-limiting the Companion connection by sending steady 20 ms audio frames and bounding high-volume capture traffic separately from control messages.",
|
"release_notes": "Prevents OBS capture bursts from rate-limiting the Companion connection, sends steady 20 ms audio frames, and removes duplicate source-state traffic from large scene collections.",
|
||||||
"installer": {
|
"installer": {
|
||||||
"id": "windows-x64-installer",
|
"id": "windows-x64-installer",
|
||||||
"platform": "win32",
|
"platform": "win32",
|
||||||
"architecture": "x64",
|
"architecture": "x64",
|
||||||
"label": "Windows x64 per-user installer",
|
"label": "Windows x64 per-user installer",
|
||||||
"filename": "Lumi.Companion-Setup.exe",
|
"filename": "Lumi.Companion-Setup.exe",
|
||||||
"url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.1.0-experimental.9/Lumi.Companion-Setup.exe",
|
"url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.1.0-experimental.10/Lumi.Companion-Setup.exe",
|
||||||
"sha256": "22bdb0f351e5b4960e2c1ae40e3bdbd8de8a8d6326b30714defb5eb202c0efcf",
|
"sha256": "8207620c1fa2a3f6371c7f5fad40f9a467146a3c44068ed6ae655ee701d98e6a",
|
||||||
"bytes": 32039974
|
"bytes": 32031916
|
||||||
},
|
},
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
@ -20,9 +20,9 @@
|
|||||||
"architecture": "x64",
|
"architecture": "x64",
|
||||||
"label": "Windows x64 self-contained",
|
"label": "Windows x64 self-contained",
|
||||||
"filename": "Lumi.Companion-win-x64.zip",
|
"filename": "Lumi.Companion-win-x64.zip",
|
||||||
"url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.1.0-experimental.9/Lumi.Companion-win-x64.zip",
|
"url": "https://git.rolfsvaag.no/Rolfsvaag_Datateknikk/Lumi/releases/download/companion-v0.1.0-experimental.10/Lumi.Companion-win-x64.zip",
|
||||||
"sha256": "70d9a780859824aa7b8a12e9f25ae8871778b83458889237f171484b2a200dbc",
|
"sha256": "f5a00c9bb778790caec596da475c33b3f7c8e6e67921da75e1e8e3b463bee624",
|
||||||
"bytes": 41757969,
|
"bytes": 41757850,
|
||||||
"entrypoint": "Lumi.Companion.App.exe"
|
"entrypoint": "Lumi.Companion.App.exe"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "lumi_transcription",
|
"id": "lumi_transcription",
|
||||||
"name": "Lumi Transcription",
|
"name": "Lumi Transcription",
|
||||||
"version": "0.1.0-experimental.9",
|
"version": "0.1.0-experimental.10",
|
||||||
"description": "Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions.",
|
"description": "Server-hosted whisper.cpp transcription for Lumi Companion and OBS closed captions.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"channel": "experimental",
|
"channel": "experimental",
|
||||||
|
|||||||
@ -117,6 +117,7 @@ function verifyLocalhostTransportPolicy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function verifyCompanionVersionOrdering() {
|
function verifyCompanionVersionOrdering() {
|
||||||
|
assert.equal(plugin.compareVersions("0.1.0-experimental.10", "0.1.0-experimental.9"), 1);
|
||||||
assert.equal(plugin.compareVersions("0.1.0-experimental.9", "0.1.0-experimental.8"), 1);
|
assert.equal(plugin.compareVersions("0.1.0-experimental.9", "0.1.0-experimental.8"), 1);
|
||||||
assert.equal(plugin.compareVersions("0.1.0-experimental.8", "0.1.0-experimental.7"), 1);
|
assert.equal(plugin.compareVersions("0.1.0-experimental.8", "0.1.0-experimental.7"), 1);
|
||||||
assert.equal(plugin.compareVersions("0.1.0-experimental.7", "0.1.0-experimental.6"), 1);
|
assert.equal(plugin.compareVersions("0.1.0-experimental.7", "0.1.0-experimental.6"), 1);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user