Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openclaw/memory/srs-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ About the features supported by SRS.

- **RTMP** — SRS is fundamentally an RTMP server. It supports publishing and playing RTMP streams, which is the core foundation of SRS. All other protocols are built on top of RTMP as the base. v1.0, 2013
- **SRT** — SRS is also an SRT server. It supports publishing and playing SRT streams. SRS uses [libsrt](https://github.com/Haivision/srt) to create the SRT server. v4.0, 2020-01
- **WebRTC** — SRS is also a WebRTC server, supporting WHIP for publishing and WHEP for playing streams. SRS is an SFU (Selective Forwarding Unit) server. It does not support TURN. It does not support P2P for WebRTC. v4.0, 2020-03
- **WebRTC** — SRS is also a WebRTC server, supporting WHIP for publishing and WHEP for playing streams. SRS is an SFU (Selective Forwarding Unit) server. It does not support TURN. It does not support P2P for WebRTC. Supports both RFC 4588 RTX (retransmission with separate SSRC) and legacy retransmission (resending original RTP packets). RFC 4588 RTX support v7.0, 2026-03
- **RTSP** — SRS only supports playing RTSP streams. Currently it only supports converting RTMP to RTSP. v7.0, 2025-07
- **HLS** — SRS supports converting RTMP to HLS. HLS is the best compatibility protocol, supported by all platforms, all browsers, and all operating systems. v1.0, 2013
- **MPEG-DASH** — SRS supports converting RTMP to DASH. DASH is similar to HLS, but not as widely supported by platforms as HLS. v5.0, 2022-11
Expand Down
64 changes: 63 additions & 1 deletion trunk/src/app/srs_app_rtc_conn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1772,6 +1772,36 @@ void SrsRtcPublishStream::on_before_decode_payload(SrsRtpPacket *pkt, SrsBuffer
}

uint32_t ssrc = pkt->header_.get_ssrc();

// Unwrap RTX packet per RFC 4588. RTX packets arrive with RTX SSRC and RTX PT.
// The payload starts with a 2-byte Original Sequence Number (OSN), followed by
// the original RTP payload. Restore the original SSRC, PT, and sequence number,
// then advance the buffer past the OSN so downstream sees the original payload.
SrsRtcTrackDescription *td = find_track_desc_by_rtx_ssrc(ssrc);
if (td) {
if (!buf->require(2)) {
srs_warn("RTC: RTX packet too short, ssrc=%u, rtx_ssrc=%u", td->ssrc_, ssrc);
return;
}

uint16_t osn = (uint16_t)buf->read_2bytes();

srs_trace("RTC: RTX unwrap rtx_ssrc=%u, osn=%u, media_ssrc=%u, pt=%u, left=%d",
ssrc, osn, td->ssrc_, td->media_ ? td->media_->pt_ : 0, buf->left());

pkt->header_.set_ssrc(td->ssrc_);
pkt->header_.set_sequence(osn);
if (td->media_) {
pkt->header_.set_payload_type(td->media_->pt_);
}

ssrc = td->ssrc_;

if (buf->empty()) {
return;
}
}

SrsRtcAudioRecvTrack *audio_track = get_audio_track(ssrc);
SrsRtcVideoRecvTrack *video_track = get_video_track(ssrc);

Expand Down Expand Up @@ -1975,6 +2005,23 @@ SrsRtcAudioRecvTrack *SrsRtcPublishStream::get_audio_track(uint32_t ssrc)
return NULL;
}

SrsRtcTrackDescription* SrsRtcPublishStream::find_track_desc_by_rtx_ssrc(uint32_t ssrc)
{
for (int i = 0; i < (int)audio_tracks_.size(); ++i) {
SrsRtcTrackDescription *desc = audio_tracks_.at(i)->get_track_desc();
if (desc->rtx_ssrc_ && desc->rtx_ssrc_ == ssrc) {
return desc;
}
}
for (int i = 0; i < (int)video_tracks_.size(); ++i) {
SrsRtcTrackDescription *desc = video_tracks_.at(i)->get_track_desc();
if (desc->rtx_ssrc_ && desc->rtx_ssrc_ == ssrc) {
return desc;
}
}
return NULL;
}

void SrsRtcPublishStream::update_rtt(uint32_t ssrc, int rtt)
{
SrsRtcVideoRecvTrack *video_track = get_video_track(ssrc);
Expand Down Expand Up @@ -3653,10 +3700,25 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
}

// set track fec_ssrc and rtx_ssrc
// Note: We match by ssrc_ directly instead of using find_track_description_by_ssrc(),
// because tracks are not yet active (is_active_=false) at this point, and has_ssrc()
// returns false for inactive tracks.
for (int j = 0; j < (int)remote_media_desc.ssrc_groups_.size(); ++j) {
const SrsSSRCGroup &ssrc_group = remote_media_desc.ssrc_groups_.at(j);
uint32_t primary_ssrc = ssrc_group.ssrcs_[0];

SrsRtcTrackDescription *track_desc = stream_desc->find_track_description_by_ssrc(ssrc_group.ssrcs_[0]);
SrsRtcTrackDescription *track_desc = NULL;
if (stream_desc->audio_track_desc_ && stream_desc->audio_track_desc_->ssrc_ == primary_ssrc) {
track_desc = stream_desc->audio_track_desc_;
}
if (!track_desc) {
for (int k = 0; k < (int)stream_desc->video_track_descs_.size(); ++k) {
if (stream_desc->video_track_descs_.at(k)->ssrc_ == primary_ssrc) {
track_desc = stream_desc->video_track_descs_.at(k);
break;
}
}
}
if (!track_desc) {
continue;
}
Expand Down
2 changes: 2 additions & 0 deletions trunk/src/app/srs_app_rtc_conn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ class SrsRtcPublishStream : public ISrsRtcPublishStream
srs_error_t on_twcc(uint16_t sn);
SrsRtcAudioRecvTrack *get_audio_track(uint32_t ssrc);
SrsRtcVideoRecvTrack *get_video_track(uint32_t ssrc);
// Find track description whose rtx_ssrc_ matches the given SSRC, for RTX (RFC 4588) unwrapping.
SrsRtcTrackDescription* find_track_desc_by_rtx_ssrc(uint32_t ssrc);
void update_rtt(uint32_t ssrc, int rtt);
void update_send_report_time(uint32_t ssrc, const SrsNtp &ntp, uint32_t rtp_time);
};
Expand Down
Loading
Loading