Conversation
A MediaPlayer, and a MediaFileSource, can be asked to decode H.264 and VP9 video on the media engine of the machine instead of the processor. It is off unless asked for: a new constructor takes the flag, the existing ones mean software, and MediaPlayer.isHardwareDecoding() says what is in use. On macOS that is VideoToolbox, through FFmpeg's hwaccels, which the FFmpeg build now has for the two codecs (h264_videotoolbox, vp9_videotoolbox). The list of components changed, so the next build of the platform rebuilds FFmpeg. Elsewhere the flag is accepted and the video is decoded in software until the build has hardware decoders for the platform. VideoDecoder does the work, and is the only place that knows. A decoded picture is read back into system memory as NV12 and goes through the I420 conversion that already exists for other formats, so what reaches WebRTC is what software decoding gives; the tests compare every frame. What hardware saves is processor time: measured on an Apple M2, about seven to eight times less for 1080p and 4K H.264 and for 1080p VP9. A stream the hardware does not take is decoded in software, and isHardwareDecoding turns false: a codec without a hardware decoder, a stream whose pixel format FFmpeg does not offer the hardware for (VP9 profile 1), or a decoder that fails. Until the hardware has produced its first frame, the packets sent to it are kept, and decoded again in software if it fails, so none is lost. After the first frame, a failure continues from the next key frame. The fallback lives in VideoDecoder, because a decode error from the decoder would end playback. Tests: new media for H.264 and VP9 (VP9 profile 1 among them), since the existing assets are VP8 and MS-MPEG4. A machine without a hardware decoder skips the tests that need one; -Dwebrtc.test.hardwareDecoding=true makes them fail instead.
The FFmpeg build for Windows now has the Direct3D 11 and DXVA2 hwaccels for H.264 and VP9 (h264_d3d11va2, vp9_d3d11va2, h264_dxva2, vp9_dxva2), so a player asked to decode in hardware does on Windows what it does on macOS. VideoDecoder needed nothing: it already tries Direct3D 11 and then DXVA2 as the device types of the platform, and reads the decoded picture back into system memory the same way. FFmpeg loads d3d11.dll, dxgi.dll, d3d9.dll and dxva2.dll itself when a device is created, so the libraries load on a machine with no GPU decoder, and the build needs the Windows SDK headers only, which it has. This has not been built or run on Windows. The option names were checked against what configure lists; nothing more could be on a Mac. The hardware tests of the module run there as they do on macOS, and compare every frame with software decoding.
The FFmpeg build for Linux on x86-64 and ARM64 now has the NVDEC hwaccels for H.264 and VP9 (h264_nvdec, vp9_nvdec), so a player asked to decode in hardware does on an NVIDIA GPU what it does on macOS and Windows. VideoDecoder needed nothing: CUDA is its device type on Linux, and it reads the decoded picture back into system memory the same way. FFmpeg decodes with NVDEC through the headers of nv-codec-headers, which it finds with pkg-config. dependencies/ffnvcodec carries them, at tag n12.0.16.1: the version of nvEncodeAPI.h webrtc-jni already vendors for NVENC, and one FFmpeg n8.1 accepts. The files are unchanged; the pkg-config file is ours, and CMake fills in the path and hands it to configure. The build needs pkg-config, which the CI runners have, and says so where it is missing. The licenses of the headers are installed into the Linux platform jars under META-INF/licenses/ffnvcodec. FFmpeg loads libcuda.so.1 and libnvcuvid.so.1 with dlopen when a device is created, so the libraries load on a machine with no NVIDIA driver, and a player asked for hardware there decodes in software. 32-bit ARM stays out, as it does in FFmpeg's own configure, which disables NVDEC for every target but x86 and little-endian aarch64 Linux. VA-API stays out too: FFmpeg links libva, which would make it a requirement of the library. This has not been built or run on Linux. configure and the header set were checked on a Mac, against FFmpeg's real configure with a stand-in for pkg-config: the version constraint is accepted, and the four headers compile together. Whether FFmpeg then builds the NVDEC code for Linux could not be seen there.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Only macOS was built and run. The Windows and Linux commits were written on a Mac, which has no Windows or Linux compiler. The CI jobs for those platforms compile them for the first time, and they need a run on a machine with a GPU decoder: see Needs testing.
A
MediaPlayer, and aMediaFileSource, can be asked to decode H.264 and VP9 video on the media engine or GPU of the machine instead of the processor. It is off unless asked for.API
MediaPlayer(reader, videoSource, audioSource, boolean hardwareDecoding), andMediaFileSourceconstructors with the flag. The existing constructors are unchanged and mean software.MediaPlayer#isHardwareDecoding()tells what is in use. That can be less than was asked for, and it can turnfalsewhile playing, but nevertrueagain.Behavior
The pictures are the same as software decoding gives. A decoded picture is read back from the hardware into system memory as NV12 and goes through the I420 conversion that already exists for formats WebRTC does not take.
What it saves is processor time, not the copy. Measured on an Apple M2 with synthetic media (noisy frames, so decoding is not trivial), process CPU time per frame:
About seven to eight times less. On the clock, multi-threaded software is two to three times faster per frame, which does not matter at playback speed. These are figures for one machine, not a promise; the guide says so.
Fallback to software, without the player noticing more than
isHardwareDecoding()turningfalse:The fallback lives in
VideoDecoder: an error from it would end playback.Native side
VideoDecoderis the only place that knows. It picks the hardware device type of the platform (VideoToolbox, D3D11VA then DXVA2, CUDA), sets aget_formatcallback that takes the hardware pixel format where it is offered, and brings hardware frames into system memory.MediaPlayergetsSetHardwareDecodingandIsHardwareDecoding, and the JNIcreatea flag and a getter.dependencies/ffmpeg/CMakeLists.txt), H.264 and VP9 only:--enable-videotoolbox,h264_videotoolbox,vp9_videotoolbox--enable-d3d11va --enable-dxva2,h264_d3d11va2,vp9_d3d11va2,h264_dxva2,vp9_dxva2. FFmpeg loads d3d11.dll, dxgi.dll, d3d9.dll and dxva2.dll itself when a device is created.--enable-ffnvcodec --enable-nvdec,h264_nvdec,vp9_nvdec. FFmpeg loadslibcuda.so.1andlibnvcuvid.so.1withdlopen, so the libraries load on a machine with no NVIDIA driver.The list of components changed, so the next build of every platform rebuilds FFmpeg once. The WebRTC caches are not affected.
dependencies/ffnvcodec(new): FFmpeg needs the headers ofnv-codec-headersfor NVDEC and finds them with pkg-config. The four headersdynlink_cuda.h,dynlink_cuviddec.h,dynlink_nvcuvid.handdynlink_loader.hare taken unchanged from tagn12.0.16.1, the version ofnvEncodeAPI.hthatwebrtc-jnivendors for NVENC and one FFmpeg n8.1 accepts (ffnvcodec >= 12.0.16.1 ffnvcodec < 12.1).nvEncodeAPI.his copied from the NVENC directory, since configure checks for it too. The pkg-config file is ours; CMake fills in the path. The licenses of the files are collected inLICENSEand installed into the Linux platform jars underMETA-INF/licenses/ffnvcodec. The build needspkg-configon Linux, which the runners have, and says so where it is missing.Testing
HardwareDecodingTest(new, 7 tests), with media made for it: the existing assets are VP8 and MS-MPEG4, for which no platform has a decoder.media-test-h264.mkvis H.264 High from VideoToolbox's encoder,media-test-vp9.webmis VP9 from libvpx,media-test-vp9-444.webmthe same in profile 1. 320x240, 15 fps, two seconds, with a key frame every second.h264MatchesSoftware,vp9MatchesSoftware: the file is played with and without the flag, and the CRC of the luma of every frame is the same.seeksInHardware: the decoder is flushed by a seek and goes on in hardware.codecWithoutHardwareFallsBack: the VP8 asset, no hardware decoder, plays in software.streamTheHardwareDoesNotTakeFallsBack: VP9 4:4:4, asked for hardware, plays in software with the same pictures.fileSourceTakesTheOption,softwareIsTheDefault.A machine without a hardware decoder skips the tests that need one;
-Dwebrtc.test.hardwareDecoding=truemakes them fail instead.Not verified yet:
configureand the header set were checked on a Mac against FFmpeg's real configure with a stand-in for pkg-config: the version constraint is accepted and the four headers compile together. Whether FFmpeg then builds the NVDEC code for Linux could not be seen there.Needs testing
On a machine with a GPU decoder (a recent AMD, Intel or NVIDIA GPU on Windows; an NVIDIA GPU on Linux):
This fails unless the player decodes in hardware, and the frames are compared with software. The module's tests only run from the reactor (
-am): started alone they use the artifacts in~/.m2, which can be from other builds.Docs
docs/guide/media/media-files.mdhas a new section, "Decoding in Hardware": the flag, the platforms, the fallback, and the measurements as measurements.