Skip to content

Commit 91ea205

Browse files
committed
feat: encode AV1 on the GPU with Media Foundation and NVENC
HardwareVideoEncoderFactory now encodes AV1 in hardware where the GPU has an AV1 encoder: with NVENC on NVIDIA RTX 40 and newer, on Windows and Linux, and on Windows otherwise with the Media Foundation encoder of the driver, e.g. on AMD RDNA3 and newer and Intel Arc. The encoders offer AV1 profile 0, the format libaom offers, so negotiation does not change. Hardware encoders produce a single layer. A stream that asks for another scalability mode fails to initialize the hardware encoder, which hands it to libaom. WebRTC sends the frame dependencies of AV1 in the dependency descriptor, taken from the generic frame info, which is filled like that of a stream without layers. The Media Foundation and NVENC encoders now serve both codecs, and the bitstream handling all hardware encoders shared is one class, EncoderOutputProcessor: it keeps the parameter sets, the SPS and PPS of H.264 or the sequence header OBU of AV1, and puts them back in front of a key frame that lacks them, and it fills the QP and the codec specific information. NVENC asks an encode session once which codecs the GPU supports.
1 parent 6072904 commit 91ea205

19 files changed

Lines changed: 767 additions & 302 deletions

File tree

‎docs/guide/advanced/video-codecs.md‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ PeerConnectionFactory factory = PeerConnectionFactory.builder()
2424
.build();
2525
```
2626

27-
| Platform | Hardware encoding |
28-
|---|---|
29-
| Windows | H.264 with NVENC on NVIDIA GPUs, otherwise through the Media Foundation encoder of the GPU driver (AMD, Intel) |
30-
| Linux | H.264 with NVENC on NVIDIA GPUs, otherwise through the VA-API encoder of the GPU driver (Intel, AMD) |
31-
| macOS | H.264 through VideoToolbox, as with `DefaultVideoEncoderFactory` |
27+
| Platform | H.264 | AV1 |
28+
|---|---|---|
29+
| Windows | NVENC on NVIDIA GPUs, otherwise the Media Foundation encoder of the GPU driver (AMD, Intel) | NVENC on NVIDIA RTX 40 and newer, otherwise Media Foundation on GPUs that encode AV1 (e.g. AMD RDNA3 and newer, Intel Arc) |
30+
| Linux | NVENC on NVIDIA GPUs, otherwise the VA-API encoder of the GPU driver (Intel, AMD) | NVENC on NVIDIA RTX 40 and newer |
31+
| macOS | VideoToolbox, as with `DefaultVideoEncoderFactory` | Software |
32+
33+
Hardware encoders produce a single layer. A stream that asks for SVC, such as an AV1 stream with a scalability mode like `L1T3`, is encoded in software.
3234

3335
NVENC needs an NVIDIA driver of version 522 or newer on Windows, 520 or newer on Linux. VA-API needs libva 2 and a driver that encodes H.264, such as Intel's `intel-media-va-driver` (iHD) or Mesa's `mesa-va-drivers` for AMD, and access to a render node in `/dev/dri`. All of them are loaded at run time, so nothing needs to be installed on machines without them.
3436

35-
The hardware encoders take over H.264 Constrained Baseline and Baseline with packetization mode 1, formats the software encoder offers too, so encoding in hardware never changes what is negotiated. When a hardware encoder fails to start, for example because the GPU has no encoder sessions left, or fails while encoding, the stream switches to the next encoder in line (e.g. from NVENC to Media Foundation on Windows), and finally to the software encoder, and continues with a key frame. On a machine without a hardware encoder, the factory encodes like `DefaultVideoEncoderFactory`.
37+
The hardware encoders take over H.264 Constrained Baseline and Baseline with packetization mode 1, and AV1 profile 0, formats the software encoders offer too, so encoding in hardware never changes what is negotiated. When a hardware encoder fails to start, for example because the GPU has no encoder sessions left, or fails while encoding, the stream switches to the next encoder in line (e.g. from NVENC to Media Foundation on Windows), and finally to the software encoder, and continues with a key frame. On a machine without a hardware encoder, the factory encodes like `DefaultVideoEncoderFactory`.
3638

37-
Which encoder a stream uses shows in the `encoderImplementation` statistic of its `outbound-rtp` stats, e.g. `NVENC (NVIDIA GeForce RTX 4070)`, `MediaFoundation (AMDh264Encoder)`, `VA-API (Intel iHD driver ...)` or `OpenH264`.
39+
Which encoder a stream uses shows in the `encoderImplementation` statistic of its `outbound-rtp` stats, e.g. `NVENC (NVIDIA GeForce RTX 4070)`, `MediaFoundation (AMDav1Encoder)`, `VA-API (Intel iHD driver ...)`, `OpenH264` or `libaom`.
3840

3941
### Native Codecs
4042

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2026 Alex Andres
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_ENCODER_OUTPUT_PROCESSOR_H_
18+
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_ENCODER_OUTPUT_PROCESSOR_H_
19+
20+
#include "api/video/encoded_image.h"
21+
#include "api/video/video_codec_type.h"
22+
#include "api/video_codecs/sdp_video_format.h"
23+
#include "api/video_codecs/video_encoder.h"
24+
#include "common_video/h264/h264_bitstream_parser.h"
25+
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
26+
#include "modules/video_coding/include/video_codec_interface.h"
27+
#include "modules/video_coding/svc/scalable_video_controller_no_layering.h"
28+
29+
#include <cstdint>
30+
#include <span>
31+
#include <vector>
32+
33+
namespace jni
34+
{
35+
// Turns what a hardware encoder produces for a frame into what WebRTC
36+
// sends, the same way for every hardware encoder: H.264 as Annex B NAL
37+
// units, AV1 as OBUs in the low overhead bitstream format.
38+
//
39+
// A receiver can start decoding only from a key frame that carries the
40+
// parameter sets, the SPS and PPS of H.264 or the sequence header of AV1.
41+
// Encoders put them in front of the first key frame, not necessarily in
42+
// front of every one, so they are kept and put back where missing. The
43+
// frame dependencies WebRTC needs for AV1 are those of a stream without
44+
// layers, which is all hardware encoders produce here.
45+
class EncoderOutputProcessor
46+
{
47+
public:
48+
EncoderOutputProcessor(webrtc::VideoCodecType codec, const webrtc::SdpVideoFormat & format);
49+
50+
// Forgets the parameter sets, for a stream that starts over.
51+
void Reset();
52+
53+
// Fills the image with the bitstream of one frame, and the codec
54+
// specific information WebRTC needs to send it. The caller sets
55+
// the size of the image before, which the AV1 frame dependencies
56+
// refer to, and its timestamps. The frame is a key
57+
// frame if the encoder says so or, for H.264, if it holds an IDR
58+
// slice. Returns false if the bitstream cannot be parsed, or a key
59+
// frame lacks parameter sets and there are none to put back.
60+
bool Process(std::span<const uint8_t> bitstream, bool keyFrame, webrtc::EncodedImage & image,
61+
webrtc::CodecSpecificInfo & info);
62+
63+
// The quality scaling settings that suit the codec: the thresholds
64+
// of WebRTC's H.264 encoder, and none for AV1, whose QP is not
65+
// parsed from the bitstream.
66+
webrtc::VideoEncoder::ScalingSettings GetScalingSettings() const;
67+
68+
private:
69+
bool ProcessH264(std::span<const uint8_t> bitstream, bool & keyFrame, std::vector<uint8_t> & output);
70+
bool ProcessAv1(std::span<const uint8_t> bitstream, bool keyFrame, std::vector<uint8_t> & output);
71+
72+
void FillCodecSpecificInfo(const webrtc::EncodedImage & image, bool keyFrame,
73+
webrtc::CodecSpecificInfo & info);
74+
75+
private:
76+
const webrtc::VideoCodecType codec;
77+
webrtc::H264PacketizationMode packetizationMode;
78+
79+
// The SPS and PPS, or the sequence header OBU, last seen.
80+
std::vector<uint8_t> parameterSets;
81+
82+
webrtc::H264BitstreamParser h264Parser;
83+
webrtc::ScalableVideoControllerNoLayering svcController;
84+
};
85+
}
86+
87+
#endif

‎webrtc-jni/src/main/cpp/include/media/video/codec/linux/VaapiH264Encoder.h‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_VAAPI_H264_ENCODER_H_
1818
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_VAAPI_H264_ENCODER_H_
1919

20+
#include "media/video/codec/EncoderOutputProcessor.h"
2021
#include "media/video/codec/linux/VaapiLibrary.h"
2122

2223
#include "api/video/video_frame.h"
2324
#include "api/video_codecs/sdp_video_format.h"
2425
#include "api/video_codecs/video_codec.h"
2526
#include "api/video_codecs/video_encoder.h"
26-
#include "common_video/h264/h264_bitstream_parser.h"
27-
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
2827

2928
#include <va/va.h>
3029
#include <va/va_enc_h264.h>
@@ -103,8 +102,7 @@ namespace jni
103102

104103
webrtc::EncodedImageCallback * callback;
105104

106-
webrtc::H264BitstreamParser bitstreamParser;
107-
webrtc::H264PacketizationMode packetizationMode;
105+
EncoderOutputProcessor outputProcessor;
108106
};
109107
}
110108

‎webrtc-jni/src/main/cpp/include/media/video/codec/nvenc/NvencLibrary.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ namespace jni
6060
// The name of the device, e.g. "NVIDIA GeForce RTX 4070".
6161
const std::string & DeviceName() const;
6262

63+
// Whether the device encodes H.264, and AV1, which only newer
64+
// GPUs do.
65+
bool SupportsH264() const;
66+
bool SupportsAv1() const;
67+
6368
// Retains the primary context of the device. Every successful call
6469
// has to be matched by ReleaseContext().
6570
bool RetainContext(CUcontext * context) const;
@@ -73,6 +78,9 @@ namespace jni
7378

7479
bool Load();
7580

81+
// Asks an encode session which codecs the device supports.
82+
void QueryCodecs();
83+
7684
private:
7785
using CuInit = CUresult (JNI_CUDAAPI *)(unsigned int flags);
7886
using CuDeviceGetCount = CUresult (JNI_CUDAAPI *)(int * count);
@@ -101,6 +109,8 @@ namespace jni
101109
NV_ENCODE_API_FUNCTION_LIST api = {};
102110
CUdevice device = 0;
103111
std::string deviceName;
112+
bool h264 = false;
113+
bool av1 = false;
104114
};
105115
}
106116

webrtc-jni/src/main/cpp/include/media/video/codec/nvenc/NvencH264Encoder.h renamed to webrtc-jni/src/main/cpp/include/media/video/codec/nvenc/NvencVideoEncoder.h

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17-
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_NVENC_H264_ENCODER_H_
18-
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_NVENC_H264_ENCODER_H_
17+
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_NVENC_VIDEO_ENCODER_H_
18+
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_NVENC_VIDEO_ENCODER_H_
1919

20+
#include "media/video/codec/EncoderOutputProcessor.h"
2021
#include "media/video/codec/nvenc/NvencLibrary.h"
2122

23+
#include "api/video/video_codec_type.h"
2224
#include "api/video/video_frame.h"
2325
#include "api/video_codecs/sdp_video_format.h"
2426
#include "api/video_codecs/video_codec.h"
2527
#include "api/video_codecs/video_encoder.h"
26-
#include "common_video/h264/h264_bitstream_parser.h"
27-
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
2828

2929
#include <nvEncodeAPI.h>
3030

@@ -34,19 +34,20 @@
3434

3535
namespace jni
3636
{
37-
// Encodes H.264 with NVENC, the encoder of NVIDIA GPUs, on the primary
38-
// CUDA context of the device.
37+
// Encodes H.264 or AV1 with NVENC, the encoder of NVIDIA GPUs, on the
38+
// primary CUDA context of the device.
3939
//
4040
// Encoding is synchronous: with no B-frames and a low-latency preset,
4141
// NVENC returns each frame as soon as it is encoded, so frames go in and
4242
// out on the encoder thread. Frames are passed in system memory as NV12,
4343
// into an input buffer NVENC allocates. Anything that fails makes the
4444
// encoder give up, so that the next encoder in line takes over.
45-
class NvencH264Encoder : public webrtc::VideoEncoder
45+
class NvencVideoEncoder : public webrtc::VideoEncoder
4646
{
4747
public:
48-
NvencH264Encoder(NvencLibrary & library, const webrtc::SdpVideoFormat & format);
49-
~NvencH264Encoder() override;
48+
// The codec is H.264 or AV1.
49+
NvencVideoEncoder(NvencLibrary & library, webrtc::VideoCodecType codec, const webrtc::SdpVideoFormat & format);
50+
~NvencVideoEncoder() override;
5051

5152
int32_t InitEncode(const webrtc::VideoCodec * codecSettings, const Settings & settings) override;
5253
int32_t RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback * callback) override;
@@ -58,13 +59,16 @@ namespace jni
5859
private:
5960
bool OpenSession();
6061
bool Configure();
62+
void ConfigureCodec();
6163
void ApplyRates();
6264
bool CopyToInput(const webrtc::VideoFrame & frame, uint32_t * pitch);
6365
void DestroySession();
6466

6567
private:
6668
NvencLibrary & library;
6769
const NV_ENCODE_API_FUNCTION_LIST & api;
70+
const webrtc::VideoCodecType codec;
71+
const GUID codecGuid;
6872
const std::string implementationName;
6973

7074
CUcontext context;
@@ -82,8 +86,7 @@ namespace jni
8286

8387
webrtc::EncodedImageCallback * callback;
8488

85-
webrtc::H264BitstreamParser bitstreamParser;
86-
webrtc::H264PacketizationMode packetizationMode;
89+
EncoderOutputProcessor outputProcessor;
8790
};
8891
}
8992

‎webrtc-jni/src/main/cpp/include/media/video/codec/nvenc/NvencVideoEncoderFactory.h‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@
2929

3030
namespace jni
3131
{
32-
// Creates NVENC encoders on an NVIDIA GPU. It offers H.264 in the
33-
// profiles WebRTC's software encoder offers too, and only with
34-
// packetization mode 1: mode 0 needs each NAL unit to fit a packet.
32+
// Creates NVENC encoders on an NVIDIA GPU, for H.264 and, on GPUs that
33+
// have it, AV1. It offers them in the formats WebRTC's software encoders
34+
// offer too: H.264 only with packetization mode 1, since mode 0 needs each
35+
// NAL unit to fit a packet, and AV1 in profile 0.
3536
class NvencVideoEncoderFactory : public webrtc::VideoEncoderFactory
3637
{
3738
public:

‎webrtc-jni/src/main/cpp/include/media/video/codec/windows/MFEncoderUtils.h‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626

2727
namespace jni
2828
{
29-
// Lists the hardware H.264 encoder transforms that take NV12, best
30-
// first. Media Foundation has to be started.
31-
HRESULT EnumerateHardwareH264Encoders(std::vector<Microsoft::WRL::ComPtr<IMFActivate>> & encoders);
29+
// Lists the hardware encoder transforms that take NV12 and produce the
30+
// given video format, such as MFVideoFormat_H264, best first. Media
31+
// Foundation has to be started.
32+
HRESULT EnumerateHardwareEncoders(const GUID & format, std::vector<Microsoft::WRL::ComPtr<IMFActivate>> & encoders);
3233

3334
// Returns the name the driver gives a transform, e.g. "AMDh264Encoder".
3435
std::string GetTransformName(IMFActivate * activate);

webrtc-jni/src/main/cpp/include/media/video/codec/windows/MFH264Encoder.h renamed to webrtc-jni/src/main/cpp/include/media/video/codec/windows/MFVideoEncoder.h

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@
1414
* limitations under the License.
1515
*/
1616

17-
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_MF_H264_ENCODER_H_
18-
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_MF_H264_ENCODER_H_
17+
#ifndef JNI_WEBRTC_MEDIA_VIDEO_CODEC_MF_VIDEO_ENCODER_H_
18+
#define JNI_WEBRTC_MEDIA_VIDEO_CODEC_MF_VIDEO_ENCODER_H_
1919

20+
#include "media/video/codec/EncoderOutputProcessor.h"
2021
#include "media/video/codec/windows/MFTransformEvents.h"
2122
#include "platform/windows/MFInitializer.h"
2223

24+
#include "api/video/video_codec_type.h"
2325
#include "api/video/video_frame.h"
2426
#include "api/video_codecs/sdp_video_format.h"
2527
#include "api/video_codecs/video_codec.h"
2628
#include "api/video_codecs/video_encoder.h"
27-
#include "common_video/h264/h264_bitstream_parser.h"
28-
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
2929

3030
#include <mftransform.h>
3131
#include <strmif.h>
@@ -42,20 +42,21 @@
4242

4343
namespace jni
4444
{
45-
// Encodes H.264 with the hardware encoder of the GPU, through the Media
46-
// Foundation transform its driver provides. These transforms are
45+
// Encodes H.264 or AV1 with the hardware encoder of the GPU, through the
46+
// Media Foundation transform its driver provides. These transforms are
4747
// asynchronous: they ask for input and announce output through events,
4848
// which arrive on a Media Foundation thread, so encoded frames are
4949
// handed to WebRTC from there.
5050
//
5151
// Frames are passed in system memory as NV12; the transform uploads them.
5252
// Anything that fails makes the encoder give up, so that WebRTC switches
5353
// to the software encoder.
54-
class MFH264Encoder : public webrtc::VideoEncoder, public MFTransformEventListener
54+
class MFVideoEncoder : public webrtc::VideoEncoder, public MFTransformEventListener
5555
{
5656
public:
57-
explicit MFH264Encoder(const webrtc::SdpVideoFormat & format);
58-
~MFH264Encoder() override;
57+
// The codec is H.264 or AV1.
58+
MFVideoEncoder(webrtc::VideoCodecType codec, const webrtc::SdpVideoFormat & format);
59+
~MFVideoEncoder() override;
5960

6061
int32_t InitEncode(const webrtc::VideoCodec * codecSettings, const Settings & settings) override;
6162
int32_t RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback * callback) override;
@@ -79,14 +80,13 @@ namespace jni
7980
HRESULT CreateTransform();
8081
HRESULT ConfigureTypes();
8182
void ConfigureCodec();
82-
void SetBitrate(uint32_t bitrateBps);
8383
HRESULT CreateInputSample(const webrtc::VideoFrame & frame, IMFSample ** sample);
8484
void ProcessOutput();
8585
void DeliverOutput(IMFSample * sample);
8686
void ShutdownTransform();
8787

8888
private:
89-
const webrtc::SdpVideoFormat format;
89+
const webrtc::VideoCodecType codec;
9090
std::string implementationName;
9191

9292
std::unique_ptr<MFInitializer> mfInitializer;
@@ -123,10 +123,8 @@ namespace jni
123123
LONGLONG lastSampleTime;
124124
bool keyFrameRequested;
125125

126-
// Used on the event thread only.
127-
std::vector<uint8_t> parameterSets;
128-
webrtc::H264BitstreamParser bitstreamParser;
129-
webrtc::H264PacketizationMode packetizationMode;
126+
// Used on the event thread only, and reset while no events arrive.
127+
EncoderOutputProcessor outputProcessor;
130128
};
131129
}
132130

‎webrtc-jni/src/main/cpp/include/media/video/codec/windows/MFVideoEncoderFactory.h‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727

2828
namespace jni
2929
{
30-
// Creates the Media Foundation hardware encoders of the GPU. It offers
31-
// H.264 in the profiles WebRTC's software encoder offers too, and only
32-
// with packetization mode 1: mode 0 needs each NAL unit to fit a packet,
33-
// which hardware encoders cannot be relied on to keep to.
30+
// Creates the Media Foundation hardware encoders of the GPU, for H.264 and
31+
// AV1, whichever the GPU has. It offers them in the formats WebRTC's
32+
// software encoders offer too: H.264 only with packetization mode 1, since
33+
// mode 0 needs each NAL unit to fit a packet, which hardware encoders
34+
// cannot be relied on to keep to, and AV1 in profile 0.
3435
class MFVideoEncoderFactory : public webrtc::VideoEncoderFactory
3536
{
3637
public:
37-
// Returns a factory, or null if there is no hardware H.264
38+
// Returns a factory, or null if there is no hardware H.264 or AV1
3839
// encoder on this system.
3940
static std::unique_ptr<MFVideoEncoderFactory> Create();
4041

@@ -45,7 +46,11 @@ namespace jni
4546
const webrtc::SdpVideoFormat & format) override;
4647

4748
private:
48-
MFVideoEncoderFactory() = default;
49+
MFVideoEncoderFactory(bool h264, bool av1);
50+
51+
private:
52+
const bool h264;
53+
const bool av1;
4954
};
5055
}
5156

0 commit comments

Comments
 (0)