fix crash issue caused by reload configuration file. v7.0.98 (#4530)

fix crash issue caused by reload configuration file, which occurs when a
vhost is added/removed in the new configuration.

Introduced by https://github.com/ossrs/srs/pull/4458

see https://github.com/ossrs/srs/issues/4529
This commit is contained in:
Haibo Chen(陈海博) 2025-10-16 19:30:16 +08:00 committed by GitHub
parent 6f526284a3
commit abaffdd4b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 973 additions and 23 deletions

View File

@ -2,7 +2,7 @@
############################################################
# build
############################################################
FROM registry.cn-hangzhou.aliyuncs.com/ossrs/srs:dev AS build
FROM ossrs/srs:ubuntu20 AS build
COPY . /tmp/signaling
RUN cd /tmp/signaling && make
@ -12,7 +12,7 @@ RUN cp -R /tmp/signaling/www /usr/local/
############################################################
# dist
############################################################
FROM centos:7 AS dist
FROM ubuntu:focal AS dist
# HTTP/1989
EXPOSE 1989

View File

@ -136,6 +136,15 @@ the HLS, all edge server never output HLS util client access the RTMP stream on
That is, never config HLS on edge server, it's no use. The HLS delivery must use squid or
traffic server to cache the HTTP origin server.
## WebRTC Edge
Currently edge cluster only support RTMP and HTTP-FLV, doesn't support WebRTC. A new version
of Edge Cluster is planning, see [#4402](https://github.com/ossrs/srs/discussions/4402) for details.
Note that you can use new version of [Origin Cluster](./origin-cluster.md) for WebRTC, as it
can also used to expand the capacity of streams. However, if you need to expand the capacity
of viewers for one stream, you still need the new version of Edge Cluster.
## Transform Vhost
The design of CDN stream system, always use `up.xxxx` and `down.xxxx` to operate them, for example, user publish to cdn by host `up.srs.com` and play by `down.srs.com`.

View File

@ -209,5 +209,54 @@ Why use HTTP FLV? HTTP FLV streaming is becoming more popular. The main advantag
5. Universality: Flash can play both RTMP and HTTP FLV. Custom apps and mainstream players also support HTTP FLV playback.
6. Simplicity: FLV is the simplest streaming media encapsulation, and HTTP is the most widely used protocol. Combining these two makes maintenance much easier than RTMP.
## IPv6
SRS (v7.0.67+) supports IPv6 for HTTP-FLV streaming, enabling dual-stack (IPv4/IPv6) operation for low-latency live streaming. This allows HTTP-FLV clients to access streams using IPv6 addresses while maintaining full compatibility with existing IPv4 infrastructure.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the HTTP server configuration. Configure the HTTP server to listen on IPv6 addresses:
```bash
http_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8080 [::]:8080;
dir ./objs/nginx/html;
}
```
Access HTTP-FLV streams via IPv6:
```bash
# HTTP-FLV stream via IPv6
http://[::1]:8080/live/livestream.flv
```
Play HTTP-FLV stream via IPv6 using FFplay:
```bash
ffplay 'http://[::1]:8080/live/livestream.flv'
```
SRS supports dual-stack HTTP-FLV operation, allowing both IPv4 and IPv6 clients to access streams simultaneously:
```bash
http_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8080 [::]:8080;
dir ./objs/nginx/html;
}
vhost __defaultVhost__ {
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}
```
This configuration allows:
- IPv4 clients: `http://192.168.1.100:8080/live/livestream.flv`
- IPv6 clients: `http://[2001:db8::1]:8080/live/livestream.flv`
![](https://ossrs.io/gif/v1/sls.gif?site=ossrs.net&path=/lts/doc/en/v7/flv)

View File

@ -587,5 +587,52 @@ The generated m3u8 playlist will reference fMP4 segments instead of TS segments,
Play the stream by SRS player: [http://localhost:8080/live/livestream.m3u8](http://localhost:8080/players/srs_player.html?stream=livestream.m3u8)
## IPv6
SRS (v7.0.67+) supports IPv6 for HLS streaming, enabling dual-stack (IPv4/IPv6) operation for HTTP-based live streaming. This allows HLS clients to access streams using IPv6 addresses while maintaining full compatibility with existing IPv4 infrastructure.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the HTTP server configuration. Configure the HTTP server to listen on IPv6 addresses:
```bash
http_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8080 [::]:8080;
dir ./objs/nginx/html;
}
```
Access HLS streams via IPv6:
```bash
# HLS stream via IPv6
http://[::1]:8080/live/livestream.m3u8
# HLS segments are also accessible via IPv6
http://[::1]:8080/live/livestream-1.ts
http://[::1]:8080/live/livestream-2.ts
```
Play HLS stream via IPv6 using FFplay:
```bash
ffplay 'http://[::1]:8080/live/livestream.m3u8'
```
SRS supports dual-stack HLS operation, allowing both IPv4 and IPv6 clients to access streams simultaneously:
```bash
http_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8080 [::]:8080;
dir ./objs/nginx/html;
}
```
This configuration allows:
- IPv4 clients: `http://192.168.1.100:8080/live/livestream.m3u8`
- IPv6 clients: `http://[2001:db8::1]:8080/live/livestream.m3u8`
![](https://ossrs.io/gif/v1/sls.gif?site=ossrs.io&path=/lts/doc/en/v7/hls)

View File

@ -341,6 +341,62 @@ openssl req -new -x509 -key server.key -out server.crt -days 3650 \
For production environments, use certificates from a trusted Certificate Authority (CA) or Let's Encrypt.
## IPv6
SRS (v7.0.67+) supports IPv6 for RTMP and RTMPS protocols, enabling dual-stack (IPv4/IPv6) operation. This allows clients to connect using IPv6 addresses while maintaining full compatibility with existing IPv4 infrastructure.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the configuration. You can configure SRS to listen on IPv6 addresses by specifying them in the `listen` directive:
```bash
rtmp {
# Listen on both IPv4 and IPv6
listen 1935 [::]:1935;
}
# For RTMPS
rtmps {
enabled on;
listen 1443 [::]:1443;
key ./conf/server.key;
cert ./conf/server.crt;
}
```
Publish RTMP stream via IPv6:
```bash
ffmpeg -re -i ./doc/source.flv -c copy -f flv 'rtmp://[::1]:1935/live/livestream'
```
Publish RTMPS stream via IPv6:
```bash
ffmpeg -re -i ./doc/source.flv -c copy -f flv 'rtmps://[::1]:1443/live/livestream'
```
Play RTMP stream via IPv6:
```bash
ffplay 'rtmp://[::1]:1935/live/livestream'
```
Play RTMPS stream via IPv6:
```bash
ffplay 'rtmps://[::1]:1443/live/livestream'
```
SRS supports dual-stack operation, allowing both IPv4 and IPv6 clients to connect simultaneously:
```bash
# Listen on both IPv4 and IPv6 addresses
listen 1935 [::]:1935;
# This allows connections from:
# - IPv4 clients: rtmp://192.168.1.100:1935/live/stream
# - IPv6 clients: rtmp://[2001:db8::1]:1935/live/stream
```
## On Demand Live Streaming
In some situations, you might want to start streaming only when someone starts watching:

View File

@ -131,6 +131,66 @@ go test ./blackbox -mod=vendor -v -count=1 -run=TestFast_RtmpPublish_RtspPlay_Ba
The current version implements only basic functionality. Additional features like authentication, redirection, and RTCP will be planned according to actual needs, possibly in the near future.
## IPv6
SRS (v7.0.67+) supports IPv6 for RTSP protocol, enabling dual-stack (IPv4/IPv6) operation for standards-based streaming. This allows RTSP clients to connect using IPv6 addresses while maintaining full compatibility with existing IPv4 infrastructure.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the configuration. Configure the RTSP server to listen on IPv6 addresses:
```bash
rtsp_server {
enabled on;
# Listen on both IPv4 and IPv6 for UDP media
listen 8554 [::]:8554;
}
vhost __defaultVhost__ {
rtsp {
enabled on;
rtmp_to_rtsp on;
}
}
```
Play RTSP stream via IPv6 using FFplay (TCP transport only):
```bash
ffplay -rtsp_transport tcp -i 'rtsp://[::1]:8554/live/livestream'
```
Play RTSP stream via IPv6 using VLC:
```bash
vlc 'rtsp://[::1]:8554/live/livestream'
```
When using IPv6 addresses in RTSP URLs, the IPv6 address must be enclosed in square brackets:
```bash
# Local IPv6 loopback
rtsp://[::1]:8554/live/livestream
# Public IPv6 address
rtsp://[2001:db8::1]:8554/live/livestream
# With authentication (if implemented)
rtsp://user:pass@[2001:db8::1]:8554/live/livestream
```
SRS supports dual-stack RTSP operation, allowing both IPv4 and IPv6 clients to connect simultaneously:
```bash
rtsp_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8554 [::]:8554;
}
```
This configuration allows:
- IPv4 clients: `rtsp://192.168.1.100:8554/live/livestream`
- IPv6 clients: `rtsp://[2001:db8::1]:8554/live/livestream`
## References
- [rfc2326-1998-rtsp.pdf](/files/rfc2326-1998-rtsp.pdf)

View File

@ -43,7 +43,8 @@ Open the following page to play the stream (if SRS is not on your local machine,
* RTMP(VLC/ffplay): `rtmp://localhost/live/livestream`
* HLS by SRS player: [http://localhost:8080/live/livestream.flv](http://localhost:8080/players/srs_player.html)
* SRT(VLC/ffplay): `srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=request`
* SRT(ffplay): `srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=request`
* SRT(VLC): `srt://127.0.0.1:10080`
SRS supports converting SRT to other protocols, which will be described in detail below.
@ -381,6 +382,121 @@ How does SRS implement SRT? Based on coroutine-based SRT architecture, we need t
> Note: Please note that the SRT in SRS 4.0 is a non-ST architecture, and it is implemented by launching a separate thread, which may not have the same level of maintainability as the native ST coroutine architecture.
## IPv6
SRS (v7.0.67+) supports IPv6 for SRT protocol, enabling dual-stack (IPv4/IPv6) operation for low-latency streaming. This allows SRT clients to connect using IPv6 addresses while maintaining full compatibility with existing IPv4 infrastructure.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the configuration. Configure the SRT server to listen on IPv6 addresses:
```bash
srt_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 10080 [::]:10080;
# Other SRT parameters remain the same
maxbw 1000000000;
mss 1500;
connect_timeout 4000;
peer_idle_timeout 8000;
default_app live;
peerlatency 0;
recvlatency 0;
}
```
Publish SRT stream via IPv6 using FFmpeg:
```bash
ffmpeg -re -i ./doc/source.flv -c copy -pes_payload_size 0 -f mpegts \
'srt://[::1]:10080?streamid=#!::r=live/livestream,m=publish'
```
Play SRT stream via IPv6 using FFplay:
```bash
ffplay 'srt://[::1]:10080?streamid=#!::r=live/livestream,m=request'
```
When using IPv6 addresses in SRT URLs, the IPv6 address must be enclosed in square brackets:
```bash
# Publishing
srt://[2001:db8::1]:10080?streamid=#!::r=live/livestream,m=publish
# Playing
srt://[2001:db8::1]:10080?streamid=#!::r=live/livestream,m=request
# With vhost support
srt://[2001:db8::1]:10080?streamid=#!::h=srs.srt.com.cn,r=live/livestream,m=publish
```
SRS supports dual-stack SRT operation, allowing both IPv4 and IPv6 clients to connect simultaneously:
```bash
srt_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 10080 [::]:10080;
}
```
This configuration allows:
- IPv4 clients: `srt://192.168.1.100:10080?streamid=#!::r=live/stream,m=publish`
- IPv6 clients: `srt://[2001:db8::1]:10080?streamid=#!::r=live/stream,m=publish`
## VLC
VLC has an important limitation: it does not support the `streamid` URL parameter. When VLC connects
to an SRT server, it always sends an empty `SRTO_STREAMID` socket option, regardless of what you put
in the URL. This means VLC can only use the simple URL format `srt://127.0.0.1:10080` without any
streamid parameter.
To support VLC and other clients that don't set `SRTO_STREAMID`, SRS provides a `default_streamid`
configuration option. When a client connects without setting streamid, SRS will use this configured
default value. By default, SRS uses `#!::r=live/livestream,m=publish` for backward compatibility,
but for VLC playback, you should configure it to use `m=request` mode instead.
SRS provides a ready-to-use configuration file `conf/srt.vlc.conf` optimized for VLC compatibility.
Start SRS with this configuration:
```bash
./objs/srs -c conf/srt.vlc.conf
```
You can also set the default streamid using an environment variable, which is useful for Docker deployments:
```bash
env SRS_SRT_SERVER_DEFAULT_STREAMID="#!::r=live/livestream,m=request" \
./objs/srs -c conf/srt.conf
```
Here's a complete workflow example. First, publish a stream with FFmpeg (which explicitly sets streamid
with `m=publish`):
```bash
ffmpeg -re -i ./doc/source.flv -c copy -pes_payload_size 0 -f mpegts \
'srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=publish'
```
Then play with VLC using the simple URL (VLC will use the server's default streamid with `m=request`):
- Open VLC Media Player
- Go to Media → Open Network Stream
- Enter URL: `srt://127.0.0.1:10080`
- Click Play
> Note: VLC doesn't support SRT with streamid, so you should use the simple URL format `srt://127.0.0.1:10080` without any streamid parameter.
You can also play with FFplay by explicitly setting the streamid:
```bash
ffplay 'srt://127.0.0.1:10080?streamid=#!::r=live/livestream,m=request'
```
The key difference between clients: VLC always uses the server's `default_streamid` configuration, while
FFmpeg/FFplay/OBS can set streamid in the URL or settings, which overrides the server default.
## Q&A
1. Does SRS support forwarding SRT streams to Nginx?

View File

@ -516,6 +516,75 @@ Output:
Winlin 2020.02
## IPv6
SRS (v7.0.67+) supports IPv6 for WebRTC protocols, enabling dual-stack (IPv4/IPv6) operation for both UDP and TCP media transport. This includes support for WHIP/WHEP signaling and media transmission over IPv6.
IPv6 support is enabled automatically when SRS detects IPv6 addresses in the configuration. Configure the RTC server to listen on IPv6 addresses:
```bash
rtc_server {
enabled on;
# Listen on both IPv4 and IPv6 for UDP media
listen 8000 [::]:8000;
# For WebRTC over TCP
tcp {
enabled on;
listen 8000 [::]:8000;
}
}
# HTTP API server for WHIP/WHEP over IPv6
http_api {
enabled on;
listen 1985 [::]:1985;
}
# HTTPS API server for secure WHIP/WHEP over IPv6
https_api {
enabled on;
listen 1990 [::]:1990;
key ./conf/server.key;
cert ./conf/server.crt;
}
```
Publish using WHIP via IPv6:
- WHIP URL: `http://[::1]:1985/rtc/v1/whip/?app=live&stream=livestream`
- Test page: [http://[::1]:8080/players/whip.html](http://[::1]:8080/players/whip.html)
Play using WHEP via IPv6:
- WHEP URL: `http://[::1]:1985/rtc/v1/whep/?app=live&stream=livestream`
- Test page: [http://[::1]:8080/players/whep.html](http://[::1]:8080/players/whep.html)
For secure connections over IPv6:
Publish using WHIP via HTTPS IPv6:
- WHIP URL: `https://[::1]:1990/rtc/v1/whip/?app=live&stream=livestream`
- Test page: [https://[::1]:8088/players/whip.html](https://[::1]:8088/players/whip.html)
Play using WHEP via HTTPS IPv6:
- WHEP URL: `https://[::1]:1990/rtc/v1/whep/?app=live&stream=livestream`
- Test page: [https://[::1]:8088/players/whep.html](https://[::1]:8088/players/whep.html)
SRS supports dual-stack WebRTC, allowing both IPv4 and IPv6 clients to connect simultaneously:
```bash
rtc_server {
enabled on;
# Listen on both IPv4 and IPv6
listen 8000 [::]:8000;
# Configure candidates for both protocols
candidate 192.168.1.100 [2001:db8::1];
}
```
This enables:
- IPv4 clients to connect via: `http://192.168.1.100:1985/rtc/v1/whip/`
- IPv6 clients to connect via: `http://[2001:db8::1]:1985/rtc/v1/whip/`
![](https://ossrs.io/gif/v1/sls.gif?site=ossrs.io&path=/lts/doc/en/v7/webrtc)

View File

@ -13,19 +13,7 @@ can be resolved through the documentation, and in cases where you still encounte
assistance from other community members. Friends in the community might offer help, but they are not
obligated to do so, as it's a universal rule of open-source communities.
I have a full-time job and support the community during my spare time, focusing primarily on code and
documentation development. As a result, I have limited time available for community support and prioritize
assistance to project contributors. If you wish for me to dedicate time specifically to assist you, you
may consider becoming a backer or sponsor of the project.
Our documentation and code are open and free, so you don't need to sponsor the community to access these resources. We are a truly open-source community. For financial sponsors, we also offer additional support, including:
* Backer: $5 per month. Online text chat support through Discord. No custom development included.
* Sponsor: $100 per month. Online text chat and online meeting support. No custom development included.
* Custom Development: Must be a Backer or Sponsor first. Fees based on duration, evaluated separately. Please contact us on Discord.
Please visit [OpenCollective](https://opencollective.com/srs-server) to become a backer or sponsor,
and send me a direct message on [Discord](https://discord.gg/yZ4BnPmHAd).
Our documentation and code are open and free, so you don't need to sponsor the community to access these resources. We are a truly open-source community. Please visit [OpenCollective](https://opencollective.com/srs-server) to become a backer or sponsor.
## Discussion

4
trunk/configure vendored
View File

@ -384,8 +384,8 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4"
"srs_utest_protocol3" "srs_utest_app" "srs_utest_app2" "srs_utest_app3" "srs_utest_app4"
"srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app8" "srs_utest_app9"
"srs_utest_app10" "srs_utest_app11" "srs_utest_app15" "srs_utest_app16"
"srs_utest_app17")
"srs_utest_app10" "srs_utest_app11" "srs_utest_app15" "srs_utest_app16"
"srs_utest_app17" "srs_utest_rtc_playstream")
# Always include SRT utest
MODULE_FILES+=("srs_utest_srt")
if [[ $SRS_GB28181 == YES ]]; then

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## SRS 7.0 Changelog
* v7.0, 2025-10-16, Merge [#4530](https://github.com/ossrs/srs/pull/4530): fix crash issue caused by reload configuration file. v7.0.98 (#4530)
* v7.0, 2025-10-15, Merge [#4520](https://github.com/ossrs/srs/pull/4520): srs_app_rtc_conn: fix illegal memory access. v7.0.97 (#4520)
* v7.0, 2025-10-14, Disable sanitizer by default to fix memory leak. (#4364) v7.0.96
* v7.0, 2025-10-01, SRT: Support configurable default_streamid option. v7.0.95 (#4515)

View File

@ -1509,6 +1509,12 @@ srs_error_t SrsConfig::reload_conf(SrsConfig *conf)
SrsConfDirective *old_vhost = old_root->get("vhost", vhost);
SrsConfDirective *new_vhost = root_->get("vhost", vhost);
// Only compare config when both old and new vhost exist.
// @see https://github.com/ossrs/srs/issues/4529
if (!old_vhost || !new_vhost) {
continue;
}
// chunk_size, only one per vhost.
if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) {
for (it = subscribes_.begin(); it != subscribes_.end(); ++it) {

View File

@ -21,6 +21,7 @@ using namespace std;
#include <srs_app_circuit_breaker.hpp>
#include <srs_app_config.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_http_api.hpp>
#include <srs_app_http_hooks.hpp>
#include <srs_app_log.hpp>
@ -304,6 +305,14 @@ ISrsRtcPliWorkerHandler::~ISrsRtcPliWorkerHandler()
{
}
ISrsRtcPliWorker::ISrsRtcPliWorker()
{
}
ISrsRtcPliWorker::~ISrsRtcPliWorker()
{
}
SrsRtcPliWorker::SrsRtcPliWorker(ISrsRtcPliWorkerHandler *h)
{
handler_ = h;
@ -455,6 +464,7 @@ SrsRtcPlayStream::SrsRtcPlayStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expir
config_ = _srs_config;
rtc_sources_ = _srs_rtc_sources;
stat_ = _srs_stat;
app_factory_ = _srs_app_factory;
}
SrsRtcPlayStream::~SrsRtcPlayStream()
@ -491,6 +501,7 @@ SrsRtcPlayStream::~SrsRtcPlayStream()
config_ = NULL;
rtc_sources_ = NULL;
stat_ = NULL;
app_factory_ = NULL;
}
srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, SrsRtcTrackDescription *> sub_relations)
@ -617,7 +628,7 @@ srs_error_t SrsRtcPlayStream::start()
}
srs_freep(trd_);
trd_ = new SrsFastCoroutine("rtc_sender", this, cid_);
trd_ = app_factory_->create_coroutine("rtc_sender", this, cid_);
if ((err = trd_->start()) != srs_success) {
return srs_error_wrap(err, "rtc_sender");

View File

@ -64,6 +64,8 @@ class ISrsSrtSourceManager;
class ISrsLiveSourceManager;
class SrsRtcPublisherNegotiator;
class SrsRtcPlayerNegotiator;
class ISrsAppFactory;
class ISrsCoroutine;
const uint8_t kSR = 200;
const uint8_t kRR = 201;
@ -192,8 +194,20 @@ public:
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
};
// The interface for PLI worker coroutine.
class ISrsRtcPliWorker : public ISrsCoroutineHandler
{
public:
ISrsRtcPliWorker();
virtual ~ISrsRtcPliWorker();
public:
virtual srs_error_t start() = 0;
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
};
// A worker coroutine to request the PLI.
class SrsRtcPliWorker : public ISrsCoroutineHandler
class SrsRtcPliWorker : public ISrsRtcPliWorker
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -247,6 +261,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
ISrsExecRtcAsyncTask *exec_;
ISrsExpire *expire_;
ISrsRtcPacketSender *sender_;
ISrsAppFactory *app_factory_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
@ -257,8 +272,8 @@ SRS_DECLARE_PRIVATE: // clang-format on
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
SrsContextId cid_;
SrsFastCoroutine *trd_;
SrsRtcPliWorker *pli_worker_;
ISrsCoroutine *trd_;
ISrsRtcPliWorker *pli_worker_;
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 97
#define VERSION_REVISION 98
#endif

View File

@ -111,3 +111,39 @@ VOID TEST(ConfigReloadTest, ReloadVhostChunkSize)
EXPECT_EQ(1, handler.count_true());
handler.reset();
}
VOID TEST(ConfigReloadTest, ReloadAddNewVhost)
{
srs_error_t err = srs_success;
MockReloadHandler handler;
MockSrsReloadConfig conf;
conf.subscribe(&handler);
// Start with one vhost
HELPER_EXPECT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost ossrs.net { chunk_size 60000; }"));
// Add a new vhost - should not crash
HELPER_EXPECT_SUCCESS(conf.do_reload(_MIN_OK_CONF "vhost ossrs.net { chunk_size 60000; } vhost new_vhost { chunk_size 65536; }"));
// Handler should not be triggered for new vhost
EXPECT_TRUE(handler.all_false());
handler.reset();
}
VOID TEST(ConfigReloadTest, ReloadRemoveVhost)
{
srs_error_t err = srs_success;
MockReloadHandler handler;
MockSrsReloadConfig conf;
conf.subscribe(&handler);
// Start with two vhosts
HELPER_EXPECT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost ossrs.net { chunk_size 60000; } vhost old_vhost { chunk_size 65536; }"));
// Remove old_vhost - should not crash
HELPER_EXPECT_SUCCESS(conf.do_reload(_MIN_OK_CONF "vhost ossrs.net { chunk_size 60000; }"));
// Handler should not be triggered for removed vhost
EXPECT_TRUE(handler.all_false());
handler.reset();
}

View File

@ -0,0 +1,360 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_rtc_playstream.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_kernel_error.hpp>
#include <srs_utest_app.hpp>
#include <srs_utest_app6.hpp>
// Mock ISrsCoroutine implementation
MockCoroutineForPlayStream::MockCoroutineForPlayStream()
{
start_error_ = srs_success;
start_count_ = 0;
stop_count_ = 0;
interrupt_count_ = 0;
}
MockCoroutineForPlayStream::~MockCoroutineForPlayStream()
{
srs_freep(start_error_);
}
srs_error_t MockCoroutineForPlayStream::start()
{
start_count_++;
if (start_error_ != srs_success) {
return srs_error_copy(start_error_);
}
return srs_success;
}
void MockCoroutineForPlayStream::stop()
{
stop_count_++;
}
void MockCoroutineForPlayStream::interrupt()
{
interrupt_count_++;
}
srs_error_t MockCoroutineForPlayStream::pull()
{
return srs_success;
}
const SrsContextId &MockCoroutineForPlayStream::cid()
{
return cid_;
}
void MockCoroutineForPlayStream::set_cid(const SrsContextId &cid)
{
cid_ = cid;
}
void MockCoroutineForPlayStream::reset()
{
srs_freep(start_error_);
start_count_ = 0;
stop_count_ = 0;
interrupt_count_ = 0;
}
void MockCoroutineForPlayStream::set_start_error(srs_error_t err)
{
srs_freep(start_error_);
start_error_ = srs_error_copy(err);
}
// Mock ISrsAppFactory implementation
MockAppFactoryForPlayStream::MockAppFactoryForPlayStream()
{
mock_coroutine_ = new MockCoroutineForPlayStream();
create_coroutine_error_ = srs_success;
create_coroutine_count_ = 0;
last_coroutine_handler_ = NULL;
}
MockAppFactoryForPlayStream::~MockAppFactoryForPlayStream()
{
srs_freep(mock_coroutine_);
srs_freep(create_coroutine_error_);
}
ISrsFileWriter *MockAppFactoryForPlayStream::create_file_writer()
{
return NULL;
}
ISrsFileWriter *MockAppFactoryForPlayStream::create_enc_file_writer()
{
return NULL;
}
ISrsFileReader *MockAppFactoryForPlayStream::create_file_reader()
{
return NULL;
}
SrsPath *MockAppFactoryForPlayStream::create_path()
{
return NULL;
}
SrsLiveSource *MockAppFactoryForPlayStream::create_live_source()
{
return NULL;
}
ISrsOriginHub *MockAppFactoryForPlayStream::create_origin_hub()
{
return NULL;
}
ISrsHourGlass *MockAppFactoryForPlayStream::create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval)
{
return NULL;
}
ISrsBasicRtmpClient *MockAppFactoryForPlayStream::create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto)
{
return NULL;
}
ISrsHttpClient *MockAppFactoryForPlayStream::create_http_client()
{
return NULL;
}
ISrsFileReader *MockAppFactoryForPlayStream::create_http_file_reader(ISrsHttpResponseReader *r)
{
return NULL;
}
ISrsFlvDecoder *MockAppFactoryForPlayStream::create_flv_decoder()
{
return NULL;
}
#ifdef SRS_RTSP
ISrsRtspSendTrack *MockAppFactoryForPlayStream::create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
ISrsRtspSendTrack *MockAppFactoryForPlayStream::create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc)
{
return NULL;
}
#endif
ISrsFlvTransmuxer *MockAppFactoryForPlayStream::create_flv_transmuxer()
{
return NULL;
}
ISrsMp4Encoder *MockAppFactoryForPlayStream::create_mp4_encoder()
{
return NULL;
}
ISrsDvrSegmenter *MockAppFactoryForPlayStream::create_dvr_flv_segmenter()
{
return NULL;
}
ISrsDvrSegmenter *MockAppFactoryForPlayStream::create_dvr_mp4_segmenter()
{
return NULL;
}
#ifdef SRS_GB28181
ISrsGbMediaTcpConn *MockAppFactoryForPlayStream::create_gb_media_tcp_conn()
{
return NULL;
}
ISrsGbSession *MockAppFactoryForPlayStream::create_gb_session()
{
return NULL;
}
#endif
ISrsInitMp4 *MockAppFactoryForPlayStream::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockAppFactoryForPlayStream::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockAppFactoryForPlayStream::create_fragmented_mp4()
{
return NULL;
}
ISrsIpListener *MockAppFactoryForPlayStream::create_tcp_listener(ISrsTcpHandler *handler)
{
return NULL;
}
ISrsRtcConnection *MockAppFactoryForPlayStream::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsFFMPEG *MockAppFactoryForPlayStream::create_ffmpeg(std::string ffmpeg_bin)
{
return NULL;
}
ISrsIngesterFFMPEG *MockAppFactoryForPlayStream::create_ingester_ffmpeg()
{
return NULL;
}
ISrsCoroutine *MockAppFactoryForPlayStream::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
create_coroutine_count_++;
last_coroutine_name_ = name;
last_coroutine_handler_ = handler;
last_coroutine_cid_ = cid;
if (create_coroutine_error_ != srs_success) {
return NULL;
}
return mock_coroutine_;
}
ISrsTime *MockAppFactoryForPlayStream::create_time()
{
return NULL;
}
ISrsConfig *MockAppFactoryForPlayStream::create_config()
{
return NULL;
}
ISrsCond *MockAppFactoryForPlayStream::create_cond()
{
return NULL;
}
void MockAppFactoryForPlayStream::reset()
{
mock_coroutine_->reset();
srs_freep(create_coroutine_error_);
create_coroutine_count_ = 0;
last_coroutine_name_ = "";
last_coroutine_handler_ = NULL;
}
void MockAppFactoryForPlayStream::set_create_coroutine_error(srs_error_t err)
{
srs_freep(create_coroutine_error_);
create_coroutine_error_ = srs_error_copy(err);
}
// Mock ISrsRtcPliWorker implementation
MockRtcPliWorkerForPlayStream::MockRtcPliWorkerForPlayStream()
{
start_error_ = srs_success;
start_count_ = 0;
request_keyframe_count_ = 0;
}
MockRtcPliWorkerForPlayStream::~MockRtcPliWorkerForPlayStream()
{
srs_freep(start_error_);
}
srs_error_t MockRtcPliWorkerForPlayStream::start()
{
start_count_++;
if (start_error_ != srs_success) {
return srs_error_copy(start_error_);
}
return srs_success;
}
void MockRtcPliWorkerForPlayStream::request_keyframe(uint32_t ssrc, SrsContextId cid)
{
request_keyframe_count_++;
}
srs_error_t MockRtcPliWorkerForPlayStream::cycle()
{
return srs_success;
}
void MockRtcPliWorkerForPlayStream::reset()
{
srs_freep(start_error_);
start_count_ = 0;
request_keyframe_count_ = 0;
}
void MockRtcPliWorkerForPlayStream::set_start_error(srs_error_t err)
{
srs_freep(start_error_);
start_error_ = srs_error_copy(err);
}
// Test SrsRtcPlayStream::start() - Basic success scenario
VOID TEST(RtcPlayStreamStartTest, StartSuccess)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockExpire mock_expire;
MockRtcPacketSender mock_sender;
SrsContextId cid;
cid.set_value("test-play-stream-start-cid");
// Create RTC play stream
SrsUniquePtr<SrsRtcPlayStream> play_stream(new SrsRtcPlayStream(&mock_exec, &mock_expire, &mock_sender, cid));
// Create and inject mock app factory
MockAppFactoryForPlayStream mock_factory;
play_stream->app_factory_ = &mock_factory;
// Create and inject mock PLI worker
MockRtcPliWorkerForPlayStream *mock_pli_worker = new MockRtcPliWorkerForPlayStream();
play_stream->pli_worker_ = mock_pli_worker;
// Test: First call to start() should succeed
HELPER_EXPECT_SUCCESS(play_stream->start());
// Verify coroutine was created with correct parameters
EXPECT_EQ(1, mock_factory.create_coroutine_count_);
EXPECT_STREQ("rtc_sender", mock_factory.last_coroutine_name_.c_str());
EXPECT_TRUE(mock_factory.last_coroutine_handler_ == play_stream.get());
EXPECT_EQ(0, cid.compare(mock_factory.last_coroutine_cid_));
// Verify coroutine start was called
EXPECT_EQ(1, mock_factory.mock_coroutine_->start_count_);
// Verify PLI worker start was called
EXPECT_EQ(1, mock_pli_worker->start_count_);
// Verify is_started_ flag is set
EXPECT_TRUE(play_stream->is_started_);
// Clean up - set to NULL to avoid double-free
play_stream->trd_ = NULL; // Set to NULL before mock_factory is destroyed
play_stream->app_factory_ = NULL;
play_stream->pli_worker_ = NULL;
srs_freep(mock_pli_worker);
}

View File

@ -0,0 +1,127 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_RTC_PLAYSTREAM_HPP
#define SRS_UTEST_RTC_PLAYSTREAM_HPP
/*
#include <srs_utest_rtc_playstream.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_kernel_factory.hpp>
// Mock ISrsCoroutine for testing SrsRtcPlayStream::start()
class MockCoroutineForPlayStream : public ISrsCoroutine
{
public:
srs_error_t start_error_;
int start_count_;
int stop_count_;
int interrupt_count_;
SrsContextId cid_;
public:
MockCoroutineForPlayStream();
virtual ~MockCoroutineForPlayStream();
public:
virtual srs_error_t start();
virtual void stop();
virtual void interrupt();
virtual srs_error_t pull();
virtual const SrsContextId &cid();
virtual void set_cid(const SrsContextId &cid);
public:
void reset();
void set_start_error(srs_error_t err);
};
// Mock ISrsAppFactory for testing SrsRtcPlayStream::start()
class MockAppFactoryForPlayStream : public ISrsAppFactory
{
public:
MockCoroutineForPlayStream *mock_coroutine_;
srs_error_t create_coroutine_error_;
int create_coroutine_count_;
std::string last_coroutine_name_;
ISrsCoroutineHandler *last_coroutine_handler_;
SrsContextId last_coroutine_cid_;
public:
MockAppFactoryForPlayStream();
virtual ~MockAppFactoryForPlayStream();
public:
// ISrsAppFactory interface methods
virtual ISrsFileWriter *create_file_writer();
virtual ISrsFileWriter *create_enc_file_writer();
virtual ISrsFileReader *create_file_reader();
virtual SrsPath *create_path();
virtual SrsLiveSource *create_live_source();
virtual ISrsOriginHub *create_origin_hub();
virtual ISrsHourGlass *create_hourglass(const std::string &name, ISrsHourGlassHandler *handler, srs_utime_t interval);
virtual ISrsBasicRtmpClient *create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto);
virtual ISrsHttpClient *create_http_client();
virtual ISrsFileReader *create_http_file_reader(ISrsHttpResponseReader *r);
virtual ISrsFlvDecoder *create_flv_decoder();
#ifdef SRS_RTSP
virtual ISrsRtspSendTrack *create_rtsp_audio_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
virtual ISrsRtspSendTrack *create_rtsp_video_send_track(ISrsRtspConnection *session, SrsRtcTrackDescription *track_desc);
#endif
virtual ISrsFlvTransmuxer *create_flv_transmuxer();
virtual ISrsMp4Encoder *create_mp4_encoder();
virtual ISrsDvrSegmenter *create_dvr_flv_segmenter();
virtual ISrsDvrSegmenter *create_dvr_mp4_segmenter();
#ifdef SRS_GB28181
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
#endif
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
virtual ISrsFFMPEG *create_ffmpeg(std::string ffmpeg_bin);
virtual ISrsIngesterFFMPEG *create_ingester_ffmpeg();
// ISrsKernelFactory interface methods
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();
virtual ISrsConfig *create_config();
virtual ISrsCond *create_cond();
public:
void reset();
void set_create_coroutine_error(srs_error_t err);
};
// Mock ISrsRtcPliWorker for testing SrsRtcPlayStream::start()
class MockRtcPliWorkerForPlayStream : public ISrsRtcPliWorker
{
public:
srs_error_t start_error_;
int start_count_;
int request_keyframe_count_;
public:
MockRtcPliWorkerForPlayStream();
virtual ~MockRtcPliWorkerForPlayStream();
public:
virtual srs_error_t start();
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid);
virtual srs_error_t cycle();
public:
void reset();
void set_start_error(srs_error_t err);
};
#endif