I'm not aware of a universal method which is guaranteed to work for every possible video playback application. However, in practice, powercfg -requests
output will very often tell you if a video is playing. For example:
C:\> powercfg -requests
DISPLAY:
[PROCESS] \Device\HarddiskVolume3\Program Files\Google\Chrome\Application\chrome.exe
Video Wake Lock
SYSTEM:
[DRIVER] NVIDIA High Definition Audio (HDAUDIO\FUNC_01&VEN_10DE&DEV_0099&SUBSYS_1462C75A&REV_1001\5&c8f5c72&0&0001)
An audio stream is currently in use.
AWAYMODE:
None.
EXECUTION:
[PROCESS] \Device\HarddiskVolume3\Program Files\Google\Chrome\Application\chrome.exe
Playing audio
PERFBOOST:
None.
ACTIVELOCKSCREEN:
None.
As you can see, if Chrome is actively playing a video, it will make a DISPLAY
power request with reason Video Wake Lock
. If you pause or stop the video, the power request is withdrawn. (Recent versions of Microsoft Edge do the same, as does Brave-unsurprising given both are mostly the same code as Chrome.)
A DISPLAY
power request isn't necessarily a video though – it could be a computer game or a slide deck, for example. For Chrome, it actually tells you it is a video using the reason message – but not every video player does that. The legacy Windows Media Player 12 (at least in my testing with version 19041.2673) makes a DISPLAY
power request, but doesn't populate a reason. (The ability to set a reason on a power request is a relatively new feature of Windows, and Microsoft appears to have never added code to the legacy Windows Media Player to exploit it.) Still, you can see the executable name is wmplayer.exe
. Similarly, VideoLan player (VLC) doesn't set a request reason either, but again, you can look at the executable name vlc.exe
.
So, by looking at power requests, combined with some heuristics, you can detect video playblack with significant (likely >80% in practice) but not perfect accuracy. A basic heuristic could be:
- Check if there is a
DISPLAY
power request whose reason contains Video
(case-insensitive)
- Otherwise, check if there is a
DISPLAY
power request from a known video player executable (e.g. wmplayer.exe
)
- If there is a
DISPLAY
power request from unknown executable, you could optionally look at its version resource strings (in particular the description) to see if it contains Media Player
, Video Player
, etc
If you want to do this programmatically: powercfg -requests
ends up invoking the NtPowerInformation
system call with a POWER_INFORMATION_LEVEL
of GetPowerRequestList
. Microsoft has never officially documented the format of the buffer that gets returned, but it has been reverse engineered. Someone has posted open source code on GitHub (diversenok/Powercfg
) which does the same as powercfg -requests
does, so you could use that code to do it.
Microsoft Photos app (distributed with Windows 10/11) is a good example of where these kinds of heuristics don't quite work though. Despite its name, it doesn't just display photos, it plays videos too. Microsoft.Photos.exe
doesn't sound like the name of a video player app though. It does set a reason, but the entirely useless reason of Windows Runtime Package: Microsoft.Windows.Photos_8wekyb3d8bbwe
. However, in my testing, it only sets a DISPLAY
request for video playback, not when showing a photo (not even for a fullscreen photo). Furthermore, while all three of Chrome, Windows Media Player and VLC withdraw the DISPLAY
power request when the video is paused, Microsoft Photos doesn't. Active audio output (e.g. a SYSTEM
request from the audio driver, as shown in this example) can be used to distinguish a paused from unpaused video, but that is not 100% accurate (what if the video has no audio? or is muted?)
Another potential gotcha: Video Wake Lock
is the string in English, but if the user is using another language edition of Windows, it is possible the string is in their own language instead of English, so heuristics based on searching for the keyword Video
may break for non-English users. There are two types of reason strings – DIAGNOSTIC_REASON_SIMPLE_STRING
, which is just a raw string, and DIAGNOSTIC_REASON_DETAILED_STRING
, which is loading a string resource with the DLL. For the simple string, you just have to hope it is in English, or else you can try searching for keywords in multiple languages (which languages to support? although video
, ignoring case and accents, actually works for the majority of European languages). For the detailed string, you could try using SetThreadUILanguage
to temporarily change the thread language to English before calling LoadStringW
to load the actual string. (Then again, it is also possible that, for your particular scenario, non-English support is a non-requirement.)