Skip to content

Commit 3d1bad4

Browse files
kixelatedclaudeQizot
authored
feat(moq-video): native per-platform H.264 codecs + zero-copy capture, drop ffmpeg (#1691)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Jakub Perżyło <jakub.perzylo@swmansion.com>
1 parent 598b99a commit 3d1bad4

21 files changed

Lines changed: 3561 additions & 698 deletions

Cargo.lock

Lines changed: 975 additions & 170 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/moq-cli/src/publish.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ pub enum PublishFormat {
2626
#[cfg(feature = "capture")]
2727
#[derive(clap::Args, Clone)]
2828
pub struct CaptureArgs {
29-
/// Camera device. Platform-specific: an avfoundation index/name on macOS,
30-
/// a `/dev/videoN` path on Linux, or a dshow device name on Windows.
31-
/// Omit to use the default camera.
32-
#[arg(long)]
29+
/// Camera device. Platform-specific: an AVFoundation `uniqueID` on macOS, or
30+
/// a camera index / `/dev/videoN` path on Linux. Omit to use the default
31+
/// camera. Ignored with `--screen`.
32+
#[arg(long, conflicts_with = "screen")]
3333
pub camera: Option<String>,
3434

35+
/// Capture a display (whole screen) instead of a camera. macOS only.
36+
#[arg(long)]
37+
pub screen: bool,
38+
3539
/// Requested capture width. The camera snaps to its nearest supported mode.
3640
#[arg(long)]
3741
pub width: Option<u32>,
@@ -52,7 +56,7 @@ pub struct CaptureArgs {
5256
#[arg(long, conflicts_with = "software")]
5357
pub hardware: bool,
5458

55-
/// Force the software encoder (libx264).
59+
/// Force the software encoder (openh264).
5660
#[arg(long)]
5761
pub software: bool,
5862

@@ -99,7 +103,7 @@ enum Source {
99103
/// Decode a container read from stdin (or an HLS playlist).
100104
Stream(PublishDecoder),
101105
/// Capture from local devices. The per-medium producers are built on their
102-
/// own capture threads (camera via ffmpeg, microphone via cpal), publishing
106+
/// own capture threads (native camera/screen capture, microphone via cpal), publishing
103107
/// onto the shared broadcast + catalog; [`Publish::run`] drives them
104108
/// concurrently.
105109
#[cfg(feature = "capture")]
@@ -220,7 +224,11 @@ impl Publish {
220224
impl CaptureArgs {
221225
fn video_config(&self) -> moq_video::capture::Config {
222226
let mut config = moq_video::capture::Config::default();
223-
config.device = self.camera.clone();
227+
if self.screen {
228+
config.source = moq_video::capture::Source::Display;
229+
} else {
230+
config.device = self.camera.clone();
231+
}
224232
config.width = self.width;
225233
config.height = self.height;
226234
config.framerate = self.fps;

rs/moq-video/Cargo.toml

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,55 @@ categories = ["multimedia", "multimedia::video", "multimedia::encoding"]
1515
[lib]
1616
doctest = false
1717

18+
[features]
19+
# NVIDIA NVENC hardware encoder (Linux only). Off by default; release builds
20+
# for Linux enable it. cudarc loads CUDA dynamically (no build-time CUDA), and
21+
# the NVENC API lives in the driver, so this is not a build dependency.
22+
nvenc = ["dep:nvidia-video-codec-sdk", "cudarc/dynamic-loading", "cudarc/cuda-12020"]
23+
# Intel/AMD VAAPI hardware encoder (Linux only). Off by default; release builds
24+
# for Linux enable it. Links libva (stable soname); pairs with a V4L2 dmabuf
25+
# capture for a fully zero-copy path.
26+
vaapi = ["dep:cros-codecs"]
27+
1828
[dependencies]
1929
anyhow = "1"
2030
bytes = "1"
21-
# H.264 encode plus webcam capture (libavdevice). One system dependency
22-
# covers both, and unlike a pure-Rust encoder it exposes the platform
23-
# hardware encoders (h264_videotoolbox / h264_nvenc / h264_vaapi) we want
24-
# for real-time capture. The `device` feature pulls in libavdevice for
25-
# avfoundation / v4l2 / dshow camera input.
26-
ffmpeg-next = { version = "8", features = ["device"] }
31+
# VAAPI H.264 encoder + dmabuf import. Gated behind `vaapi` + a linux cfg; pinned
32+
# (pre-1.0 churns). Pulls libva (cros-libva) when the feature is on.
33+
cros-codecs = { version = "=0.0.6", optional = true, default-features = false, features = ["vaapi"] }
34+
cudarc = { version = "0.16", optional = true, default-features = false, features = ["driver"] }
2735
moq-mux = { workspace = true }
2836
moq-net = { workspace = true }
37+
# Pure-Rust webcam capture (avfoundation / v4l2 / msmf), decoding MJPEG/YUYV to
38+
# RGBA. Platform input backends are enabled per-target below.
39+
nokhwa = { version = "0.10.11", default-features = false, features = ["decoding"] }
40+
# NVENC backend, gated behind the `nvenc` feature + a linux cfg. Listed here
41+
# (not under a target table) so the feature can reference it on any host; the
42+
# code that uses it is `cfg(target_os = "linux")`.
43+
nvidia-video-codec-sdk = { version = "0.4", optional = true }
44+
# Vendored, statically-linked software H.264 fallback (no system dependency).
45+
openh264 = "0.9.3"
2946
thiserror = "2"
3047
tokio = { workspace = true, features = ["rt", "macros"] }
3148
tracing = "0.1"
49+
# RGBA -> I420 color conversion (replaces ffmpeg swscale).
50+
yuv = "0.8.14"
51+
52+
[target.'cfg(target_os="linux")'.dependencies]
53+
nokhwa = { version = "0.10.11", features = ["input-v4l"] }
54+
55+
[target.'cfg(target_os="macos")'.dependencies]
56+
block2 = "0.6.2"
57+
dispatch2 = "0.3.1"
58+
nokhwa = { version = "0.10.11", features = ["input-avfoundation"] }
59+
objc2 = "0.6.4"
60+
objc2-av-foundation = "0.3.2"
61+
objc2-core-foundation = "0.3.2"
62+
objc2-core-media = "0.3.2"
63+
objc2-core-video = "0.3.2"
64+
objc2-foundation = "0.3.2"
65+
objc2-screen-capture-kit = "0.3.2"
66+
objc2-video-toolbox = "0.3.2"
67+
68+
[target.'cfg(target_os="windows")'.dependencies]
69+
nokhwa = { version = "0.10.11", features = ["input-msmf"] }

0 commit comments

Comments
 (0)