Skip to content

feat: encode and decode H.265 in hardware on Windows - #347

Merged
devopvoid merged 2 commits into
feat/webrtc-h265from
feat/windows-hevc-codecs
Oct 11, 2026
Merged

devopvoid merged 2 commits into
feat/webrtc-h265from
feat/windows-hevc-codecs

Conversation

@devopvoid

Copy link
Copy Markdown
Owner

Summary

Stacked on #346 (H.265 in the WebRTC build), and based on its branch until that is merged.

The hardware codec factories now offer H.265 (Main profile) where the GPU has it:

  • Encoding: NVENC (NVIDIA, Windows and Linux) or the Media Foundation encoder of the GPU driver (AMD, Intel on Windows). NVENC detects the HEVC GUID and is configured like H.264 (infinite GOP, VPS/SPS/PPS with every IDR, one slice); Media Foundation takes MFVideoFormat_HEVC with eAVEncH265VProfile_Main_420_8.
  • Decoding: the Media Foundation decoder of Windows on Direct3D 11, where the GPU has D3D11_DECODER_PROFILE_HEVC_VLD_MAIN and Windows has an HEVC decoder (the HEVC Video Extensions, or one of the GPU vendor).
  • EncoderOutputProcessor handles H.265 as it does H.264: it keeps the parameter sets and puts them back in front of key frames that lack them, treats IRAP pictures as key frames, parses the QP (with the H.264 quality scaling thresholds, as WebRTC does), and fills the frame dependencies from the generic frame info, as for AV1.
  • There is no software H.265, so H.265 is offered after the codecs of the default factories, which keep their order: the codec a peer connection prefers does not change, and a call uses H.265 only if it is preferred and the other end decodes it. When the hardware encoder cannot take a stream, WebRTC switches to the next negotiated codec. The factory Javadoc and the codec guide say so.
  • H265Format holds the one SDP format all of them offer: Main profile, main tier, level 3.1, tx-mode=SRST.

Testing

On Windows x64 with an AMD Radeon RX 9070 XT (AMDh265Encoder, HEVCVideoExtension), with webrtc.test.hardwareH265Encoder and webrtc.test.hardwareH265Decoder set so that the H.265 tests fail rather than skip:

  • New: hardwareEncodesH265, hardwareDecodesH265 and hardwareCropsPaddedH265 (640x360, which HEVC decodes into a larger surface), plus hardwareKeepsCodecs in both test classes, which now checks that the default codecs come first in their order and that hardware adds nothing but H.265.
  • The full webrtc test suite: 275 tests, no failures (the two skipped are the macOS-only ones). hardwareCropsPaddedH265 passed in five runs.

Two things about AMD's H.265 encoder showed up and are handled in the tests rather than the code:

  • It refuses widths up to 352 (MF_E_INVALIDMEDIATYPE); widths of 480 and up work. WebRTC then switches to the next codec, so the H.265 call tests send 640x360 instead of 320x240.
  • At heights that are no multiple of 16 it pads the coded picture with neutral chroma, and its loop filters pull the last chroma row towards it by up to about 20 (8 of 57,600 samples). hardwareCropsPaddedH265 leaves that row out; a crop at the wrong offset would show across the plane. The AMD H.264 encoder does not do this.

Not verified

NVENC's H.265 has not run: there is no NVIDIA GPU here, and CI has no GPU, where the hardware tests skip. Nor has an Intel GPU. H.265 decoding on Linux (NVDEC, Vulkan/VA-API) and macOS comes in later changes, so a Linux NVENC H.265 stream needs a Windows receiver for now.

The hardware codec factories now use VideoToolbox for the codecs WebRTC's
macOS factories lack, where the Mac has the hardware for them:

- H.265 (Main profile), encoded and decoded on the media engine. The
  sessions are required to be hardware ones and checked to be, so H.265 is
  never encoded or decoded in software; a Mac without a hardware HEVC
  encoder or decoder does not offer it. The encoder works in real time,
  without frame reordering, with a key frame only on request, and takes
  CVPixelBuffers as they are and converts other frames to NV12. Its output
  goes through EncoderOutputProcessor, which puts the parameter sets in
  front of each key frame. The decoder makes its session from the
  VPS, SPS and PPS of key frames.
- AV1 (profile 0, 8 bit), decoded on Macs with an AV1 decoder (Apple M3
  and newer). The session is made from the sequence header, which is
  parsed for the AV1 configuration box; high bit depth, monochrome and
  spatial layers go to dav1d, as does a decoder that keeps failing.

The VP9 decoder shares a new VTVideoDecoder base with them, which holds
the session, the hardware check and the output. The session limit of the
process applies only to codecs with a software decoder to go to.

H.265 is offered after the default codecs, which keep their order, with
the SDP format and the EncoderOutputProcessor support of #347, taken over
as they are there. Tests that a call in H.265 is encoded and decoded in
hardware run on macOS now, check that the call is still in H.265 rather
than in a codec WebRTC switched to, and cover odd sizes, a resolution
change, a bitrate cap and restarts; the software factories are checked to
offer no H.265.
The hardware codec factories offer H.265 (Main profile) where the GPU has
it: encoded with NVENC or the Media Foundation encoder of the GPU driver,
decoded by the Media Foundation decoder of Windows on Direct3D 11, which
needs the HEVC Video Extensions. There is no software H.265, so H.265 is
offered after the codecs of the default factories, which keep their order,
and only where the hardware has it; a call uses it only if it is preferred
and the other end decodes it. When the hardware encoder cannot take a
stream, WebRTC switches to the next negotiated codec. NVENC's H.265 is in
the Linux build as well.

The H.265 handling of EncoderOutputProcessor (parameter sets in front of
key frames, IRAP pictures as key frames, the QP and the H.264 quality
scaling thresholds, frame dependencies from the generic frame info), the
SDP format and the H.265 call tests come with the macOS change this is
stacked on, and serve these encoders and decoders as they are.

AMD's Media Foundation H.265 encoder refuses widths up to 352, which is
why the H.265 call tests send 640x360, and it leaves the last chroma row
of a picture whose height is no multiple of 16 slightly off, which the
padded frame test of H.265 leaves out of its check.
@devopvoid
devopvoid force-pushed the feat/windows-hevc-codecs branch from ab23890 to 14a0dc4 Compare October 11, 2026 13:59
@devopvoid
devopvoid merged commit 19f7b1c into main Oct 11, 2026
24 checks passed
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