Skip to content

Conversation

@Ramil980
Copy link

@Ramil980 Ramil980 commented Jan 5, 2026

This PR introduces AV1 RTP packetizer and depacketizer support to str0m and enables AV1 in SDP by default

The packetizer is based on the libwebrtc implementation while the depacketizer is adapted from the pion (Go) implementation.

Unit tests for both the packetizer and depacketizer were adapted from libwebrtc, and AV1 support has been verified end-to-end using the chat example.

This is my first time writing code in Rust, so I’d really appreciate any feedback or suggestions 🙂

Copy link
Owner

@algesten algesten left a comment

Choose a reason for hiding this comment

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

Hi there! Thanks for this!

Overall this looks good. I left some comments in the code.

I suspect there is room to reduce the number of allocations done by the code (the liberal use of Vec), but I'm not overly worried about that. We can merge this PR and revisit with a follow-up.

/// Default payload type for VP9 profile 0 RTX.
pub(crate) const PT_VP9_RTX: Pt = Pt::new_with_value(99);

/// Default payload type for VP9 profile 0.
Copy link
Owner

Choose a reason for hiding this comment

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

Doc is wrong here

//c.enable_vp8(true);
//c.enable_h264(true);
//c.enable_vp9(true);
c.enable_av1(true);
Copy link
Owner

Choose a reason for hiding this comment

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

We shouldn't change the defaults here. Let's have a discussion on whether AV1 should be enabled by default. Ideally if you join us on Discord, otherwise, we can discuss in #541

payload: Vec<u8>,
size: usize,
}

Copy link
Owner

Choose a reason for hiding this comment

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

In these files we try to follow a pattern of having the main exportables from the mod first in the file. See vp8.rs for instance. Then we have internal structs and helper functions in order of appearance.

/// - T (4 bits) - OBU Type: This 4-bit field specifies the type of data structure contained in the OBU payload.
/// - E (1 bit) - OBU Extension Flag: A flag indicatin if the optional obu_extension_header is present.
/// - S (1 bit) - OBU Has Size Field: A flag indicating whether the obu_size syntax element will be present
/// - R (1 bit) - OBU Reserved Bits: must be set to 0. The value is ignored by a decoder.
Copy link
Owner

Choose a reason for hiding this comment

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

The doc here should be in the order the fields are in the ascii art.

/// 0 1 2 3 4 5 6 7
/// +---------------+
/// |F| T |S|E|R|
/// +---------------+
Copy link
Owner

Choose a reason for hiding this comment

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

Why is there whitespace after |R|?

obu.obu_type() != OBU_TYPE_TILE_LIST &&
obu.obu_type() != OBU_TYPE_PADDING {
parsed_obus.push(obu);
}
Copy link
Owner

Choose a reason for hiding this comment

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

I suspect it would be better to have an enum.

enum ObuType {
  TemporalDelimiter = 2,
  TileList = 8}

That would allow you to add logic to the type itself.

impl ObuType {
   fn include_in_parsed(&self) -> bool {
      matches!(self, Self::TemporalDelimiter | Self::TileList | …)
   }
}

Copy link
Owner

Choose a reason for hiding this comment

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

Maybe bike shed the name on the logic. include_in_packetized or something?

return;
}

let mut packets: Vec<Packet> = vec![];
Copy link
Owner

Choose a reason for hiding this comment

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

This looks highly reusable if we store it on the struct and clear between uses. That way we can avoid new allocations for every frame.

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

Add depacketizer to fuzz

src/_internal_test_exports/fuzz.rs

let codec = match rng.u8(4)? {
0 => Codec::Opus,
1 => Codec::Vp8,
2 => Codec::Vp9,
3 => Codec::H264,
4 => Codec::H265,
_ => unreachable!(),
};

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

Fix CI, just ping here with me or algesten so we can trigger new runs.

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

Might be worth looking into this impl as well: https://patchwork.ffmpeg.org/project/ffmpeg/patch/[email protected]/

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

Might be worth creating a pcap from a couple of other packetizer implementations as well to find differences.

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

I think this is a good start, it needs some performance tweaks, tests and more checking against the spec. Ideally cross tests agains other implementations as well.

@Ramil980
Copy link
Author

Ramil980 commented Jan 6, 2026

I will fix the CI issues and comments on weekend, will be busy next few days

@xnorpx is there an automatic way of fixing lint errors in Rust or I need to fix them manually?

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

I will fix the CI issues and comments on weekend, will be busy next few days

@xnorpx is there an automatic way of fixing lint errors in Rust or I need to fix them manually?

cargo clippy --fix can do some.

I tend to use llms for clippies as it usually just follow directions and usually do the correct thing.

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

You can check the cargo clippy and format commands in the ci job here: https://github.com/algesten/str0m/blob/main/.github/workflows/cargo.yml

So you can run it locally.

Only thing is snowflake that might need some manual editing and then make sure cargo fmt does not mess it up.

@xnorpx
Copy link
Collaborator

xnorpx commented Jan 6, 2026

Maybe also just copy all unittests that pion has as well

@sr1990
Copy link

sr1990 commented Jan 10, 2026

Hi @Ramil980, how did you capture pcap data?

@Ramil980
Copy link
Author

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.

4 participants