Skip to content

fix: fall back from a failed hardware video decoder without losing or damaging pictures - #329

Merged
devopvoid merged 2 commits into
mainfrom
fix/hardware-decoder-fallback
Oct 5, 2026
Merged

devopvoid merged 2 commits into
mainfrom
fix/hardware-decoder-fallback

Conversation

@devopvoid

Copy link
Copy Markdown
Owner

Summary

Several ways the media player's hardware decoder gives way to software did not hold up:

  • A stream the hardware does not take (a profile or a sampling it has no format for) was decoded by the context made for hardware, which has one thread. The decoder now notices, before the first picture, that it went for a software format, and decodes again in a context made for software, which has the cores of the machine.
  • A hardware decoder that failed while the stream was being drained handed its replacement neither the end of the stream nor the pictures it had queued, so the last pictures of a short stream were lost. The replacement is told the stream has ended, and the pictures of the packets it takes up again are returned at once.
  • A packet that could not be decoded was taken for a hardware failure after the first picture, so one damaged packet gave up the hardware for good, while software decoding stops with an error. It is now left to fail as it does there. Before the first picture the software decoder still takes over, since it decodes the same packets and reports the error if the stream is at fault.
  • After a failure past the first picture the replacement was sent packets whose reference pictures it does not have. H.264 decoders skip them, VP9 decoders fail on them and ended playback. It now waits for a key frame, within a bound for streams that do not mark them.
  • "No hardware video decoder" is logged verbosely: it is the ordinary case of a codec without one, and went to stderr for every player.

The docs paragraph on fallback is updated to match.

Tests

A GPU cannot be made to fail on demand, so the tests arm a small native fault (HardwareFault, with its Java class in the test sources only). New cases in HardwareDecodingTest cover a failure before the first picture, bad data before and after it, a failure past it for H.264 and VP9, a failure while draining, and the thread count of the decoder that takes over. With each fix taken out in turn, the matching test fails (the H.264 key frame case does not notice, because H.264 hides it; the VP9 case does).

  • Media module suite with -Dwebrtc.test.hardwareDecoding=true: 74 tests, 0 failures, with Direct3D 11 as the only device type
  • With the real device order (Direct3D 12 first), the new tests pass and the same 8 existing cases fail as on a clean main

Known failures on main, not caused by this change

On an AMD Radeon RX 9070 XT, Direct3D 12 decoding (#326) fails on the first picture with No space for new Reference frame! (d3d12va_decode.c:81), and the player falls back to software. 8 HardwareDecodingTest cases that require hardware fail on a clean main for that reason: h264MatchesSoftware, vp9MatchesSoftware, vp9MatchesSoftware1080p, loopsInHardware, seekBetweenKeyFrames, seekAfterEndStartsOver, pausesAndResumes and decodesConcurrently. They pass when only Direct3D 11 is listed. Machines without a GPU skip them.

… damaging pictures

Several ways the media player's hardware decoder gives way to software did
not hold up:

- A stream the hardware does not take (a profile or a sampling it has no
  format for) was decoded by the context made for hardware, which has one
  thread. The decoder now notices, before the first picture, that it went for
  a software format, and decodes again in a context made for software, which
  has the cores of the machine.
- A hardware decoder that failed while the stream was being drained handed
  its replacement neither the end of the stream nor the pictures it had
  queued, so the last pictures of a short stream were lost. The replacement
  is told the stream has ended, and the pictures of the packets it takes up
  again are returned at once rather than with a later call.
- A packet that could not be decoded was taken for a failure of the hardware
  after its first picture, so one damaged packet gave up the hardware for
  good while software decoding stops with an error. It is now left to fail
  as it does there. Before the first picture the software decoder still
  takes over, since it decodes the same packets and reports the error if the
  stream is at fault.
- After a failure past the first picture the replacement was sent packets
  whose reference pictures it does not have. H.264 decoders skip them, VP9
  decoders fail on them and ended playback. It now waits for a key frame,
  within a bound for streams that do not mark them.

"No hardware video decoder" is logged verbosely: it is the ordinary case of
a codec without one, and went to stderr for every player.

The tests make the hardware fail on purpose, which no GPU does on demand,
through a small native fault the tests arm (HardwareFault, with a Java class
in the tests only). They cover a failure before the first picture, bad data
before and after it, a failure past it for H.264 and VP9, a failure while
draining, and the thread count of the decoder that takes over. Each was seen
to fail with its fix taken out.
The fault tests took a hardware decoder that opens for one that decodes. On
the macOS runners, which are virtual machines, VideoToolbox opens a device
and then cannot set up the decoder ("hwaccel initialisation returned
error"), so the stream is decoded in software from its first packet and a
fault meant for the hardware never arrives. The tests that fail the hardware
after its first pictures then saw no failure and no error.

A stream is now played once in hardware before the first of them runs, and
the tests are skipped, or fail where hardware is required, unless the
hardware delivered its pictures. The test of the software decoder's threads
needs only the hardware decoder to be set up, since it is about the decoder
that replaces it, and keeps that check.
@devopvoid
devopvoid merged commit 997f4fe into main Oct 5, 2026
24 checks passed
@devopvoid
devopvoid deleted the fix/hardware-decoder-fallback branch October 5, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant