vrubleg / soundkeeper

Prevents SPDIF/HDMI digital audio playback devices from sleeping.
https://veg.by/projects/soundkeeper/
MIT License
213 stars 9 forks source link

Stops working after exclusive mode on Windows 10 #9

Open blebras-fr opened 8 months ago

blebras-fr commented 8 months ago

Hello VEG, hope you're doing good!

Thank you very much for this superb utility. It really is a great solution for my current setup.

I encounter a weird problem that I have trouble replicating. It seems to happen when the Netflix application is ran, but only after a while. Basically, when the problem happens, it's like Soundkeeper doesn't work anymore and I get the audio delay when I start a video (aka same behavior as if SoundKeeper is not started). The process is ongoing and visible in task manager.

My setup is a NUC win10 plugged to TV by HDMI and TV plugged to Samsung soundbar by HDMI.

I tried to run the software with:

Here's the log of the debug soft when the problem happened: problem.log

The program normally logs a lot when Netflix is open without playing but as the log shows, it stopped at some point with Leave rendering thread. Return code: 0.

I also tried to run the soft again to see if it corrected the first one but it didn't work:

2024/01/08 22:03:56.056 [15228] Sound Keeper v1.3.3.0 [2023/08/19]
22:03:56.056 [15228] Enter main thread.
22:03:56.056 [15228] Windows Build Number: 19045 (leaky WASAPI).
22:03:56.056 [15228] Exe File Name: SoundKeeperDigitalOpenOnlyd.exe.
22:03:56.056 [15228] Remaining time to sleeping: -2 seconds.
22:03:56.056 [15228] Device Type: Digital.
22:03:56.056 [15228] Stream Type: None (Open Only).
22:03:56.056 [15228] Periodicity: Disabled.
22:03:56.056 [15228] Stopping another instance...

The first instance was not stopping and the new one just closed its window.

The Netflix app has all those DRM related specifications and I'm wondering if this could cause the problem. If you need me to run any test or any different options, I'll gladly do it as I'm really interest in this tool and ways to help you better it!

I'll try more options and update this post if it's relevant.

Thanks a lot for your help.

vrubleg commented 8 months ago

Thanks for the report, I'll look into it in a while.

Was it a crash (the Sound Keeper process is terminated) or the program just froze at that state (it was still visible in process list)?

blebras-fr commented 8 months ago

Hello VEG, thanks for the quick answer.

The logs have been taken from the debug window that is opened when the debug build is launched. I did not redirect to a file. I would say the process is still running but I saw that the debug window and the Soundkeeper process are different sub processes so I will check again next time it happens to be sure.

vrubleg commented 8 months ago

Oh, if the window stayed open, it means that it just stuck there and didn't crash.

Seems like the issue is related to a workaround for a Windows 10 issue with exclusive mode that has been fixed in Windows 11. The workaround wasn't tested much and seems like needs to be adjusted. I'll look into it.

vrubleg commented 7 months ago

I still didn't look into it and don't know when I'll have enough time for it, but eventually I'll come to it for sure. Just in case, if you can reproduce the issue with SoundKeeper.exe stuck in that state, it would be really helpful if I had a dump of the process in such a broken state. When it is stuck again, just open Task Manager, right click on the process, and choose "Create dump file". It will create a dump of the Sound Keeper process that you could share.

blebras-fr commented 7 months ago

No problem, I also can't do much testing right now. I'll provide the dump when I reproduce the error.

blebras-fr commented 7 months ago

Here is the dump file : SoundKeeperAll.zip Also, the logs : SoundKeeperAll.log

vrubleg commented 7 months ago

The issue is in the workaround for a Windows 10 specific issue (when any program tries to open an audio device that is already used in exclusive mode, Windows Audio Service leaks significant amount of memory). The workaround doesn't work for you as expected because Netflix opens the audio device in exclusive mode, but doesn't have an active audio session that streams something. Sound Keeper tries to find an active audio session as soon as a program opens your audio device in exclusive mode because it's possible to get notifications when that audio session is finished, so Sound Keeper could assume that the audio device is available and could try to use it in shared mode again. Unfortunately, there are no reliable events that would allow the program to detect when exclusive mode is not used anymore (instead of just trying to open the audio device), so I had to use this trick that doesn't work in your case.

I'll think what to do with this. Meanwhile, you could consider upgrading your OS to Windows 11 where the original memory leak issue is apparently fixed and the workaround is not used.

I reported that bug to Microsoft a few years before before Windows 11 release. They fixed it, but for some reason in Windows 11 only. I expected that the bugfix would be delivered with Windows 10 updates to Windows 10, but it didn't happen. Apparently, somebody at Microsoft thinks that a memory leak is not critical enough to port the fix to Windows 10.

vrubleg commented 7 months ago

So the original issue is in the detection that a device is being used in exclusive mode. There are no events related to this that would allow a program to just subscribe to that event and wait until the device is free again. The only way is to poll the device from time to time with IAudioClient::Initialize, it returns AUDCLNT_E_DEVICE_IN_USE when the device is being used in exclusive mode. But there is a bug in WASAPI on Windows 10: it leaks some amount of memory in audio service on every such attempt.

I made a hacky workaround to avoid calling IAudioClient::Initialize frequently and it works in simple cases when a program opens an audio device in exclusive mode and immediately plays something, but apparently it doesn't work as expected in all cases and Sound Keeper decides to stop itself to not leak memory. If I disable the workaround, Sound Keeper won't stop itself and will work as expected, but the memory will be slowly leaking in the audio service.

I'm open to any simple ideas how to avoid this memory leak and detect that a device is busy.

Currently I see such crazy ways of working this around:

  1. Hook the Windows Audio Service, monitor all memory allocations, detect when the memory leak happens and help the service to free this memory.
  2. Make a custom patch for the Windows Audio Service that fixes the bug.
  3. Hook IAudioClient functions system-wide to detect when apps are entering exclusive mode and leaving it.

I don't like any of them because it requires to get into other processes and interfere with them. And also it's quite a lot of effort to implement any of them. Eventually, Windows 10 will be replaced by newer Windows versions and this hack will become obsolete, so don't want to spend really much time on this.

I'm open to any ideas how Sound Keeper could detect that a device is busy without leaking memory.

Currently I'm considering just giving up on the workaround and making Sound Keeper polling the audio device infrequently to make sure that even though memory is leaking, the amount is negligible even when exclusive mode is being used for hours.

Also, as a cheap solution, Sound Keeper could ask for admin rights that would allow to restart the Windows Audio Serivce after exclusive mode was used (so all the leaked memory would be cleared this way).

vrubleg commented 7 months ago

Another idea. Probably it is possible to monitor output level in shared mode that is displayed in volume mixer as a green jumping bar, and most probably it doesn't show anything in exclusive mode. If it is the case, I could try to monitor this output level and when it's non-zero, it would mean that exclusive mode is over and something is playing in shared mode.

But there are drawbacks. Even if it works, the first sound after exclusive mode will be trimmed, and very short sounds might not be detected by Sound Keeper.

Another idea. Monitor all sound sessions and try to reinitialize whenever there are any changes. Currently used workaround actually does almost this. It monitors all audio sessions to find the one that is most probably related to the exclusive mode (that fails in your case for some reason, seems like your app doesn't create an audio session even though the device is already opened in exclusive mode). When that session is disconnected, it is used as a signal that exclusive mode probably is over.

hwoarang5 commented 7 months ago

guess its not working for win10 user? wish nvidia able to fix it or include a fix in their hdmi audio drivers.

vrubleg commented 7 months ago

It might stop working if you use exclusive mode that is quite rare.

blebras-fr commented 7 months ago

I don't have smart ideas to circumvent the problem and I don't think giving the admin access is good. As you said, W10 will disappear so I just upgraded to W11. Thanks for your time @vrubleg.