Repository navigation
Conversation
Windows Media Foundation lists the hardware H.264 encoders, and Coded now opens the first one that takes NV12 and answers H.264: async MFT fed NV12 at a promised stride, drained through its event queue, CBR at the asked bitrate, keyframes through CODECAPI_AVEncVideoForceKeyFrame, output normalized to Annex B. A machine with no hardware MFT, or one that refuses its setup, keeps the OpenH264 path and its words, and skips the download only when the hardware will take the job. The OpenH264 retry, cache and failure words are untouched.
…by hand Windows names the interface ICodecAPI, under MediaFoundation in this crate, not IMFCodecAPI. And rustc does not walk a ManuallyDrop on a union field by itself, so each VARIANT body layer is taken explicitly. open_h264 loses the frame size it never used: OpenH264 learns the size from its first frame.
itz4blitz
requested changes
Nov 10, 2026
itz4blitz
left a comment
Collaborator
There was a problem hiding this comment.
Draft, so this is early feedback, but I would not merge it as it stands. Thanks for taking this on, the shape (one Coded surface, MF behind it, OpenH264 as the fallback) is the right one.
Blocking
pr-gateis red and it is real, not flaky: "A feature needs an e2e scenario". Perdocs/quality-gate.mda feature needs ane2e/scenarios/<id>in this PR. If the lab cannot exercise real encoder hardware, the scenario can still drive a share on Windows and assert the fallback path and the Annex B output; say so under Proof rather than leaving it as "none yet".- Types are set in the wrong order in
Encoder::open.SetInputTypeis called beforeSetOutputType. Media Foundation's H.264 encoders (and the NVENC/QSV/AMF MFTs) generally want the output type first and reject the input type withMF_E_TRANSFORM_TYPE_NOT_SET. As written I expect many machines to fall straight back to OpenH264, which defeats the feature. Please swap them and, ideally, check it on real NVIDIA/Intel/AMD hardware. - The profile fallback quietly changes what goes on the wire. If the encoder refuses
ConstrainedBase/level,openretries with no profile and only logs at debug. The PR body and06-calls.mdboth promise constrained baseline, but the SFU and receivers get whatever the driver picks (Main/High). Either fail over to OpenH264 in that case, or read the negotiated profile back and reject non-baseline. - A failure after open is fatal.
ProcessInput/ProcessOutputerrors (MF_E_TRANSFORM_STREAM_CHANGE,NEED_MORE_INPUT, a driver reset, GPU removed) come back asErrand nothing falls back to OpenH264 mid-share. Sinceneeds_openh264()now skips the earlyload_api(), a failure here also ends up as the share dying rather than switching codecs. A runtime failover inCoded::encode(swapBackendto OpenH264 on error and force a keyframe) would make "falls back exactly as before" actually true.
Should fix
MFSampleExtension_CleanPointis checked with.is_ok(). That tells you the attribute exists, not that it is TRUE. Read the value and require it to be non-zero, otherwise a delta frame can be tagged as a key frame.encodecan block the capture thread up to 120 ms per frame when the encoder is holding frames back (lookahead or a slow driver). At 15 to 60 fps that stalls capture. Consider a much shorter wait, or never waiting after the first frame.- Leaks: in
enumerateandopen, only entry 0 of theMFTEnumExarray is taken; the otherIMFActivates are never released, andenumeratereleases none. AlsoShutdownObjecton the chosen activate is never called, andMFStartup/CoInitializeExare never balanced. Small, but easy to get right. available()is cached for the whole process and is first evaluated on whatever thread calls it. ARPC_E_CHANGED_MODEon that one thread would disable hardware encoding for the rest of the run. Do the probe on a dedicated thread, or do not cache an error caused by the thread's apartment.
Tests
The unit tests cover nv12, annex_b, and the packing helpers well. Nothing covers Encoder::open/encode, key-frame detection, or the fallback after a refusal (a small trait or closure seam for the MFT would let you test those without hardware).
Also: the PR body has literal backslashes where the code spans should be. Not a blocker.
…ion and helper tests
… failure falls back to OpenH264
…ut a block they do not need
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.
What changed and why
A screen share or camera on Windows now asks Media Foundation for the machine's own hardware H.264 encoder first:
crates/mediad/src/mf_encoder.rsenumerates the hardware MFTs (MFTEnumEx), drives the first one that takes NV12 and answers H.264 as an async transform, and normalizes the output to the Annex B stream the SFU and the OpenH264 decoder already take. A machine that lists no hardware encoder, or one that refuses its setup, falls back to the OpenH264 runtime download exactly as before, with its words untouched; on a machine where the hardware takes the job, the download is skipped. One encode surface (Coded), two backends behind it.Review changes in this push:
MF_E_TRANSFORM_TYPE_NOT_SET).MF_MT_MPEG2_PROFILE) and must be baseline (66) or constrained baseline (256). A driver that kept Main or High, or names no profile, is refused in its own words andCodedfalls back to OpenH264. Nothing is retried without the profile, so what goes on the wire is always what the SFU was told to expect.Coded::encodeswaps the backend to OpenH264 (loaded on demand) with a forced keyframe and the track keeps going. Until the OpenH264 download lands, the frame is skipped and the next one tries again.MFSampleExtension_CleanPointis read (GetUINT32) and must be non-zero, not merely present.IMFActivatefromMFTEnumExis released (inenumerateand at open), the chosen activate isShutdownObjectafterActivateObject, andMFStartup/CoInitializeExare balanced by a guard the encoder holds — Media Foundation after the transform, the apartment last.available()probe runs once on a thread of its own, so the answer is the machine's and not the asking thread's apartment; a probe that never really ran (its thread would not start) is not cached.One encode surface (
Coded), two backends behind it; the asks of the MFT are narrowed to a small trait (Mft) with the machine's transform handed over by aSource, which is what lets the tests below run without hardware.Type
Platforms
Proof
Scenario:
e2e/scenarios/hardware-encoder-fallback— its driver runs, per test, the Media Foundation encoder's unit tests against the fake-MFT seam (no devices, no encoder hardware, no network): the encoder selection, the H.264 output type refused, a driver that kept Main/High or named no profile (refused in its own words,Codedsettling on OpenH264), keyframe detection requiringCleanPointnon-zero, the frame loop with its key/tune asks, the close, the probe on its own thread, and the pure NV12 and Annex B helpers. On the base it fails: the module does not exist, so cargo reports no matching tests.What this scenario cannot say: that a real NVIDIA/Intel/AMD MFT negotiates output-first, keeps constrained baseline, and survives a whole call. That needs the lab recording a real share on a machine with a hardware MFT, which is the released-client proof this scenario points to. Until then, the CI runners prove the decision, the refusal and the fallback paths, and the OpenH264 fallback keeps its own covered path.
Compatibility
No protocol change: the same H.264 constrained baseline goes out, Annex B, one layer — and now enforced rather than assumed, since a driver that would send anything else is refused and OpenH264 takes over. An older server and an older client keep working.
Risk and rollback
If a hardware MFT misbehaves on a machine, the encoder returns its own truthful words and
Codedfalls back to OpenH264 — at open, and now also mid-share; the fallback is the previous behavior verbatim. Revert the commit to return entirely to OpenH264.Release note
Screen shares and cameras on Windows are encoded by the machine's own hardware encoder, with the OpenH264 download as the fallback.