diff --git a/src/Earmark.App/Hosting/HostBuilderExtensions.cs b/src/Earmark.App/Hosting/HostBuilderExtensions.cs index 8c60d73..de1ae44 100644 --- a/src/Earmark.App/Hosting/HostBuilderExtensions.cs +++ b/src/Earmark.App/Hosting/HostBuilderExtensions.cs @@ -47,13 +47,13 @@ public static HostApplicationBuilder ConfigureEarmark(this HostApplicationBuilde builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); return builder; } diff --git a/src/Earmark.Audio/Services/AudioEndpointService.cs b/src/Earmark.Audio/Services/AudioEndpointService.cs index bdc39ab..408c4ca 100644 --- a/src/Earmark.Audio/Services/AudioEndpointService.cs +++ b/src/Earmark.Audio/Services/AudioEndpointService.cs @@ -188,6 +188,10 @@ private List BuildList(DataFlow dataFlow) { _logger.LogWarning(ex, "Failed to map endpoint {Id}", device.ID); } + finally + { + device.Dispose(); + } } return list; diff --git a/src/Earmark.Audio/Services/AudioSessionService.cs b/src/Earmark.Audio/Services/AudioSessionService.cs index d03c761..c955f9c 100644 --- a/src/Earmark.Audio/Services/AudioSessionService.cs +++ b/src/Earmark.Audio/Services/AudioSessionService.cs @@ -112,9 +112,16 @@ private List BuildSnapshot() for (var i = 0; i < sessions.Count; i++) { var session = sessions[i]; - if (TryMap(session, device.ID, out var mapped)) + try { - results.Add(mapped); + if (TryMap(session, device.ID, out var mapped)) + { + results.Add(mapped); + } + } + finally + { + session.Dispose(); } } } @@ -122,6 +129,10 @@ private List BuildSnapshot() { _logger.LogWarning(ex, "Enumerating sessions on {Id} failed", device.ID); } + finally + { + device.Dispose(); + } } return results; @@ -142,15 +153,24 @@ private void AttachAll() foreach (var device in _enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active)) { + var attached = false; try { var watcher = new SessionWatcher(device, this); _watchers[device.ID] = watcher; + attached = true; } catch (Exception ex) { _logger.LogWarning(ex, "Failed to attach session watcher to {Id}", device.ID); } + finally + { + if (!attached) + { + device.Dispose(); + } + } } } @@ -264,8 +284,10 @@ private sealed class SessionWatcher : IAudioSessionEventsHandler, IDisposable private readonly MMDevice _device; private readonly AudioSessionService _owner; private readonly NotificationClient _notify; + private readonly System.Collections.Concurrent.ConcurrentBag _registeredControls = new(); private readonly string _deviceId; + private bool _disposed; public SessionWatcher(MMDevice device, AudioSessionService owner) { @@ -280,10 +302,16 @@ public SessionWatcher(MMDevice device, AudioSessionService owner) private void OnSessionCreated(object sender, IAudioSessionControl newSession) { + if (_disposed) + { + return; + } + try { var control = new AudioSessionControl(newSession); control.RegisterEventClient(this); + _registeredControls.Add(control); if (_owner.TryMap(control, _deviceId, out var mapped)) { _owner._logger.LogInformation( @@ -310,6 +338,13 @@ public void OnGroupingParamChanged(ref Guid groupingId) { } public void Dispose() { + if (_disposed) + { + return; + } + + _disposed = true; + try { _device.AudioSessionManager.OnSessionCreated -= OnSessionCreated; @@ -319,6 +354,12 @@ public void Dispose() // Ignore. } + while (_registeredControls.TryTake(out var control)) + { + try { control.UnRegisterEventClient(this); } catch { } + try { control.Dispose(); } catch { } + } + _device.Dispose(); _ = _notify; }