Skip to content

feat: encode VP9 in hardware on Windows, when turned on - #348

Merged
devopvoid merged 1 commit into
feat/windows-hevc-codecsfrom
feat/windows-vp9-encoder
Oct 11, 2026
Merged

devopvoid merged 1 commit into
feat/windows-hevc-codecsfrom
feat/windows-vp9-encoder

Conversation

@devopvoid

Copy link
Copy Markdown
Owner

Summary

Stacked on #347 (hardware H.265 on Windows), and based on its branch until that is merged.

Intel GPUs have a Media Foundation VP9 encoder. The hardware encoder factory now uses it for VP9 (profile 0), the way it uses the H.264, H.265 and AV1 encoders — but only when it is turned on:

System.setProperty("webrtc.hardware.vp9Encoder", "true"); // before the factory is created

It is off by default because it has not been tested on an Intel GPU, and once on it takes over VP9 from libvpx for every stream.

  • The property is read natively (HardwareCodecOptions, through Boolean.getBoolean) when the Windows hardware factories are made, so it reaches both the codec list and the encoders of a call, with no new API. The read is safe from any thread: its own local frame, nothing done while another exception is pending.
  • Without the property, or where there is no VP9 encoder, VP9 stays with libvpx. A hardware encoder that fails to start, or a stream that asks for SVC, falls back to libvpx too, through the existing fallback chain. VP9 is offered by the software encoders anyway, so turning it on changes nothing that is negotiated.
  • EncoderOutputProcessor passes VP9 frames on as they come, with the VP9 information of a single layer that the Java encoders get as well (the group of frames with every key frame), parses the QP and uses the VP9 quality scaling thresholds of the Java encoders.
  • The factory Javadoc and the codec guide describe the property.

Testing

On Windows x64 (AMD Radeon RX 9070 XT):

  • vp9HardwareEncodingIsOptIn: without the property, a VP9 call is encoded by libvpx. It runs on every Windows and Linux machine, CI included.
  • hardwareEncodesVp9WhenEnabled: with the property, a VP9 call is encoded in hardware where there is a VP9 encoder; webrtc.test.hardwareVp9Encoder makes it fail rather than skip without one. This machine has an Intel VP9 encoder registered but no Intel GPU: the factory found it, the encoder failed to start (E_FAIL) and libvpx took over, so the call went on and the test skipped.
  • The full webrtc test suite: 277 tests, no failures.

Not verified

VP9 encoding on an actual Intel GPU: neither this machine nor CI has one. That is why it is opt-in.

@devopvoid
devopvoid added this pull request to stack #349 October 11, 2026 13:02
Intel GPUs have a Media Foundation VP9 encoder. The hardware encoder
factory uses it for VP9 (profile 0), the way it uses the others, when the
system property webrtc.hardware.vp9Encoder is true as the factory is
created. It is off by default: the encoder has not been tested on an Intel
GPU, and once on, it would take over VP9 from libvpx. Without the property,
or where there is no VP9 encoder, VP9 is encoded with libvpx, and a
hardware encoder that fails to start, or a stream that asks for SVC, falls
back to libvpx as well. VP9 is offered by the software encoders anyway, so
turning it on changes nothing that is negotiated.

EncoderOutputProcessor passes VP9 frames on as they come, with the VP9
information of a single layer that the Java encoders get too (the group
of frames with every key frame), parses their QP, and uses the VP9 quality
scaling thresholds of the Java encoders.

On a machine with an Intel VP9 encoder registered but no Intel GPU, it
fails to start and libvpx takes over, which the test of the property saw.
@devopvoid
devopvoid force-pushed the feat/windows-vp9-encoder branch from 581ea61 to 74846be Compare October 11, 2026 17:37
@devopvoid
devopvoid merged commit 25df78d into main Oct 11, 2026
24 checks passed
@devopvoid
devopvoid deleted the feat/windows-vp9-encoder branch October 11, 2026 18:25
SendableMetatype added a commit to EduGeyser/webrtc-java that referenced this pull request Oct 15, 2026
Brings in the work upstream added from October 11 to 15: hardware video
codecs on every platform, through Media Foundation, VideoToolbox, VA-API
and NVDEC, with WebRTC built with H.265 (devopvoid#346 to devopvoid#348, devopvoid#352, devopvoid#353, devopvoid#356,
devopvoid#359, devopvoid#371 to devopvoid#377); HEVC and AV1 decoding in the media player, with
dav1d and Vulkan Video (devopvoid#331, devopvoid#345); encoded frame injectors (devopvoid#365);
receiver source changes and audio packet infos (devopvoid#366); audio codec
factories (devopvoid#340); network thread observers called on a callback thread
(devopvoid#364); screen capture with ScreenCaptureKit (devopvoid#354); Windows audio
devices that mix more than two channels (devopvoid#358); autoninja (devopvoid#357); and
the release channels of the docs site (devopvoid#361).

Fork-side adaptations:
- The factory keeps the CreatePeerConnectionFactory overload without a
  worker thread, and still switches device capture on the network
  thread. devopvoid#355 copies WebRTC's factory setup and has the audio module
  learn a worker thread, which only matters for a worker thread distinct
  from the network thread, and WebRTC is removing those. The audio codec
  factories and hardware codec switches of devopvoid#340 and devopvoid#359 go to that
  overload.
- WebRTC stays on a commit on main, so the move to branch 8059 (devopvoid#360)
  and its fixed clang package are left out.
- The data channels variant builds WebRTC without H.265 and without the
  extra targets, leaves out the audio codec factory bindings and
  addTransceiverOfType, and skips the new tests that need audio or
  video.
- dav1d is built against the sysroot on native Linux builds as well, as
  FFmpeg is. CI installs Meson for the full variant only, checks out the
  dav1d submodule, and adds its commit to the FFmpeg cache key. The
  fork's Windows SDK step stays, without upstream's second one.
- The release workflow records the docs' version in the tagged commit,
  as upstream's now does. The docs site still builds from main, since
  this fork publishes none.
- The Maven files stay deleted: Jetty is bumped in the version catalog,
  and the docs lockfile takes upstream's bumps on top of the fork's
  newer VitePress.
- The media module's README builds with Gradle.
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