Skip to content

Add support for Nvidia Jetson V4L2 M2M video encoder - #1161

Merged
chenosaurus merged 39 commits into
mainfrom
dc/exp/jetson_encode_0527
Jun 12, 2026
Merged

Add support for Nvidia Jetson V4L2 M2M video encoder#1161
chenosaurus merged 39 commits into
mainfrom
dc/exp/jetson_encode_0527

Conversation

@chenosaurus

Copy link
Copy Markdown
Contributor
  • Add support for Nvidia Jetson V4L2 M2M video encoder
  • Support H264, H265, AV1 encoding
  • Update the local_video example with --source flag that can be uvc or argus

@github-actions

Copy link
Copy Markdown
Contributor

Changeset

The following package versions will be affected by this PR:

Package Bump
libwebrtc patch
webrtc-sys patch

Comment thread examples/local_video/Cargo.toml Outdated
nokhwa = { git = "https://github.com/l1npengtul/nokhwa", rev = "4923ecab7cf26f9dba83867a15a9d8662d021296", default-features = false, features = ["input-msmf"] }

[build-dependencies]
cc = "1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the workspace dependency here (see top-level Cargo.toml). The parallel feature is enabled for better performance

fallback
}

fn find_video_outbound_stats(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Outside the scope of this PR, but we might want to add some helpers in the core SDK to handle this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea good idea

Comment thread examples/local_video/src/publisher.rs Outdated

/// `CLOCK_MONOTONIC` value used to translate Argus sensor timestamps into wall time.
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
fn monotonic_time_ns_now() -> Option<u64> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): Consider keeping Argus specific helpers in their own module to keep the main module lean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved them to the argus mod

use std::io;

/// Opaque handle to an Argus capture session.
pub struct ArgusCaptureSession {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: While this is probably fine for an example, C/C++ bindings typically live in their own -sys crates (e.g., webrtc-sys, argus-sys) and then provide a higher level Rust wrapper that hides any FFI types. Also outside the scope of this PR, but I do see value in eventually shipping a dedicated crate for this and updating the example to use it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call out, I've created a new project to create new livekit-capture crate and migrate all capture related code there. Will refactor examples once we have that.

/// Captures a Jetson DMA-buffer backed video frame.
///
/// `pixel_format` is `0` for NV12 and `1` for YUV420M.
pub fn capture_dmabuf_frame(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This and the following method should only be compiled for Linux.

unsafe impl Send for ArgusCaptureSession {}

extern "C" {
fn lk_argus_create_session(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is cool--if I'm tracking the pieces right, couldn't this code be used by consumers in library form (vs example runnable form) to do Argus frame capture? Or is the expectation that user pipelines will do their own Argus interfacing, this is just an example of how it connects with LiveKit?

Or am I not following right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, we plan on creating a dedicated capture crate that contains all the different platform capture paths. For now this is just inline in the example app so it can run on a jetson. I would expect customers to do their own integration, and they will probably use C++ SDK if they were using libargus anyways.

int scaled_height) override;

// DMA buffer accessors
int dmabuf_fd() const { return dmabuf_fd_; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: in the C++ SDK we aligned all methods to camelBack() case, since this isn't in the SDK not a biggie just for your awareness. If the webrtc stuff does snake_case for base accessors and PascalCase for operations (which it appears to from the override lines above), nothing to change such that it's consistent.

Comment thread webrtc-sys/src/jetson/av1_encoder_impl.h
const ::webrtc::VideoFrame& input_frame,
bool is_keyframe);

const webrtc::Environment& env_;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: I am generally avoidant of inheritance (esp double inheritance, given this already extends VideoEncoder, but there seems to be a lot of shared state between these _impl classes.

Consider a JetsonVideoEncoder class that extends VideoEncoder which these then extend, or for composition instead, a helper object that defines all the common variables/methods and then is instantiated in each of these. One layer of indirection, but less copy/pasta

public:
struct LayerConfig {
int simulcast_idx = 0;
int width = -1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: std::optional is more modern/clear than default negative values, but this certainly works given the context of the data, and is more lightweight/C-like

Comment thread webrtc-sys/src/jetson/jetson_mmapi_encoder.cpp
Comment thread webrtc-sys/src/jetson/jetson_mmapi_encoder.cpp Outdated
(!output_is_nv12_ && buffer->n_planes > 2) &&
GetPitchAndHeightFromNvBufSurfaceFd(buffer->planes[2].fd, 2, &v_pitch, &v_h, &v_np);

auto stride_from_plane = [&](int plane_index,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this lambda (and similar sections of this method) could be written in a way that don't depend on HW state, such that they could be unit tested

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored into a new file, added unit tests

Comment thread webrtc-sys/src/jetson/jetson_mmapi_encoder.h Outdated
Comment thread webrtc-sys/build.rs
let jetson_mmapi_include = PathBuf::from("/usr/src/jetson_multimedia_api/include");
if jetson_mmapi_include.exists() {
let jetson_classes_dir =
PathBuf::from("/usr/src/jetson_multimedia_api/samples/common/classes");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if this path is valid as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I wonder if we should just copy the files over and include them in our repo like we do w/ the nvenc related files

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloudwebrtc any opinions? I see for nvenc we added NvEncoder.x NvDecoder.x into the repo.

@chenosaurus
chenosaurus merged commit 527aba5 into main Jun 12, 2026
23 checks passed
@chenosaurus
chenosaurus deleted the dc/exp/jetson_encode_0527 branch June 12, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants