Skip to content

Commit a3a2fa5

Browse files
authored
AI: Fix race condition causing immediate deletion of new sources. v7.0.127 (#4449) (#4576)
**Problem**: Newly created sources (RTMP/SRT/RTC/RTSP) were being immediately marked as "dead" and deleted by the cleanup timer before publishers could connect, causing "new live source, dead=1" errors. **Root Cause**: All source constructors initialized `stream_die_at_ = 0`, causing `stream_is_dead()` to return `true` immediately since current time was always greater than `0 + 3 seconds`. **Solution**: Changed all four source constructors to initialize `stream_die_at_ = srs_time_now_cached()`, giving newly created sources a proper 3-second grace period before cleanup.
1 parent 6e93dd7 commit a3a2fa5

File tree

9 files changed

+230
-21
lines changed

9 files changed

+230
-21
lines changed

trunk/doc/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The changelog for SRS.
77
<a name="v7-changes"></a>
88

99
## SRS 7.0 Changelog
10+
* v7.0, 2025-11-14, AI: Fix race condition causing immediate deletion of new sources. v7.0.127 (#4449)
1011
* v7.0, 2025-11-11, AI: WebRTC: Support optional msid attribute per RFC 8830. v7.0.126 (#4570)
1112
* v7.0, 2025-11-11, AI: SRT: Stop TS parsing after codec detection. v7.0.125 (#4569)
1213
* v7.0, 2025-11-09, AI: WebRTC: Support G.711 (PCMU/PCMA) audio codec for WebRTC. v7.0.124 (#4075)

trunk/research/players/js/srs.sdk.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ function SrsRtcWhipWhepAsync() {
3434
self.displayStream = null;
3535
self.userStream = null;
3636

37+
// Store the WHIP session resource URL from Location header for cleanup.
38+
self.resourceUrl = null;
39+
3740
// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
3841
// @url The WebRTC url to publish with, for example:
3942
// http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream
@@ -111,6 +114,14 @@ function SrsRtcWhipWhepAsync() {
111114
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
112115
const data = xhr.responseText;
113116
console.log("Got answer: ", data);
117+
118+
// Extract Location header for WHIP session resource URL.
119+
const location = xhr.getResponseHeader('Location');
120+
if (location) {
121+
self.resourceUrl = new URL(location, url).href;
122+
console.log(`WHIP session resource URL: ${self.resourceUrl}`);
123+
}
124+
114125
return data.code ? reject(xhr) : resolve(data);
115126
}
116127
xhr.open('POST', url, true);
@@ -159,6 +170,14 @@ function SrsRtcWhipWhepAsync() {
159170
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
160171
const data = xhr.responseText;
161172
console.log("Got answer: ", data);
173+
174+
// Extract Location header for WHEP session resource URL.
175+
const location = xhr.getResponseHeader('Location');
176+
if (location) {
177+
self.resourceUrl = new URL(location, url).href;
178+
console.log(`WHEP session resource URL: ${self.resourceUrl}`);
179+
}
180+
162181
return data.code ? reject(xhr) : resolve(data);
163182
}
164183
xhr.open('POST', url, true);
@@ -190,6 +209,25 @@ function SrsRtcWhipWhepAsync() {
190209
});
191210
self.userStream = null;
192211
}
212+
213+
// Send DELETE request to WHIP session resource URL to cleanup server resources.
214+
if (self.resourceUrl) {
215+
const xhr = new XMLHttpRequest();
216+
xhr.open('DELETE', self.resourceUrl, true);
217+
xhr.onload = function() {
218+
if (xhr.readyState !== xhr.DONE) return;
219+
if (xhr.status === 200) {
220+
console.log(`WHIP session deleted: ${self.resourceUrl}`);
221+
} else {
222+
console.warn(`Failed to delete WHIP session: ${self.resourceUrl}, status: ${xhr.status}`);
223+
}
224+
};
225+
xhr.onerror = function() {
226+
console.warn(`Error deleting WHIP session: ${self.resourceUrl}`);
227+
};
228+
xhr.send();
229+
self.resourceUrl = null;
230+
}
193231
};
194232

195233
// The callback when got local stream.

trunk/src/app/srs_app_rtc_api.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,16 +491,26 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHtt
491491
vcodec = r->query_get("codec");
492492
}
493493
string acodec = r->query_get("acodec");
494+
// For client to specifies whether encrypt by SRTP.
495+
string srtp = r->query_get("encrypt");
496+
string dtls = r->query_get("dtls");
494497

495-
srs_trace("RTC publish %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, vcodec=%s, acodec=%s",
498+
srs_trace("RTC publish %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, vcodec=%s, acodec=%s, srtp=%s, dtls=%s",
496499
streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), ruc.req_->app_.c_str(), ruc.req_->stream_.c_str(),
497-
remote_sdp_str.length(), eip.c_str(), vcodec.c_str(), acodec.c_str());
500+
remote_sdp_str.length(), eip.c_str(), vcodec.c_str(), acodec.c_str(), srtp.c_str(), dtls.c_str());
498501

499502
ruc.eip_ = eip;
500503
ruc.vcodec_ = vcodec;
501504
ruc.acodec_ = acodec;
502505
ruc.publish_ = true;
503-
ruc.dtls_ = ruc.srtp_ = true;
506+
507+
// For client to specifies whether encrypt by SRTP.
508+
ruc.dtls_ = (dtls != "false");
509+
if (srtp.empty()) {
510+
ruc.srtp_ = config_->get_rtc_server_encrypt();
511+
} else {
512+
ruc.srtp_ = (srtp != "false");
513+
}
504514

505515
// TODO: FIXME: It seems remote_sdp doesn't represents the full SDP information.
506516
ruc.remote_sdp_str_ = remote_sdp_str;

trunk/src/app/srs_app_rtc_source.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ srs_error_t SrsRtcSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtr<Sr
335335
pps = source;
336336
} else {
337337
SrsSharedPtr<SrsRtcSource> source = SrsSharedPtr<SrsRtcSource>(new SrsRtcSource());
338-
srs_trace("new rtc source, stream_url=%s", stream_url.c_str());
338+
srs_trace("new rtc source, stream_url=%s, dead=%d", stream_url.c_str(), source->stream_is_dead());
339339
pps = source;
340340

341341
pool_[stream_url] = source;
@@ -399,7 +399,10 @@ SrsRtcSource::SrsRtcSource()
399399
circuit_breaker_ = _srs_circuit_breaker;
400400

401401
pli_for_rtmp_ = pli_elapsed_ = 0;
402-
stream_die_at_ = 0;
402+
// Initialize stream_die_at_ to current time to prevent newly created sources
403+
// from being immediately considered dead by stream_is_dead() check.
404+
// @see https://github.com/ossrs/srs/issues/4449
405+
stream_die_at_ = srs_time_now_cached();
403406

404407
app_factory_ = _srs_app_factory;
405408
}

trunk/src/app/srs_app_rtmp_source.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ srs_error_t SrsLiveSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtr<S
16591659
pps = source;
16601660
} else {
16611661
SrsSharedPtr<SrsLiveSource> source = app_factory_->create_live_source();
1662-
srs_trace("new live source, stream_url=%s", stream_url.c_str());
1662+
srs_trace("new live source, stream_url=%s, dead=%d", stream_url.c_str(), source->stream_is_dead());
16631663
pps = source;
16641664

16651665
// Callback to notify request of source creation
@@ -1781,7 +1781,10 @@ SrsLiveSource::SrsLiveSource()
17811781
mix_queue_ = new SrsMixQueue();
17821782

17831783
can_publish_ = true;
1784-
stream_die_at_ = 0;
1784+
// Initialize stream_die_at_ to current time to prevent newly created sources
1785+
// from being immediately considered dead by stream_is_dead() check.
1786+
// @see https://github.com/ossrs/srs/issues/4449
1787+
stream_die_at_ = srs_time_now_cached();
17851788
publisher_idle_at_ = 0;
17861789

17871790
rtmp_bridge_ = NULL;

trunk/src/app/srs_app_rtsp_source.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ srs_error_t SrsRtspSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtr<S
194194
pps = source;
195195
} else {
196196
SrsSharedPtr<SrsRtspSource> source = SrsSharedPtr<SrsRtspSource>(new SrsRtspSource());
197-
srs_trace("new rtsp source, stream_url=%s", stream_url.c_str());
197+
srs_trace("new rtsp source, stream_url=%s, dead=%d", stream_url.c_str(), source->stream_is_dead());
198198
pps = source;
199199

200200
pool_[stream_url] = source;
@@ -249,7 +249,10 @@ SrsRtspSource::SrsRtspSource()
249249

250250
req_ = NULL;
251251

252-
stream_die_at_ = 0;
252+
// Initialize stream_die_at_ to current time to prevent newly created sources
253+
// from being immediately considered dead by stream_is_dead() check.
254+
// @see https://github.com/ossrs/srs/issues/4449
255+
stream_die_at_ = srs_time_now_cached();
253256

254257
stat_ = _srs_stat;
255258
circuit_breaker_ = _srs_circuit_breaker;

trunk/src/app/srs_app_srt_source.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ srs_error_t SrsSrtSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtr<Sr
179179
pps = source;
180180
} else {
181181
SrsSharedPtr<SrsSrtSource> source(new SrsSrtSource());
182-
srs_trace("new srt source, stream_url=%s", stream_url.c_str());
182+
srs_trace("new srt source, stream_url=%s, dead=%d", stream_url.c_str(), source->stream_is_dead());
183183
pps = source;
184184

185185
pool_[stream_url] = source;
@@ -1230,7 +1230,10 @@ SrsSrtSource::SrsSrtSource()
12301230
req_ = NULL;
12311231
can_publish_ = true;
12321232
srt_bridge_ = NULL;
1233-
stream_die_at_ = 0;
1233+
// Initialize stream_die_at_ to current time to prevent newly created sources
1234+
// from being immediately considered dead by stream_is_dead() check.
1235+
// @see https://github.com/ossrs/srs/issues/4449
1236+
stream_die_at_ = srs_time_now_cached();
12341237

12351238
stat_ = _srs_stat;
12361239
format_ = new SrsSrtFormat();

trunk/src/core/srs_core_version7.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
#define VERSION_MAJOR 7
1111
#define VERSION_MINOR 0
12-
#define VERSION_REVISION 126
12+
#define VERSION_REVISION 127
1313

1414
#endif

0 commit comments

Comments
 (0)