|
8 | 8 |
|
9 | 9 | #include <srs_app_circuit_breaker.hpp> |
10 | 10 | #include <srs_app_rtc_source.hpp> |
| 11 | +#include <srs_app_rtmp_source.hpp> |
| 12 | +#include <srs_app_srt_source.hpp> |
| 13 | +#ifdef SRS_RTSP |
| 14 | +#include <srs_app_rtsp_source.hpp> |
| 15 | +#endif |
11 | 16 | #include <srs_core_autofree.hpp> |
12 | 17 | #include <srs_kernel_buffer.hpp> |
13 | 18 | #include <srs_kernel_codec.hpp> |
@@ -1790,7 +1795,6 @@ VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateInitializeFailure) |
1790 | 1795 | mock_source->set_initialize_error(initialize_error_); |
1791 | 1796 | } |
1792 | 1797 | SrsSharedPtr<SrsRtcSource> source = SrsSharedPtr<SrsRtcSource>(mock_source); |
1793 | | - srs_trace("new rtc source, stream_url=%s", stream_url.c_str()); |
1794 | 1798 | pps = source; |
1795 | 1799 |
|
1796 | 1800 | pool_[stream_url] = source; |
@@ -1870,7 +1874,6 @@ VOID TEST(AppTest2, RtcSourceManagerFetchOrCreateErrorWrapping) |
1870 | 1874 | srs_freep(init_error); |
1871 | 1875 |
|
1872 | 1876 | SrsSharedPtr<SrsRtcSource> source = SrsSharedPtr<SrsRtcSource>(mock_source); |
1873 | | - srs_trace("new rtc source, stream_url=%s", stream_url.c_str()); |
1874 | 1877 | pps = source; |
1875 | 1878 |
|
1876 | 1879 | pool_[stream_url] = source; |
@@ -2176,14 +2179,16 @@ VOID TEST(AppTest2, RtcSourceOnConsumerDestroyStreamDeath) |
2176 | 2179 |
|
2177 | 2180 | // Verify initial state - stream is not created (no publisher) |
2178 | 2181 | EXPECT_FALSE(source->is_created_); |
2179 | | - EXPECT_EQ(0, source->stream_die_at_); |
| 2182 | + // After fix #4449: stream_die_at_ is initialized to current time, not 0 |
| 2183 | + srs_utime_t initial_die_at = source->stream_die_at_; |
| 2184 | + EXPECT_GT(initial_die_at, 0); |
2180 | 2185 |
|
2181 | | - // Remove consumer when stream is not created - should set stream_die_at_ |
| 2186 | + // Remove consumer when stream is not created - should update stream_die_at_ |
2182 | 2187 | srs_utime_t before_time = srs_time_now_cached(); |
2183 | 2188 | source->on_consumer_destroy(consumer); |
2184 | 2189 | srs_utime_t after_time = srs_time_now_cached(); |
2185 | 2190 |
|
2186 | | - // Verify stream death time was set |
| 2191 | + // Verify stream death time was updated to current time |
2187 | 2192 | EXPECT_TRUE(source->stream_die_at_ >= before_time); |
2188 | 2193 | EXPECT_TRUE(source->stream_die_at_ <= after_time); |
2189 | 2194 |
|
@@ -2215,13 +2220,15 @@ VOID TEST(AppTest2, RtcSourceOnConsumerDestroyStreamAlive) |
2215 | 2220 |
|
2216 | 2221 | // Verify initial state |
2217 | 2222 | EXPECT_TRUE(source->is_created_); |
2218 | | - EXPECT_EQ(0, source->stream_die_at_); |
| 2223 | + // After fix #4449: stream_die_at_ is initialized to current time, not 0 |
| 2224 | + srs_utime_t initial_die_at = source->stream_die_at_; |
| 2225 | + EXPECT_GT(initial_die_at, 0); |
2219 | 2226 |
|
2220 | | - // Remove consumer when stream is created - should NOT set stream_die_at_ |
| 2227 | + // Remove consumer when stream is created - should NOT update stream_die_at_ |
2221 | 2228 | source->on_consumer_destroy(consumer); |
2222 | 2229 |
|
2223 | | - // Verify stream death time was NOT set |
2224 | | - EXPECT_EQ(0, source->stream_die_at_); |
| 2230 | + // Verify stream death time was NOT changed (still has initial value) |
| 2231 | + EXPECT_EQ(initial_die_at, source->stream_die_at_); |
2225 | 2232 |
|
2226 | 2233 | // Clean up |
2227 | 2234 | srs_freep(consumer); |
@@ -4598,3 +4605,144 @@ VOID TEST(AppTest2, RtcSourceGetTrackDescMultipleMatchingVideoTracks) |
4598 | 4605 | EXPECT_EQ("video-h264-track-2", all_video_tracks[1]->id_); |
4599 | 4606 | EXPECT_EQ("video-h265-track", all_video_tracks[2]->id_); |
4600 | 4607 | } |
| 4608 | + |
| 4609 | +// Reproduce issue 4449: Newly created source is immediately considered dead |
| 4610 | +// When a new source is created with stream_die_at_=0, if notify() timer fires |
| 4611 | +// before a publisher connects, the source gets deleted because stream_is_dead() |
| 4612 | +// returns true. This causes "new live source, dead=1" in logs. |
| 4613 | +VOID TEST(ReproduceIssue4449, RtmpLiveSourceNotifyDeletesNewlyCreatedSource) |
| 4614 | +{ |
| 4615 | + srs_error_t err; |
| 4616 | + |
| 4617 | + // Create a source manager |
| 4618 | + SrsUniquePtr<SrsLiveSourceManager> manager(new SrsLiveSourceManager()); |
| 4619 | + HELPER_EXPECT_SUCCESS(manager->initialize()); |
| 4620 | + |
| 4621 | + // Create a mock request |
| 4622 | + SrsUniquePtr<SrsRequest> req(new SrsRequest()); |
| 4623 | + req->host_ = "localhost"; |
| 4624 | + req->vhost_ = "test.vhost"; |
| 4625 | + req->app_ = "live"; |
| 4626 | + req->stream_ = "thegobot"; |
| 4627 | + |
| 4628 | + // Fetch or create source (this creates a new source) |
| 4629 | + SrsSharedPtr<SrsLiveSource> source; |
| 4630 | + HELPER_EXPECT_SUCCESS(manager->fetch_or_create(req.get(), source)); |
| 4631 | + |
| 4632 | + // After fix: newly created source should NOT be dead |
| 4633 | + EXPECT_FALSE(source->stream_is_dead()); |
| 4634 | + EXPECT_EQ(1, (int)manager->pool_.size()); |
| 4635 | + |
| 4636 | + // Simulate timer firing - call notify() |
| 4637 | + int pool_size_before = (int)manager->pool_.size(); |
| 4638 | + HELPER_EXPECT_SUCCESS(manager->notify(0, 0, 0)); |
| 4639 | + int pool_size_after = (int)manager->pool_.size(); |
| 4640 | + |
| 4641 | + // After fix: the newly created source should NOT be deleted by notify |
| 4642 | + EXPECT_EQ(pool_size_before, pool_size_after); |
| 4643 | + EXPECT_EQ(1, pool_size_after); |
| 4644 | +} |
| 4645 | + |
| 4646 | +// Test SRT source for the same issue |
| 4647 | +VOID TEST(ReproduceIssue4449, SrtSourceNotifyDeletesNewlyCreatedSource) |
| 4648 | +{ |
| 4649 | + srs_error_t err; |
| 4650 | + |
| 4651 | + // Create a SRT source manager |
| 4652 | + SrsUniquePtr<SrsSrtSourceManager> manager(new SrsSrtSourceManager()); |
| 4653 | + HELPER_EXPECT_SUCCESS(manager->initialize()); |
| 4654 | + |
| 4655 | + // Create a mock request |
| 4656 | + SrsUniquePtr<SrsRequest> req(new SrsRequest()); |
| 4657 | + req->host_ = "localhost"; |
| 4658 | + req->vhost_ = "test.vhost"; |
| 4659 | + req->app_ = "live"; |
| 4660 | + req->stream_ = "thegobot"; |
| 4661 | + |
| 4662 | + // Fetch or create source (this creates a new source) |
| 4663 | + SrsSharedPtr<SrsSrtSource> source; |
| 4664 | + HELPER_EXPECT_SUCCESS(manager->fetch_or_create(req.get(), source)); |
| 4665 | + |
| 4666 | + // After fix: newly created source should NOT be dead |
| 4667 | + EXPECT_FALSE(source->stream_is_dead()); |
| 4668 | + EXPECT_EQ(1, (int)manager->pool_.size()); |
| 4669 | + |
| 4670 | + // Simulate timer firing - call notify() |
| 4671 | + int pool_size_before = (int)manager->pool_.size(); |
| 4672 | + HELPER_EXPECT_SUCCESS(manager->notify(0, 0, 0)); |
| 4673 | + int pool_size_after = (int)manager->pool_.size(); |
| 4674 | + |
| 4675 | + // After fix: the newly created source should NOT be deleted by notify |
| 4676 | + EXPECT_EQ(pool_size_before, pool_size_after); |
| 4677 | + EXPECT_EQ(1, pool_size_after); |
| 4678 | +} |
| 4679 | + |
| 4680 | +// Test RTC source for the same issue |
| 4681 | +VOID TEST(ReproduceIssue4449, RtcSourceNotifyDeletesNewlyCreatedSource) |
| 4682 | +{ |
| 4683 | + srs_error_t err; |
| 4684 | + |
| 4685 | + // Create a RTC source manager |
| 4686 | + SrsUniquePtr<SrsRtcSourceManager> manager(new SrsRtcSourceManager()); |
| 4687 | + HELPER_EXPECT_SUCCESS(manager->initialize()); |
| 4688 | + |
| 4689 | + // Create a mock request |
| 4690 | + SrsUniquePtr<SrsRequest> req(new SrsRequest()); |
| 4691 | + req->host_ = "localhost"; |
| 4692 | + req->vhost_ = "test.vhost"; |
| 4693 | + req->app_ = "live"; |
| 4694 | + req->stream_ = "thegobot"; |
| 4695 | + |
| 4696 | + // Fetch or create source (this creates a new source) |
| 4697 | + SrsSharedPtr<SrsRtcSource> source; |
| 4698 | + HELPER_EXPECT_SUCCESS(manager->fetch_or_create(req.get(), source)); |
| 4699 | + |
| 4700 | + // After fix: newly created source should NOT be dead |
| 4701 | + EXPECT_FALSE(source->stream_is_dead()); |
| 4702 | + EXPECT_EQ(1, (int)manager->pool_.size()); |
| 4703 | + |
| 4704 | + // Simulate timer firing - call notify() |
| 4705 | + int pool_size_before = (int)manager->pool_.size(); |
| 4706 | + HELPER_EXPECT_SUCCESS(manager->notify(0, 0, 0)); |
| 4707 | + int pool_size_after = (int)manager->pool_.size(); |
| 4708 | + |
| 4709 | + // After fix: the newly created source should NOT be deleted by notify |
| 4710 | + EXPECT_EQ(pool_size_before, pool_size_after); |
| 4711 | + EXPECT_EQ(1, pool_size_after); |
| 4712 | +} |
| 4713 | + |
| 4714 | +#ifdef SRS_RTSP |
| 4715 | +// Test RTSP source for the same issue |
| 4716 | +VOID TEST(ReproduceIssue4449, RtspSourceNotifyDeletesNewlyCreatedSource) |
| 4717 | +{ |
| 4718 | + srs_error_t err; |
| 4719 | + |
| 4720 | + // Create a RTSP source manager |
| 4721 | + SrsUniquePtr<SrsRtspSourceManager> manager(new SrsRtspSourceManager()); |
| 4722 | + HELPER_EXPECT_SUCCESS(manager->initialize()); |
| 4723 | + |
| 4724 | + // Create a mock request |
| 4725 | + SrsUniquePtr<SrsRequest> req(new SrsRequest()); |
| 4726 | + req->host_ = "localhost"; |
| 4727 | + req->vhost_ = "test.vhost"; |
| 4728 | + req->app_ = "live"; |
| 4729 | + req->stream_ = "thegobot"; |
| 4730 | + |
| 4731 | + // Fetch or create source (this creates a new source) |
| 4732 | + SrsSharedPtr<SrsRtspSource> source; |
| 4733 | + HELPER_EXPECT_SUCCESS(manager->fetch_or_create(req.get(), source)); |
| 4734 | + |
| 4735 | + // After fix: newly created source should NOT be dead |
| 4736 | + EXPECT_FALSE(source->stream_is_dead()); |
| 4737 | + EXPECT_EQ(1, (int)manager->pool_.size()); |
| 4738 | + |
| 4739 | + // Simulate timer firing - call notify() |
| 4740 | + int pool_size_before = (int)manager->pool_.size(); |
| 4741 | + HELPER_EXPECT_SUCCESS(manager->notify(0, 0, 0)); |
| 4742 | + int pool_size_after = (int)manager->pool_.size(); |
| 4743 | + |
| 4744 | + // After fix: the newly created source should NOT be deleted by notify |
| 4745 | + EXPECT_EQ(pool_size_before, pool_size_after); |
| 4746 | + EXPECT_EQ(1, pool_size_after); |
| 4747 | +} |
| 4748 | +#endif |
0 commit comments