Skip to content

feat: support NACK retransmission without RTX via in-band resend on main SSRC#980

Open
mstyura wants to merge 1 commit into
algesten:mainfrom
mstyura:nack-is-nack-rtx-is-rtx
Open

feat: support NACK retransmission without RTX via in-band resend on main SSRC#980
mstyura wants to merge 1 commit into
algesten:mainfrom
mstyura:nack-is-nack-rtx-is-rtx

Conversation

@mstyura

@mstyura mstyura commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

I'd like to propose to decouple NACK from RTX. This PR gates NACK on negotiated nack feedback rather than the presence of RTX. When no RTX SSRC is configured, retransmissions are resent in-band on the main SSRC (original PT + sequence number, as libwebrtc does).

The main motivation: str0m can now NACK-retransmit Opus packets, which previously had no loss recovery since audio never gets RTX. In practice this is audible - a long sustained sound (e.g. "AAAAAAA" for 5 seconds) recorded over a lossy network (5%) now plays back without the audible glitches that the dropped packets used to cause.

Also adds PayloadParams::set_resend to configure/disable RTX.

image

@mstyura mstyura force-pushed the nack-is-nack-rtx-is-rtx branch from a0aaa87 to dcfa60e Compare June 19, 2026 13:54
@algesten

Copy link
Copy Markdown
Owner

@mstyura not necessarily arguing against this, but why wouldn't you use RTX also for the Opus case?

RTX is a cryptographic safety to avoid an evil MITM forcing us to repeat the same data over the main SRTP channel. Seems like you want to enable it, no?

@algesten

Copy link
Copy Markdown
Owner

I guess the answer is: libWebRTC doesn't do that.

@algesten algesten left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I'm on the fence about this one.

I do think supporting Opus resends without RTX is a nice goal, but I'm not sure this PR goes all the way. In my mind, I've used the "rtx" meaning "can resend" interchangeably, which means to make the code base consistent in language and logic, there's a deeper take that is needed.

I would want to do that manually myself.

pub fn set_resend(&mut self, resend: Option<Pt>) {
self.resend = resend;
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

pt and resend are set on construction and immutable. There deliberately is no setter for resend.

Comment thread src/streams/send.rs
} else if !param.fb_nack() {
// No RTX PT and no `fb_nack`: the packet cannot be resent (neither
// via RTX nor in-band), so it is de-facto not nackable. This ensures
// there are no entries in the rtx cache we can't resend.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This causes a confusion in the code base. "rtx" is in many places used to mean "can resend" interchangeably. I'm a bit frustrated with myself for doing that back in the day, because at this point this PR is working against language and patterns that are consistent across the code base.

I don't know what the solution is.

Comment thread src/streams/send.rs
// payload decrypts at the receiver.
header_ref.ext_vals.rid_repair = None;
header_ref.clone()
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I'm uncertain about having a new enum value here. Whether we want just NextPacketKind::Resend, but discover the "mode" of whether we are operating with or without RTX.

However this has consequences for the BWE and Pacer, so again. I'm not certain.

@mstyura

mstyura commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

I have the following use-case of str0m. The libwebrtc clients (both native and browsers) connects to str0m-powered backend to send media. The media is then being processed with ffmpeg-next. Sometimes audio packets are lost in such a way that causes audible glitches during playback. libwebrtc only supports nack for audio, so I was thinking that str0m just not yet handle inband retransmits, rather than intentionally not implemented it.

@algesten

Copy link
Copy Markdown
Owner

@mstyura yes. It surely is an omission.

@xnorpx

xnorpx commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Note that RTX usage for audio is not so common due to re-order of packets and causing extra delay (if that matters) In bacnd fec or DRED in Opus is better in terms of latency.

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