Repository navigation
Conversation
Same-size UYVY422 and YUYV422 frames copy to NV12 in scalar or AVX2 code. convert reuses output buffers after the last frame reference drops.
|
@kyzer1023 is attempting to deploy a commit to the Cap Software Inc Team on Vercel. A member of the Team first needs to authorize it. |
| let mut pool = OUTPUT_POOL.lock(); | ||
| let same_size = pool.iter().filter(|buf| buf.size == size).count(); | ||
| if same_size < POOLED_PER_SIZE { | ||
| pool.push(PooledBuffer { ptr: data, size }); |
There was a problem hiding this comment.
OUTPUT_POOL limits buffers per allocation size but never limits total cached bytes or removes old sizes. Changing output sizes leaves each size's buffers allocated until the app exits, even after every converter and frame pool drops. Sixteen cached 4K NV12 frames hold roughly 190 MiB before padding.
Consider adding a total byte limit with eviction, or tying the cache to the converter's lifetime.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/frame-converter/src/output_frame.rs
Line: 185-188
Comment:
**Old sizes keep memory**
`OUTPUT_POOL` limits buffers per allocation size but never limits total cached bytes or removes old sizes. Changing output sizes leaves each size's buffers allocated until the app exits, even after every converter and frame pool drops. Sixteen cached 4K NV12 frames hold roughly 190 MiB before padding.
Consider adding a total byte limit with eviction, or tying the cache to the converter's lifetime.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Fixed in 8734f17 by removing the pool. output_frame.rs is deleted and frame_pool.rs allocates with frame::Video::new again. The encoder only reuses the one converted frame it already owned.
|
Thanks @kyzer1023, the kernel itself is careful work (stride, odd size and overlap checks all look right). The problem is where it's wired in. |
UYVY422 and YUYV422 frames become YUV420P or NV12 through AVX2 on the encoder queue path. Odd sizes, scaling, other formats, and machines without AVX2 stay on swscale. The unused frame-converter output pool is removed.
|
Thanks @richiemcilroy, you were right: nothing constructs On the real path the conversion step is 2.09x at 1080p UYVY and 1.57x at 1080p YUYV (2.55x and 1.44x at 720p, 1.99x and 1.88x at 4K), median of 31. The earlier 8x was against @greptileai review |
| Yuyv, | ||
| } | ||
|
|
||
| struct Job { |
There was a problem hiding this comment.
Job, positive_linesize, and covers are compiled on every target, but all their uses are inside x86_64-only code. On Apple Silicon they produce unused-code warnings, which fail the macOS CI job because it runs Clippy with -D warnings.
Add #[cfg(target_arch = "x86_64")] to all three declarations or move them into the gated module.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/enc-ffmpeg/src/video/packed_yuv.rs
Line: 10
Comment:
**Apple Silicon checks fail**
`Job`, `positive_linesize`, and `covers` are compiled on every target, but all their uses are inside `x86_64`-only code. On Apple Silicon they produce unused-code warnings, which fail the macOS CI job because it runs Clippy with `-D warnings`.
Add `#[cfg(target_arch = "x86_64")]` to all three declarations or move them into the gated module.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 333451c. Job, positive_linesize and covers are now #[cfg(target_arch = "x86_64")], and the needless return in the non-x86 tail is gone. Clippy with -D warnings is clean for aarch64 Linux and x86_64. I also fixed the mod packed_yuv ordering in video/mod.rs for rustfmt.
|
@greptileai review |
Summary
Camera frames that arrive as UYVY422 or YUYV422 at the encoder's output size are now converted inside the H264 encoder with an AVX2 kernel instead of swscale. This is the path recordings actually use. The conversion step is 1.4x to 2.6x faster depending on format and size. Odd sizes, scaling, other pixel formats, and CPUs without AVX2 keep the existing swscale context.
Change
crates/enc-ffmpeg/src/video/packed_yuv.rs: AVX2 UYVY/YUYV to YUV420P and NV12 kernels, plusconvert_or_scale. It copies luma and averages each vertical chroma pair, rounding half up.try_convertreturns false for odd sizes, a size change, any other format, or no AVX2 at runtime, and the caller then runs the encoder's swscale context.crates/enc-ffmpeg/src/video/h264.rsandh264_packet.rs:queue_frame_to,queue_frame_reusable,encode_frameandencode_frame_reusablecallpacked_yuv::convert_or_scaleinstead ofconverter.run. libx264 gets YUV420P. NV12 is produced only when the opened encoder's format is NV12.frame-converterwiring, output frame pool, andoutput_frame.rsare removed. Nothing in the app or CLI constructsAsyncCameraMp4Muxer, so that path never ran on a recording, and the pool could hold about 190 MiB at 4K.Camera call paths that reach this code: macOS
MacOSFragmentedM4SMuxerthroughSegmentedVideoEncoder::queue_frame, Windowswin.rsthroughH264Encoder::queue_frame(after UYVY to YUYV), LinuxMp4Muxerthroughqueue_frame, and the out-of-process muxer throughH264PacketEncoder::encode_frame.Overlap with #2981: both PRs change the same four
converter.runlines. Whichever merges second should call one helper that tries packed YUV, then the RGB path, then swscale.Benchmarks
Conversion step only: the previous
scaler.run(FAST_BILINEAR, YUV420P) versusconvert_or_scale, output frame reused. 5 warmup samples, then the median of 31 runs.Hardware: Intel Xeon, family 6 model 207, 4 vCPU, KVM, AVX2. rustc 1.88.0, libavcodec 61.19.100.
Tests
packed_yuv::tests: 6 passed with--include-ignored(5 tests plus the bench).try_convertreturn false, andconvert_or_scalematches swscale byte for byte.libx264_queue_accepts_packed_yuvskips on the cloud machine because its libavcodec was built without libx264. It runs wherever libx264 is linked.cargo fmt --all -- --checkpasses.cargo clippy -p cap-enc-ffmpeg --all-targets -- -D warningsis clean on x86_64 Linux and on aarch64 Linux. The AVX2 helpers (Job,positive_linesize,covers) are compiled only on x86_64, so ARM builds have no dead code. An aarch64-apple-darwin cross check could not build on Linux because there is no Apple SDK, so macOS CI confirms that target.Test plan
cargo test -p cap-enc-ffmpeg --release --lib packed_yuv::tests -- --include-ignored --nocapture --test-threads=1Risk
The fast path runs only for same-size, even-dimension UYVY or YUYV with AVX2. Everything else uses the encoder's existing swscale context unchanged. Output differs from swscale by at most 2 in luma and 3 in chroma on YUV420P.


Confidence Score: 5/5
The PR appears safe to merge, with both previous findings addressed and no new blocking issues found.Summary
Adds AVX2 conversion from same-size, even-dimension UYVY/YUYV frames to YUV420P or NV12 in both H.264 encoder paths.
Reviews (3) · Last reviewed commit: "Gate x86 only helpers and fix formatting" · Reviewed by Greptile