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:
parent
6f526284a3
commit
abaffdd4b9
4
trunk/3rdparty/signaling/Dockerfile
vendored
4
trunk/3rdparty/signaling/Dockerfile
vendored
|
|
@ -2,7 +2,7 @@
|
||||||
############################################################
|
############################################################
|
||||||
# build
|
# build
|
||||||
############################################################
|
############################################################
|
||||||
FROM registry.cn-hangzhou.aliyuncs.com/ossrs/srs:dev AS build
|
FROM ossrs/srs:ubuntu20 AS build
|
||||||
|
|
||||||
COPY . /tmp/signaling
|
COPY . /tmp/signaling
|
||||||
RUN cd /tmp/signaling && make
|
RUN cd /tmp/signaling && make
|
||||||
|
|
@ -12,7 +12,7 @@ RUN cp -R /tmp/signaling/www /usr/local/
|
||||||
############################################################
|
############################################################
|
||||||
# dist
|
# dist
|
||||||
############################################################
|
############################################################
|
||||||
FROM centos:7 AS dist
|
FROM ubuntu:focal AS dist
|
||||||
|
|
||||||
# HTTP/1989
|
# HTTP/1989
|
||||||
EXPOSE 1989
|
EXPOSE 1989
|
||||||
|
|
|
||||||
9
trunk/3rdparty/srs-docs/doc/edge.md
vendored
9
trunk/3rdparty/srs-docs/doc/edge.md
vendored
|
|
@ -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
|
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.
|
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
|
## 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`.
|
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`.
|
||||||
|
|
|
||||||
49
trunk/3rdparty/srs-docs/doc/flv.md
vendored
49
trunk/3rdparty/srs-docs/doc/flv.md
vendored
|
|
@ -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.
|
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.
|
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`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
47
trunk/3rdparty/srs-docs/doc/hls.md
vendored
47
trunk/3rdparty/srs-docs/doc/hls.md
vendored
|
|
@ -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)
|
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`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
56
trunk/3rdparty/srs-docs/doc/rtmp.md
vendored
56
trunk/3rdparty/srs-docs/doc/rtmp.md
vendored
|
|
@ -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.
|
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
|
## On Demand Live Streaming
|
||||||
|
|
||||||
In some situations, you might want to start streaming only when someone starts watching:
|
In some situations, you might want to start streaming only when someone starts watching:
|
||||||
|
|
|
||||||
60
trunk/3rdparty/srs-docs/doc/rtsp.md
vendored
60
trunk/3rdparty/srs-docs/doc/rtsp.md
vendored
|
|
@ -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.
|
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
|
## References
|
||||||
|
|
||||||
- [rfc2326-1998-rtsp.pdf](/files/rfc2326-1998-rtsp.pdf)
|
- [rfc2326-1998-rtsp.pdf](/files/rfc2326-1998-rtsp.pdf)
|
||||||
118
trunk/3rdparty/srs-docs/doc/srt.md
vendored
118
trunk/3rdparty/srs-docs/doc/srt.md
vendored
|
|
@ -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`
|
* RTMP(VLC/ffplay): `rtmp://localhost/live/livestream`
|
||||||
* HLS by SRS player: [http://localhost:8080/live/livestream.flv](http://localhost:8080/players/srs_player.html)
|
* 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.
|
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.
|
> 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
|
## Q&A
|
||||||
|
|
||||||
1. Does SRS support forwarding SRT streams to Nginx?
|
1. Does SRS support forwarding SRT streams to Nginx?
|
||||||
|
|
|
||||||
69
trunk/3rdparty/srs-docs/doc/webrtc.md
vendored
69
trunk/3rdparty/srs-docs/doc/webrtc.md
vendored
|
|
@ -516,6 +516,75 @@ Output:
|
||||||
|
|
||||||
Winlin 2020.02
|
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/`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
14
trunk/3rdparty/srs-docs/pages/contact-en.md
vendored
14
trunk/3rdparty/srs-docs/pages/contact-en.md
vendored
|
|
@ -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
|
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.
|
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
|
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.
|
||||||
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).
|
|
||||||
|
|
||||||
## Discussion
|
## Discussion
|
||||||
|
|
||||||
|
|
|
||||||
4
trunk/configure
vendored
4
trunk/configure
vendored
|
|
@ -384,8 +384,8 @@ if [[ $SRS_UTEST == YES ]]; then
|
||||||
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4"
|
"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_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_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_app10" "srs_utest_app11" "srs_utest_app15" "srs_utest_app16"
|
||||||
"srs_utest_app17")
|
"srs_utest_app17" "srs_utest_rtc_playstream")
|
||||||
# Always include SRT utest
|
# Always include SRT utest
|
||||||
MODULE_FILES+=("srs_utest_srt")
|
MODULE_FILES+=("srs_utest_srt")
|
||||||
if [[ $SRS_GB28181 == YES ]]; then
|
if [[ $SRS_GB28181 == YES ]]; then
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
||||||
<a name="v7-changes"></a>
|
<a name="v7-changes"></a>
|
||||||
|
|
||||||
## SRS 7.0 Changelog
|
## 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-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-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)
|
* v7.0, 2025-10-01, SRT: Support configurable default_streamid option. v7.0.95 (#4515)
|
||||||
|
|
|
||||||
|
|
@ -1509,6 +1509,12 @@ srs_error_t SrsConfig::reload_conf(SrsConfig *conf)
|
||||||
SrsConfDirective *old_vhost = old_root->get("vhost", vhost);
|
SrsConfDirective *old_vhost = old_root->get("vhost", vhost);
|
||||||
SrsConfDirective *new_vhost = 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.
|
// chunk_size, only one per vhost.
|
||||||
if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) {
|
if (!srs_directive_equals(new_vhost->get("chunk_size"), old_vhost->get("chunk_size"))) {
|
||||||
for (it = subscribes_.begin(); it != subscribes_.end(); ++it) {
|
for (it = subscribes_.begin(); it != subscribes_.end(); ++it) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ using namespace std;
|
||||||
|
|
||||||
#include <srs_app_circuit_breaker.hpp>
|
#include <srs_app_circuit_breaker.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_app_factory.hpp>
|
||||||
#include <srs_app_http_api.hpp>
|
#include <srs_app_http_api.hpp>
|
||||||
#include <srs_app_http_hooks.hpp>
|
#include <srs_app_http_hooks.hpp>
|
||||||
#include <srs_app_log.hpp>
|
#include <srs_app_log.hpp>
|
||||||
|
|
@ -304,6 +305,14 @@ ISrsRtcPliWorkerHandler::~ISrsRtcPliWorkerHandler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsRtcPliWorker::ISrsRtcPliWorker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsRtcPliWorker::~ISrsRtcPliWorker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsRtcPliWorker::SrsRtcPliWorker(ISrsRtcPliWorkerHandler *h)
|
SrsRtcPliWorker::SrsRtcPliWorker(ISrsRtcPliWorkerHandler *h)
|
||||||
{
|
{
|
||||||
handler_ = h;
|
handler_ = h;
|
||||||
|
|
@ -455,6 +464,7 @@ SrsRtcPlayStream::SrsRtcPlayStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expir
|
||||||
config_ = _srs_config;
|
config_ = _srs_config;
|
||||||
rtc_sources_ = _srs_rtc_sources;
|
rtc_sources_ = _srs_rtc_sources;
|
||||||
stat_ = _srs_stat;
|
stat_ = _srs_stat;
|
||||||
|
app_factory_ = _srs_app_factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtcPlayStream::~SrsRtcPlayStream()
|
SrsRtcPlayStream::~SrsRtcPlayStream()
|
||||||
|
|
@ -491,6 +501,7 @@ SrsRtcPlayStream::~SrsRtcPlayStream()
|
||||||
config_ = NULL;
|
config_ = NULL;
|
||||||
rtc_sources_ = NULL;
|
rtc_sources_ = NULL;
|
||||||
stat_ = NULL;
|
stat_ = NULL;
|
||||||
|
app_factory_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, SrsRtcTrackDescription *> sub_relations)
|
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_);
|
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) {
|
if ((err = trd_->start()) != srs_success) {
|
||||||
return srs_error_wrap(err, "rtc_sender");
|
return srs_error_wrap(err, "rtc_sender");
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ class ISrsSrtSourceManager;
|
||||||
class ISrsLiveSourceManager;
|
class ISrsLiveSourceManager;
|
||||||
class SrsRtcPublisherNegotiator;
|
class SrsRtcPublisherNegotiator;
|
||||||
class SrsRtcPlayerNegotiator;
|
class SrsRtcPlayerNegotiator;
|
||||||
|
class ISrsAppFactory;
|
||||||
|
class ISrsCoroutine;
|
||||||
|
|
||||||
const uint8_t kSR = 200;
|
const uint8_t kSR = 200;
|
||||||
const uint8_t kRR = 201;
|
const uint8_t kRR = 201;
|
||||||
|
|
@ -192,8 +194,20 @@ public:
|
||||||
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
|
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.
|
// A worker coroutine to request the PLI.
|
||||||
class SrsRtcPliWorker : public ISrsCoroutineHandler
|
class SrsRtcPliWorker : public ISrsRtcPliWorker
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SRS_DECLARE_PRIVATE: // clang-format on
|
SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
|
|
@ -247,6 +261,7 @@ SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
ISrsExecRtcAsyncTask *exec_;
|
ISrsExecRtcAsyncTask *exec_;
|
||||||
ISrsExpire *expire_;
|
ISrsExpire *expire_;
|
||||||
ISrsRtcPacketSender *sender_;
|
ISrsRtcPacketSender *sender_;
|
||||||
|
ISrsAppFactory *app_factory_;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SRS_DECLARE_PRIVATE: // clang-format on
|
SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
|
|
@ -257,8 +272,8 @@ SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SRS_DECLARE_PRIVATE: // clang-format on
|
SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
SrsContextId cid_;
|
SrsContextId cid_;
|
||||||
SrsFastCoroutine *trd_;
|
ISrsCoroutine *trd_;
|
||||||
SrsRtcPliWorker *pli_worker_;
|
ISrsRtcPliWorker *pli_worker_;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SRS_DECLARE_PRIVATE: // clang-format on
|
SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 7
|
#define VERSION_MAJOR 7
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 97
|
#define VERSION_REVISION 98
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -111,3 +111,39 @@ VOID TEST(ConfigReloadTest, ReloadVhostChunkSize)
|
||||||
EXPECT_EQ(1, handler.count_true());
|
EXPECT_EQ(1, handler.count_true());
|
||||||
handler.reset();
|
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();
|
||||||
|
}
|
||||||
|
|
|
||||||
360
trunk/src/utest/srs_utest_rtc_playstream.cpp
Normal file
360
trunk/src/utest/srs_utest_rtc_playstream.cpp
Normal 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);
|
||||||
|
}
|
||||||
127
trunk/src/utest/srs_utest_rtc_playstream.hpp
Normal file
127
trunk/src/utest/srs_utest_rtc_playstream.hpp
Normal 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
|
||||||
Loading…
Reference in New Issue
Block a user