diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index d7d03da73..a1d216280 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 7.0 Changelog
+* v7.0, 2025-09-21, Fix WHIP with transcoding bug. v7.0.92 (#4495)
* v7.0, 2025-09-20, Merge [#4504](https://github.com/ossrs/srs/pull/4504): fix rtsp compiling warning. v7.0.91 (#4504)
* v7.0, 2025-09-19, Merge [#4503](https://github.com/ossrs/srs/pull/4503): AI: Refine RTMP/SRT/RTC bridge. v7.0.90 (#4503)
* v7.0, 2025-09-15, RTC2RTMP: Fix sequence number wraparound assertion crashes. v7.0.89 (#4491)
@@ -105,6 +106,7 @@ The changelog for SRS.
## SRS 6.0 Changelog
+* v6.0, 2025-09-21, Fix WHIP with transcoding bug. v6.0.179 (#4495)
* v6.0, 2025-09-15, RTC2RTMP: Fix sequence number wraparound assertion crashes. v6.0.177 (#4491)
* v6.0, 2025-09-05, RTX: Fix race condition for timer. v6.0.176 (#4470) (#4474)
* v6.0, 2025-08-26, Merge [#4451](https://github.com/ossrs/srs/pull/4451): RTC: Fix null pointer crash in RTC2RTMP when start packet is missing. v6.0.175 (#4451)
diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp
index 50e361cb8..5e9cd561a 100644
--- a/trunk/src/app/srs_app_rtc_conn.cpp
+++ b/trunk/src/app/srs_app_rtc_conn.cpp
@@ -1869,6 +1869,9 @@ SrsRtcConnection::~SrsRtcConnection()
srs_freep(req_);
srs_freep(pli_epp_);
+
+ // Optional to release the publisher token.
+ publish_token_ = NULL;
}
void SrsRtcConnection::on_before_dispose(ISrsResource *c)
@@ -1931,6 +1934,11 @@ string SrsRtcConnection::token()
return token_;
}
+void SrsRtcConnection::set_publish_token(SrsSharedPtr publish_token)
+{
+ publish_token_ = publish_token;
+}
+
ISrsKbpsDelta *SrsRtcConnection::delta()
{
return networks_->delta();
diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp
index 1423fa625..e30ac76fa 100644
--- a/trunk/src/app/srs_app_rtc_conn.hpp
+++ b/trunk/src/app/srs_app_rtc_conn.hpp
@@ -55,6 +55,7 @@ class SrsRtcNetworks;
class SrsRtcUdpNetwork;
class ISrsRtcNetwork;
class SrsRtcTcpNetwork;
+class SrsStreamPublishToken;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
@@ -529,6 +530,7 @@ private:
ISrsRequest *req_;
SrsSdp remote_sdp_;
SrsSdp local_sdp_;
+ SrsSharedPtr publish_token_;
private:
// twcc handler
@@ -561,6 +563,8 @@ public:
std::string username();
// Get the token for verify this session, for example, when delete session by WHIP API.
std::string token();
+ // Set the publish token for this session if publisher.
+ void set_publish_token(SrsSharedPtr publish_token);
public:
virtual ISrsKbpsDelta *delta();
diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp
index 6937d1fd7..f998f41e5 100644
--- a/trunk/src/app/srs_app_rtc_server.cpp
+++ b/trunk/src/app/srs_app_rtc_server.cpp
@@ -348,7 +348,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
if (ruc->publish_ && (err = _srs_stream_publish_tokens->acquire_token(req, publish_token_raw)) != srs_success) {
return srs_error_wrap(err, "acquire stream publish token");
}
- SrsUniquePtr publish_token(publish_token_raw);
+ SrsSharedPtr publish_token(publish_token_raw);
if (publish_token.get()) {
srs_trace("stream publish token acquired, type=rtc, url=%s", req->get_stream_url().c_str());
}
@@ -370,6 +370,11 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
return srs_error_wrap(err, "create session");
}
+ // Update publish token for publisher.
+ if (ruc->publish_) {
+ session->set_publish_token(publish_token);
+ }
+
*psession = session;
return err;
diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp
index b84fee9e8..b89064db4 100644
--- a/trunk/src/app/srs_app_rtc_source.cpp
+++ b/trunk/src/app/srs_app_rtc_source.cpp
@@ -715,9 +715,6 @@ void SrsRtcSource::on_unpublish()
srs_trace("cleanup when unpublish, created=%u, deliver=%u", is_created_, is_delivering_packets_);
- is_created_ = false;
- is_delivering_packets_ = false;
-
if (!_source_id.empty()) {
_pre_source_id = _source_id;
}
@@ -744,16 +741,20 @@ void SrsRtcSource::on_unpublish()
if (consumers_.empty()) {
stream_die_at_ = srs_time_now_cached();
}
+
+ // Should never change the final state before all cleanup is done.
+ is_created_ = false;
+ is_delivering_packets_ = false;
}
-void SrsRtcSource::subscribe(ISrsRtcSourceEventHandler *h)
+void SrsRtcSource::rtc_source_subscribe(ISrsRtcSourceEventHandler *h)
{
if (std::find(event_handlers_.begin(), event_handlers_.end(), h) == event_handlers_.end()) {
event_handlers_.push_back(h);
}
}
-void SrsRtcSource::unsubscribe(ISrsRtcSourceEventHandler *h)
+void SrsRtcSource::rtc_source_unsubscribe(ISrsRtcSourceEventHandler *h)
{
std::vector::iterator it;
it = std::find(event_handlers_.begin(), event_handlers_.end(), h);
diff --git a/trunk/src/app/srs_app_rtc_source.hpp b/trunk/src/app/srs_app_rtc_source.hpp
index 1adcc5b18..609ce17c8 100644
--- a/trunk/src/app/srs_app_rtc_source.hpp
+++ b/trunk/src/app/srs_app_rtc_source.hpp
@@ -310,8 +310,8 @@ public:
public:
// For event handler
- virtual void subscribe(ISrsRtcSourceEventHandler *h);
- virtual void unsubscribe(ISrsRtcSourceEventHandler *h);
+ virtual void rtc_source_subscribe(ISrsRtcSourceEventHandler *h);
+ virtual void rtc_source_unsubscribe(ISrsRtcSourceEventHandler *h);
public:
// Get and set the publisher, passed to consumer to process requests such as PLI.
diff --git a/trunk/src/app/srs_app_rtsp_source.cpp b/trunk/src/app/srs_app_rtsp_source.cpp
index 01bf75578..8aee27514 100644
--- a/trunk/src/app/srs_app_rtsp_source.cpp
+++ b/trunk/src/app/srs_app_rtsp_source.cpp
@@ -444,9 +444,6 @@ void SrsRtspSource::on_unpublish()
srs_trace("cleanup when unpublish, created=%u, deliver=%u", is_created_, is_delivering_packets_);
- is_created_ = false;
- is_delivering_packets_ = false;
-
if (!_source_id.empty()) {
_pre_source_id = _source_id;
}
@@ -459,6 +456,10 @@ void SrsRtspSource::on_unpublish()
if (consumers_.empty()) {
stream_die_at_ = srs_time_now_cached();
}
+
+ // Should never change the final state before all cleanup is done.
+ is_created_ = false;
+ is_delivering_packets_ = false;
}
srs_error_t SrsRtspSource::on_rtp(SrsRtpPacket *pkt)
diff --git a/trunk/src/app/srs_app_srt_source.cpp b/trunk/src/app/srs_app_srt_source.cpp
index 5a8e2f00e..2e5dd2b86 100644
--- a/trunk/src/app/srs_app_srt_source.cpp
+++ b/trunk/src/app/srs_app_srt_source.cpp
@@ -1085,8 +1085,6 @@ void SrsSrtSource::on_unpublish()
return;
}
- can_publish_ = true;
-
SrsStatistic *stat = SrsStatistic::instance();
stat->on_stream_close(req_);
@@ -1099,6 +1097,9 @@ void SrsSrtSource::on_unpublish()
if (consumers_.empty()) {
stream_die_at_ = srs_time_now_cached();
}
+
+ // Should never change the final state before all cleanup is done.
+ can_publish_ = true;
}
srs_error_t SrsSrtSource::on_packet(SrsSrtPacket *packet)
diff --git a/trunk/src/app/srs_app_st.cpp b/trunk/src/app/srs_app_st.cpp
index bc9695fb5..2fa3a8f3c 100644
--- a/trunk/src/app/srs_app_st.cpp
+++ b/trunk/src/app/srs_app_st.cpp
@@ -185,6 +185,7 @@ void SrsFastCoroutine::stop()
}
disposed_ = true;
stopping_ = true;
+ stopping_cid_ = _srs_context->get_id();
interrupt();
diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp
index a47cbcd67..2152ad512 100644
--- a/trunk/src/core/srs_core_version6.hpp
+++ b/trunk/src/core/srs_core_version6.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
-#define VERSION_REVISION 177
+#define VERSION_REVISION 179
#endif
diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp
index 13c13f3d6..1ad45ef8e 100644
--- a/trunk/src/core/srs_core_version7.hpp
+++ b/trunk/src/core/srs_core_version7.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
-#define VERSION_REVISION 91
+#define VERSION_REVISION 92
#endif
\ No newline at end of file
diff --git a/trunk/src/utest/srs_utest_app2.cpp b/trunk/src/utest/srs_utest_app2.cpp
index 661120778..41bcef5bf 100644
--- a/trunk/src/utest/srs_utest_app2.cpp
+++ b/trunk/src/utest/srs_utest_app2.cpp
@@ -1575,8 +1575,8 @@ VOID TEST(AppTest2, RtcSourceOnConsumerDestroyNotifyEventHandlers)
// Set up source with publish stream and event handlers
source->set_publish_stream(publish_stream);
- source->subscribe(handler1);
- source->subscribe(handler2);
+ source->rtc_source_subscribe(handler1);
+ source->rtc_source_subscribe(handler2);
// Create mock consumers
MockRtcConsumer *consumer1 = new MockRtcConsumer();
@@ -1625,7 +1625,7 @@ VOID TEST(AppTest2, RtcSourceOnConsumerDestroyNoPublishStream)
// Create mock event handler
MockRtcSourceEventHandler *handler = new MockRtcSourceEventHandler();
- source->subscribe(handler);
+ source->rtc_source_subscribe(handler);
// Create mock consumer
MockRtcConsumer *consumer = new MockRtcConsumer();
@@ -1961,12 +1961,12 @@ VOID TEST(AppTest2, RtcSourceSubscribeBasic)
EXPECT_EQ(0, (int)source->event_handlers_.size());
// Subscribe first handler
- source->subscribe(handler1.get());
+ source->rtc_source_subscribe(handler1.get());
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
// Subscribe second handler
- source->subscribe(handler2.get());
+ source->rtc_source_subscribe(handler2.get());
EXPECT_EQ(2, (int)source->event_handlers_.size());
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
EXPECT_EQ(handler2.get(), source->event_handlers_[1]);
@@ -1991,18 +1991,18 @@ VOID TEST(AppTest2, RtcSourceSubscribeDuplicateHandler)
SrsUniquePtr handler(new MockRtcSourceEventHandler());
// Subscribe handler first time
- source->subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
// Subscribe same handler again - should not add duplicate
- source->subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
// Subscribe same handler multiple times - should still be only one
- source->subscribe(handler.get());
- source->subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(handler.get(), source->event_handlers_[0]);
}
@@ -2026,12 +2026,12 @@ VOID TEST(AppTest2, RtcSourceSubscribeNullHandler)
EXPECT_EQ(0, (int)source->event_handlers_.size());
// Subscribe null handler - should add it (implementation allows null)
- source->subscribe(NULL);
+ source->rtc_source_subscribe(NULL);
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(NULL, source->event_handlers_[0]);
// Subscribe null handler again - should not add duplicate
- source->subscribe(NULL);
+ source->rtc_source_subscribe(NULL);
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(NULL, source->event_handlers_[0]);
}
@@ -2060,9 +2060,9 @@ VOID TEST(AppTest2, RtcSourceSubscribeMultipleHandlers)
EXPECT_EQ(0, (int)source->event_handlers_.size());
// Subscribe handlers in order
- source->subscribe(handler1.get());
- source->subscribe(handler2.get());
- source->subscribe(handler3.get());
+ source->rtc_source_subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
+ source->rtc_source_subscribe(handler3.get());
// Verify all handlers are subscribed in correct order
EXPECT_EQ(3, (int)source->event_handlers_.size());
@@ -2071,8 +2071,8 @@ VOID TEST(AppTest2, RtcSourceSubscribeMultipleHandlers)
EXPECT_EQ(handler3.get(), source->event_handlers_[2]);
// Try to subscribe duplicates - should not change the list
- source->subscribe(handler2.get());
- source->subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
+ source->rtc_source_subscribe(handler1.get());
EXPECT_EQ(3, (int)source->event_handlers_.size());
EXPECT_EQ(handler1.get(), source->event_handlers_[0]);
EXPECT_EQ(handler2.get(), source->event_handlers_[1]);
@@ -2099,29 +2099,29 @@ VOID TEST(AppTest2, RtcSourceSubscribeUnsubscribeInteraction)
SrsUniquePtr handler2(new MockRtcSourceEventHandler());
// Subscribe both handlers
- source->subscribe(handler1.get());
- source->subscribe(handler2.get());
+ source->rtc_source_subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
EXPECT_EQ(2, (int)source->event_handlers_.size());
// Unsubscribe first handler
- source->unsubscribe(handler1.get());
+ source->rtc_source_unsubscribe(handler1.get());
EXPECT_EQ(1, (int)source->event_handlers_.size());
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
// Re-subscribe first handler - should be added back
- source->subscribe(handler1.get());
+ source->rtc_source_subscribe(handler1.get());
EXPECT_EQ(2, (int)source->event_handlers_.size());
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
EXPECT_EQ(handler1.get(), source->event_handlers_[1]);
// Unsubscribe all handlers
- source->unsubscribe(handler1.get());
- source->unsubscribe(handler2.get());
+ source->rtc_source_unsubscribe(handler1.get());
+ source->rtc_source_unsubscribe(handler2.get());
EXPECT_EQ(0, (int)source->event_handlers_.size());
// Re-subscribe in different order
- source->subscribe(handler2.get());
- source->subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
+ source->rtc_source_subscribe(handler1.get());
EXPECT_EQ(2, (int)source->event_handlers_.size());
EXPECT_EQ(handler2.get(), source->event_handlers_[0]);
EXPECT_EQ(handler1.get(), source->event_handlers_[1]);
@@ -2147,8 +2147,8 @@ VOID TEST(AppTest2, RtcSourceSubscribeEventNotification)
SrsUniquePtr handler2(new MockRtcSourceEventHandler());
// Subscribe handlers
- source->subscribe(handler1.get());
- source->subscribe(handler2.get());
+ source->rtc_source_subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
// Verify initial state
EXPECT_EQ(0, handler1->on_unpublish_count_);
@@ -2174,11 +2174,11 @@ VOID TEST(AppTest2, RtcSourceSubscribeEventNotification)
handler2->on_unpublish_count_ = 0;
// Subscribe both handlers to the new source
- source2->subscribe(handler1.get());
- source2->subscribe(handler2.get());
+ source2->rtc_source_subscribe(handler1.get());
+ source2->rtc_source_subscribe(handler2.get());
// Unsubscribe handler1
- source2->unsubscribe(handler1.get());
+ source2->rtc_source_unsubscribe(handler1.get());
// Verify only handler2 is subscribed now
EXPECT_EQ(1, (int)source2->event_handlers_.size());
@@ -2255,7 +2255,7 @@ VOID TEST(AppTest2, RtcSourcePublishStreamWithConsumerDestroy)
// Create a mock event handler
SrsUniquePtr handler(new MockRtcSourceEventHandler());
- source->subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
// Create consumers
ISrsRtcConsumer *consumer1 = NULL;
@@ -3204,8 +3204,8 @@ VOID TEST(AppTest2, RtcSourceSetStreamCreatedWithEventHandlers)
// Create and subscribe event handlers
SrsUniquePtr handler1(new MockRtcSourceEventHandler());
SrsUniquePtr handler2(new MockRtcSourceEventHandler());
- source->subscribe(handler1.get());
- source->subscribe(handler2.get());
+ source->rtc_source_subscribe(handler1.get());
+ source->rtc_source_subscribe(handler2.get());
// Verify initial state
EXPECT_FALSE(source->is_created_);
@@ -3242,7 +3242,7 @@ VOID TEST(AppTest2, RtcSourcePublishStreamWithoutConsumerDestroy)
// Create a mock event handler
SrsUniquePtr handler(new MockRtcSourceEventHandler());
- source->subscribe(handler.get());
+ source->rtc_source_subscribe(handler.get());
// Create and destroy consumer
ISrsRtcConsumer *consumer = NULL;