Hardware versus software decoding: why your fan spins up
One is a fixed circuit that either exists for your codec or does not. The other is your CPU doing the same job with general-purpose instructions. There is no middle setting.
Two ways to turn a compressed video stream into pictures. They produce the same output, and almost nothing else about them is the same.
What each one is
Hardware decoding hands the compressed stream to a fixed-function block on your GPU. That block implements one specific codec in silicon. It is not a faster version of the software decoder; it is dedicated circuitry that happens to produce the same frames, using almost no CPU and very little power.
Software decoding runs the decoder as ordinary code on your CPU, across several cores. It works for any codec the software supports, which is nearly all of them, and it costs real CPU time.
The distinction that matters most: hardware support is binary per codec. Your GPU either has a decoder for AV1 or it does not. There is no partial acceleration, no degraded mode, no "mostly hardware". If the block is absent, the work falls entirely to software.
The setting, and what it actually emits
Three states, mapping to three decoder selections handed to the player with each piece of media:
Auto avcodec-hw=d3d11va ask for the Direct3D 11 video decoder
On avcodec-hw=any take any hardware decoder available
Off avcodec-hw=none software only
Auto is right for almost everyone. Off is a debugging tool: if a channel shows corruption or fails to open, forcing software decode tells you within one attempt whether you are looking at a driver problem or a stream problem.
One structural detail, because it caused a real bug. This is a per-media option, applied every time a piece of media is opened, rather than an instance-level switch set once at startup. That is the right design. It also means each playback path has to remember to apply it.
The bug that hid in exactly that gap
Until 2026-08-23, live TV honoured the acceleration setting and the on-demand path hard-coded it to off. Every film and every episode was software decoded, regardless of what the setting said, on every machine.
We got this wrong, and nothing about it was visible from the interface. The setting existed, it persisted, it said Auto, and films decoded in software anyway. It surfaced as "films make the fan spin up and live TV does not", a symptom easy to blame on file size or bitrate.
After the fix, the same 4K title played with the GPU video-decode engines at about 11 percent. Same file, same machine, same setting.
The reason to write this down rather than fix it quietly: it is the clearest illustration of why you verify hardware decode is engaged rather than trusting a setting. Open the stream information panel, and check your GPU's video-decode engine in Task Manager while something plays. A 4K stream with the video-decode engine near zero and CPU cores loaded is software decoding, whatever the setting says.
Threads, and a cost you would not predict
When software decoding does happen, the decoder splits frames across threads. How many it chooses matters for reasons that have nothing to do with CPU time.
Left to choose for itself, the decoder picked 6 threads on the measurement machine. That produced a 40-surface Direct3D 11 output pool at 4K, roughly 500 MB of GPU-visible commit. Against an Xbox budget of about 1 GB, that is half the memory the whole application is allowed, spent on decoder buffers. I did not expect a thread count to be a memory decision.
So the console caps it at 3.
The elegant part of that cap is what it costs, which is nothing on any path that works. Hardware decode does not use those threads at all. The cap applies only to the software fallback, which on the console is where AV1 lands, because the console's player build has no AV1 hardware decoder.
The other lever, which is not a decoder setting
If decode cost is your problem, resolution is a bigger lever than the decoder choice.
Multiview tiles are capped to 640 by 360 when muted and 1280 by 720 when carrying audio, and that cap is why a 9 tile grid is possible at all. It is less a quality compromise than an acknowledgement that a tile 4 inches wide does not benefit from 1080p.
The same reasoning applies to a channel your machine struggles with. If your provider offers the same channel at a lower rendition, that is often a better answer than any setting, because it reduces the work rather than redistributing it.
How to actually verify it, in 2 minutes
The setting is not evidence. This is.
Open Task Manager, go to Performance, select your GPU, and change one of the graphs to Video Decode. Then start a channel.
Video Decode busy, CPU cores quiet hardware decoding
Video Decode flat, several cores busy software decoding
That is the whole test, and it is definitive in a way the settings screen is not. On the machine in our own verification pass, a 4K title moved that graph to about 11 percent where it had previously sat at zero with the CPU carrying the work.
Two false readings to know about. A machine with 2 GPUs may be decoding on the one you are not looking at, which is common on laptops with switchable graphics. And a very small window can be cheap enough to decode in software without obvious CPU load, so test at a size you would actually watch.
The stream information panel is the other half of the check, because it tells you which codec you are asking the machine to decode. Its protocol and container fields are derived from the URL rather than from the demuxer; the video codec field is the one that answers this question.
The memory cost, which is the surprising part
Decoding is usually discussed as a CPU-versus-GPU trade. On a memory-constrained device it is a memory decision as much as a processing one, and the numbers are not intuitive.
Frame threading allocates picture buffers. More threads means more buffers in flight, and at 4K each buffer is large. That is the whole story behind the 500 MB pool above, and it is why the cap is 3 rather than a number chosen for CPU reasons. It is also why the cap costs nothing where hardware decode is working: the hardware path does not allocate those buffers at all.
The wider context makes it sharper. Our own profiling of a 12 minute browse session on the desktop ended at 918 MB of working set and peaked at 1,080 MB, against an internal budget of 400 MB, with the managed heap flat at about 62 MB across the same cycles. The growth is native, and decoder buffers are native. On a console with roughly 1 GB usable, a software decode path that wants 500 MB of pools is not a performance problem. It is a termination.
Why "just fall back" is not a design
A reasonable-sounding suggestion: try hardware, and if it fails, switch to software automatically and silently.
What makes that harder than it sounds is the failure mode of the layer underneath. Handing the player a switch belonging to a module its build does not carry does not produce a recoverable error. It fails the entire player instance, and the failure does not name the option that caused it. The stream does not open.
So the emitted switch set is computed up front, per head, from an allow-list, with tests asserting that every combination of head, role, setting and surface depth emits only permitted switches. The alternative, discovering support by trying things, means a failed attempt costs a whole instance and produces an error that points nowhere.
This is also why capabilities the console cannot support are hidden rather than shown and disabled. There is no partial version to degrade to, and a control that would fail the player if used is worse than no control.
There is a genuine fallback ladder, and it shows what a well-behaved one looks like. The video surface tries 3 configurations in order:
1 R10G10B10A2 + Ignore preferred, 10-bit
2 R10G10B10A2 + Unspecified 10-bit, different alpha handling
3 B8G8R8A8 + Unspecified 8-bit, identical to the previous behaviour
What makes that ladder safe is the last rung: it is byte-identical to the configuration that shipped before any of this existed, so a machine that cannot compose 10-bit behaves exactly as it always did rather than entering an untested state. A total failure logs and leaves the surface unloaded rather than throwing out of a layout callback, which would take the application down.
A fallback is only a fallback if the bottom rung is known good.
What to check, in order
Is hardware decode actually engaged? Look at the GPU video-decode engine, not at the setting.
If it is not, is there a hardware decoder for this codec on this machine? H.264, HEVC and AV1 covers where the boundaries sit, and AV1 is where most of them are.
If there is, are your GPU drivers current? Dull advice, and occasionally the whole answer, because decoder support ships in drivers.
And if this machine will never decode this codec in hardware, the options are a lower rendition, a different codec from the provider, or different hardware. No setting substitutes for a circuit that is not there.
What this article measured21 claims, each with the evidence behind it
| Claim | Evidence | Counted |
|---|---|---|
| The acceleration setting has three states which map to three decoder selections passed per media: a Direct3D 11 video acceleration decoder, any available decoder, or none. | n = 3 | Aug 20, 2026 |
| The decoder selection is applied as a per-media option on every playback path rather than being an instance-level switch, which is what allowed one path to disagree with the setting. | n = 1 | Aug 23, 2026 |
| The on-demand path hard-coded acceleration off until 2026-08-23, so every film and episode was software decoded no matter what the setting said, while live TV honoured it correctly. | n = 1 | Aug 23, 2026 |
| After the fix, a 4K title played with the GPU video-decode engines active at around 11 percent, where the same path had previously been software-only. | n = 1 | Aug 23, 2026 |
| Frame-decoding threads are used only by the software decoder. Capping them costs nothing on hardware-decoded playback and applies only to the fallback. | n = 1 | Aug 31, 2026 |
| Automatic thread selection picked 6 on the measurement machine and produced a 40-surface Direct3D 11 output pool at 4K, roughly 500 MB of GPU-visible commit against an Xbox budget of about 1 GB. The cap is 3. | n = 1 | Aug 31, 2026 |
| The console's player build carries no AV1 hardware decoder, so AV1 always takes the software path there regardless of the setting. | n = 1 | Aug 23, 2026 |
| A switch belonging to a module the player build does not carry fails the whole instance rather than being ignored, which is why the emitted set is decided per head from an allow-list. | n = 1 | Aug 23, 2026 |
| The video surface tries three configurations in order and its bottom rung is byte-identical to the previously shipped stock configuration, so a machine that cannot compose 10-bit behaves exactly as it did before. | n = 3 | Aug 23, 2026 |
| A 12 minute desktop browse session ended at 918 MB of working set and peaked at 1,080 MB against an internal budget of 400 MB, with the managed heap flat at about 62 MB across the same cycles, so the growth is native memory. | n = 3 | Aug 30, 2026 |
| The two heads link differently named native packages, and the console's is built from an older libvlc with an app-container plugin set that omits modules the desktop has. | n = 2 | Sep 1, 2026 |
| The console head had already been broken once by an option belonging to a module it did not carry, which is the reason the switch set is now computed per head from an allow-list. | n = 1 | Sep 1, 2026 |
| Whether the loaded library supports the HDR switch is read from the runtime version rather than assumed from the head, so a downgraded native package loses HDR instead of failing to construct a player. | n = 1 | Sep 1, 2026 |
| The builder is given the precision the surface actually came up at rather than the one requested, so a failed 10-bit allocation cannot leave a mismatched switch behind. | n = 1 | Sep 1, 2026 |
| Only two surface precisions exist in the model: 8-bit, which is SDR only and leaves HDR sources to be tone-mapped, and 10-bit, which is the precondition for the graphics layer accepting an HDR colour space at all. | n = 2 | Sep 1, 2026 |
| Each multiview tile is its own player instance rather than a second view onto one, which is why the decode cost of a grid is multiplied rather than shared. | n = 1 | Sep 1, 2026 |
| Tiles are built from a different base option set than the main player: they skip the snapshot and on-screen-display suppression the primary instance carries, and add a title-overlay suppression the primary does not need. | n = 2 | Sep 1, 2026 |
| Every primary player instance is constructed with a 400 millisecond network cache before any per-path caching is applied on top. | n = 1 | Sep 1, 2026 |
| Audio passthrough is emitted as a numeric mode rather than a boolean, and only when the head is desktop, the instance is primary, and the setting is not off. | n = 3 | Sep 1, 2026 |
| The modern tone-mapping curve is not available at all in the player generation we ship: the bundled library predates it, the relevant module lives only in a video output we cannot use from our surface, and the Direct3D shader tone-mapper is a fixed curve. | n = 1 | Aug 23, 2026 |
| Multiview tiles are capped to 640 by 360 muted and 1280 by 720 with audio, which reduces decode work independently of which decoder is doing it. | n = 1 | Aug 23, 2026 |