Bump to v2.2.0: Opus native binding, efficiency tidy-up, diag self-meter

Single biggest change: added the Concentus.Native NuGet package. Concentus
2.0+ auto-detects native libopus at runtime and routes encode calls
through it; encoder state lives on the C side and is reused across calls
rather than `new`ing ~15 working buffers per call (Concentus issue #22,
open since 2018). Measured on the desktop test at 15:36:55 — Opus 10 ms
allocation rate dropped from 4,625 KB/s to 108 KB/s, a 97.7% reduction.
Process CPU dropped from 4.7% to 1.6% in the same config. Audio is bit-
for-bit identical (it's literally the same encoder, just better
packaged). `OpusEncoderState.cs` itself unchanged on the call site.

Diagnostic / measurement layer (gated on Enable-logs, zero cost when off):
* ProcessSelfMeter: CPU%, managed heap MB, working set MB, allocation
  rate per second, GC counts per generation
* Per-thread work-time counters: captureMs / sendMs / recvMs / renderMs
  expressed as milliseconds of CPU consumed by each audio thread per
  second
* Inter-packet arrival gap measured at the user-space UDP socket
  (rxNetGapMs) — pinpoints whether arrival jitter is in the network or
  our own dispatch path

Small efficiency wins (each one was small but cumulative):
* deviceRefreshTimer interval 1s -> 3s (item 4)
* WaitHandle array allocations eliminated in MixingEngine.MixLoop and
  MultiOutputPlayout.ProduceLoop (item 6)
* MultiOutputPlayout caches its output-buffer snapshot and only rebuilds
  on SetOutputDevices, instead of rebuilding every 10 ms (item 7)
* HeartbeatService reuses an outbound ping byte[] instead of allocating
  per send (item 14)
* PeerDiscoveryService caches broadcast addresses and invalidates on
  Windows' NetworkChange event instead of walking all NICs every 1.5 s
  (item 16)

Legacy / dead-code removal:
* KeepAlive packet's implementation (struct, enums, writer, reader, size
  constant) — all dead since HeartbeatService landed 2026-05-06. Kept
  the RemPacketType.KeepAlive enum value and silent-drop dispatch for
  wire compat with any pre-2026-05-06 build still in the wild (item 30)
* driftDropFramesTotal / driftRepeatFramesTotal fields and accessors —
  Phase-2 splice corrector relics, never incremented since Phase-4
  resampler design landed; backed five always-zero diag log columns
  (items 34 + 35)
* DriftAccumulator (always returned 0) — same shape, removed alongside
  the driftAcc= column (item 35)
* TakeMaxFanOutCacheBytes / Ms + fanCacheMs column — FanOutSource was
  retired in May (item 36)

Project documentation:
* RemSoundefficiency.md added as the canonical record of the efficiency
  analysis, every item's status, and the measured wins from this round
* Honest item-by-item review of the original 50-item list — several
  items I had sized optimistically in the original analysis turned out
  to be already-done (item 20), already-optimal (item 22), or below
  the meter floor (items 9, 15, 17, 25). Recorded so future passes
  don't re-investigate.

Wire format and audio pipeline unchanged from v1.5 onward — v1.5 through
v2.2 peers interoperate.
This commit is contained in:
Ednunp
2026-05-23 15:56:03 +01:00
parent 79b28b6c02
commit 6d6d6897e4
22 changed files with 847 additions and 232 deletions
+17 -1
View File
@@ -14,7 +14,7 @@
tag_name on the latest GitHub release; bump it on every public release. The
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
is what the About dialog and the updater both read. -->
<Version>2.1.0</Version>
<Version>2.2.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -38,6 +38,22 @@
manual port forwarding. Cross-protocol — picks whichever the router speaks. Used
under the AppConfig.UpnpEnabled toggle, off by default. -->
<PackageReference Include="Mono.Nat" Version="3.0.4" />
<!-- Native libopus binaries that Concentus 2.0+ auto-detects at runtime and routes
encode/decode calls through. Without this package Concentus uses its pure-managed
C# fallback, which `new`s ~15 working buffers per encode call (issue #22 on the
Concentus repo, open since 2018) and produces ~4.5 MB/s of GC pressure per
encoding lane at 10 ms frames. Switching to native via this package keeps the
encoder state allocated once (C-side) and reuses it across calls. Same encoder
settings, bit-for-bit identical audio output. Installed at the top-level project
(per the package's install guidance) so `dotnet publish` correctly trims native
binaries for irrelevant RIDs from the release output. 2026-05-23. -->
<PackageReference Include="Concentus.Native" Version="1.5.2" />
<!-- Explicit pin for the transitive Concentus.Native.NetCore. The parent package
declares a minimum version of 1.5.1, but 1.5.1 was never published to nuget.org —
only 1.5.2 was. NuGet still resolves correctly (it picks 1.5.2) but emits NU1603
as a warning, which our TreatWarningsAsErrors policy promotes to an error.
Pinning explicitly skips that warning and is self-documenting. -->
<PackageReference Include="Concentus.Native.NetCore" Version="1.5.2" />
</ItemGroup>
<ItemGroup>