## Introduce
This PR adds support for viewing streams via the RTSP protocol. Note
that it only supports viewing streams, not publishing streams via RTSP.
Currently, only publishing via RTMP is supported, which is then
converted to RTSP. Further work is needed to support publishing RTC/SRT
streams and converting them to RTSP.
## Usage
Build and run SRS with RTSP support:
```
cd srs/trunk && ./configure --rtsp=on && make -j16
./objs/srs -c conf/rtsp.conf
```
Push stream via RTMP by FFmpeg:
```
ffmpeg -re -i doc/source.flv -c copy -f flv rtmp://localhost/live/livestream
```
View the stream via RTSP protocol, try UDP first, then use TCP:
```
ffplay -i rtsp://localhost:8554/live/livestream
```
Or specify the transport protocol with TCP:
```
ffplay -rtsp_transport tcp -i rtsp://localhost:8554/live/livestream
```
## Unit Test
Run utest for RTSP:
```
./configure --utest=on & make utest -j16
./objs/srs_utest
```
## Regression Test
You need to start SRS for regression testing.
```
./objs/srs -c conf/regression-test-for-clion.conf
```
Then run regression tests for RTSP.
```
cd srs/trunk/3rdparty/srs-bench
go test ./srs -mod=vendor -v -count=1 -run=TestRtmpPublish_RtspPlay
```
## Blackbox Test
For blackbox testing, SRS will be started by utest, so there is no need
to start SRS manually.
```
cd srs/trunk/3rdparty/srs-bench
go test ./blackbox -mod=vendor -v -count=1 -run=TestFast_RtmpPublish_RtspPlay_Basic
```
## UDP Transport
As UDP requires port allocation, this PR doesn't support delivering
media stream via UDP transport, so it will fail if you try to use UDP as
transport:
```
ffplay -rtsp_transport udp -i rtsp://localhost:8554/live/livestream
[rtsp @ 0x7fbc99a14880] method SETUP failed: 461 Unsupported Transport
rtsp://localhost:8554/live/livestream: Protocol not supported
[2025-07-05 21:30:52.738][WARN][14916][7d7gf623][35] RTSP: setup failed: code=2057
(RtspTransportNotSupported) : UDP transport not supported, only TCP/interleaved mode is supported
```
There are no plans to support UDP transport for RTSP. In the real world,
UDP is rarely used; the vast majority of RTSP traffic uses TCP.
## Play Before Publish
RTSP supports audio with AAC and OPUS codecs, which is significantly
different from RTMP or WebRTC.
RTSP uses commands to exchange SDP and specify the audio track to play,
unlike WHEP or HTTP-FLV, which use the query string of the URL. RTSP
depends on the player’s behavior, making it very difficult to use and
describe.
Considering the feature that allows playing the stream before publishing
it, it requires generating some default parameters in the SDP. For OPUS,
the sample rate is 48 kHz with 2 channels, while AAC is more complex,
especially regarding the sample rate, which may be 44.1 kHz, 32 kHz, or
48 kHz.
Therefore, for RTSP, we cannot support play-then-publish. Instead, there
must already be a stream when playing it, so that the audio codec is
determined.
## Opus Codec
No Opus codec support for RTSP, because for RTC2RTSP, it always converts
RTC to RTMP frames, then converts them to RTSP packets. Therefore, the
audio codec is always AAC after converting RTC to RTMP.
This means the bridge architecture needs some changes. We need a new
bridge that binds to the target protocol. For example, RTC2RTMP converts
the audio codec, but RTC2RTSP keeps the original audio codec.
Furthermore, the RTC2RTMP bridge should also support bypassing the Opus
codec if we use enhanced-RTMP, which supports the Opus audio codec. I
think it should be configurable to either transcode or bypass the audio
codec. However, this is not relevant to RTSP.
## AI Contributor
Below commits are contributed by AI:
* [AI: Remove support for media transport via
UDP.](755686229f)
* [AI: Add crutial logs for each RTSP
stage.](9c8cbe7bde)
* [AI: Support AAC doec for
RTSP.](7d7cc12bae)
* [AI: Add option --rtsp for
RTSP.](f67414d9ee)
* [AI: Extract SrsRtpVideoBuilder for RTC and
RTSP.](562e76b904)
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
Co-authored-by: winlin <winlinvip@gmail.com>
This PR refactors the RTMP to RTC bridge to support multiple video
tracks by implementing lazy initialization of audio and video tracks.
Instead of pre-determining track parameters during bridge construction,
tracks are now initialized dynamically when the first packet of each
type is received, allowing proper codec detection and track
configuration for dual video track scenarios.
Failed to view WHEP with HEVC before publishing RTMP, because the
default codec is AVC and will not be updated until the stream is
published. This enables better handling of streams with multiple video
tracks in RTMP to WebRTC bridging scenarios. Now, you are able to:
1. View WHEP with HEVC: Play with WebRTC:
http://localhost:8080/players/whep.html?schema=http&&codec=hevc
2. Then publish by RTMP: `ffmpeg -stream_loop -1 -re -i doc/source.flv
-c:v libx265 -profile:v main -preset fast -b:v 2000k -maxrate 2000k
-bufsize 2000k -bf 0 -c:a aac -b:a 48k -ar 44100 -ac 2 -f flv
rtmp://localhost/live/livestream`
Or publish RTMP with HEVC, then view by WHEP.
Note that if the codecs do not match, the error log will display RTC:
`Drop for ssrc xxxxxx not found`. For example, this can occur if you
publish RTMP with HEVC while viewing the stream with AVC.
**Introduce**
This pull request builds upon the foundation laid in
https://github.com/ossrs/srs/pull/4289 . While the previous work solely
implemented unidirectional HEVC support from RTMP to RTC, this
submission further enhances it by introducing support for the RTC to
RTMP direction.
**Usage**
Launch SRS with `rtc2rtmp.conf`
```bash
./objs/srs -c conf/rtc2rtmp.conf
```
**Push with WebRTC**
Upgrade browser to Chrome(136+) or Safari(18+), then open [WHIP
encoder](http://localhost:8080/players/whip.html?schema=http&&codec=hevc),
push stream with URL that enables HEVC by query string `codec=hevc`:
```bash
http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream&codec=hevc
```
This query string `codec=hevc` is used to select the video codec, and
generate lines in the answer SDP.
```
m=video 9 UDP/TLS/RTP/SAVPF 49 123
a=rtpmap:49 H265/90000
```
The encoder log also show the codec:
```
Audio: opus, 48000HZ, channels: 2, pt: 111
Video: H265, 90000HZ, pt: 49
```
**Play with RTMP**
Play HEVC stream via RTMP.
```bash
ffplay -i rtmp://localhost/live/livestream
```
You will see the codec in logs:
```
Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp
Stream #0:1: Video: hevc (Main), yuv420p(tv, bt709), 320x240, 30 fps, 30 tbr, 1k tbn
```
You can also use [WHEP
player](http://localhost:8080/players/whep.html?schema=http&&codec=hevc)
to play the stream.
Important refactor with AI:
* [AI: Refactor packet cache for RTC frame
builder.](b8ffa1630e)
* [AI: Refactor the packet copy and free for
SrsRtcFrameBuilder](f3487b45d7)
* [AI: Refactor the frame detector for
SrsRtcFrameBuilder](4ffc1526b9)
* [AI: Refactor the packet_video_rtmp for
SrsRtcFrameBuilder](81f6aef4ed)
* [AI: Add utests for
SrsCodecPayload.codec](61eb1c0bfc)
* [AI: Add utests for VideoPacketCache in
SrsRtcFrameBuilder.](fd25480dfa)
* [AI: Add utests for VideoFrameDetector in
SrsRtcFrameBuilder.](b4aa977bbd)
* [AI: Add regression test for RTC2RTMP with
HEVC.](5259a2aac3)
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
Co-authored-by: winlin <winlinvip@gmail.com>
SrsUniquePtr does not support array or object created by malloc, because
we only use delete to dispose the resource. You can use a custom
function to free the memory allocated by malloc or other allocators.
```cpp
char* p = (char*)malloc(1024);
SrsUniquePtr<char> ptr(p, your_free_chars);
```
This is used to replace the SrsAutoFreeH. For example:
```cpp
addrinfo* r = NULL;
SrsAutoFreeH(addrinfo, r, freeaddrinfo);
getaddrinfo("127.0.0.1", NULL, &hints, &r);
```
Now, this can be replaced by:
```cpp
addrinfo* r = NULL;
getaddrinfo("127.0.0.1", NULL, &hints, &r);
SrsUniquePtr<addrinfo> r2(r, freeaddrinfo);
```
Please aware that there is a slight difference between SrsAutoFreeH and
SrsUniquePtr. SrsAutoFreeH will track the address of pointer, while
SrsUniquePtr will not.
```cpp
addrinfo* r = NULL;
SrsAutoFreeH(addrinfo, r, freeaddrinfo); // r will be freed even r is changed later.
SrsUniquePtr<addrinfo> ptr(r, freeaddrinfo); // crash because r is an invalid pointer.
```
---------
Co-authored-by: Haibo Chen <495810242@qq.com>
Co-authored-by: john <hondaxiao@tencent.com>
## How to reproduce?
1. Refer this commit, which contains the web demo to capture screen as
video stream through RTC.
2. Copy the `trunk/research/players/whip.html` and
`trunk/research/players/js/srs.sdk.js` to replace the `develop` branch
source code.
3. `./configure && make`
4. `./objs/srs -c conf/rtc2rtmp.conf`
5. open `http://localhost:8080/players/whip.html?schema=http`
6. check `Screen` radio option.
7. click `publish`, then check the screen to share.
8. play the rtmp live stream: `rtmp://localhost/live/livestream`
9. check the video stuttering.
## Cause
When capture screen by the chrome web browser, which send RTP packet
with empty payload frequently, then all the cached RTP packets are
dropped before next key frame arrive in this case.
The OBS screen stream and camera stream do not have such problem.
## Add screen stream to WHIP demo
><img width="581" alt="Screenshot 2024-08-28 at 2 49 46 PM"
src="https://github.com/user-attachments/assets/9557dbd2-c799-4dfd-b336-5bbf2e4f8fb8">
---------
Co-authored-by: winlin <winlinvip@gmail.com>
Please note that the proxy server is a new architecture or the next
version of the Origin Cluster, which allows the publication of multiple
streams. The SRS origin cluster consists of a group of origin servers
designed to handle a large number of streams.
```text
+-----------------------+
+---+ SRS Proxy(Deployment) +------+---------------------+
+-----------------+ | +-----------+-----------+ + +
| LB(K8s Service) +--+ +(Redis/MESH) + SRS Origin Servers +
+-----------------+ | +-----------+-----------+ + (Deployment) +
+---+ SRS Proxy(Deployment) +------+---------------------+
+-----------------------+
```
The new origin cluster is designed as a collection of proxy servers. For
more information, see [Discussion
#3634](https://github.com/ossrs/srs/discussions/3634). If you prefer to
use the old origin cluster, please switch to a version before SRS 6.0.
A proxy server can be used for a set of origin servers, which are
isolated and dedicated origin servers. The main improvement in the new
architecture is to store the state for origin servers in the proxy
server, rather than using MESH to communicate between origin servers.
With a proxy server, you can deploy origin servers as stateless servers,
such as in a Kubernetes (K8s) deployment.
Now that the proxy server is a stateful server, it uses Redis to store
the states. For faster development, we use Go to develop the proxy
server, instead of C/C++. Therefore, the proxy server itself is also
stateless, with all states stored in the Redis server or cluster. This
makes the new origin cluster architecture very powerful and robust.
The proxy server is also an architecture designed to solve multiple
process bottlenecks. You can run hundreds of SRS origin servers with one
proxy server on the same machine. This solution can utilize multi-core
machines, such as servers with 128 CPUs. Thus, we can keep SRS
single-threaded and very simple. See
https://github.com/ossrs/srs/discussions/3665#discussioncomment-6474441
for details.
```text
+--------------------+
+-------+ SRS Origin Server +
+ +--------------------+
+
+-----------------------+ + +--------------------+
+ SRS Proxy(Deployment) +------+-------+ SRS Origin Server +
+-----------------------+ + +--------------------+
+
+ +--------------------+
+-------+ SRS Origin Server +
+--------------------+
```
Keep in mind that the proxy server for the Origin Cluster is designed to
handle many streams. To address the issue of many viewers, we will
enhance the Edge Cluster to support more protocols.
```text
+------------------+ +--------------------+
+ SRS Edge Server +--+ +-------+ SRS Origin Server +
+------------------+ + + +--------------------+
+ +
+------------------+ + +-----------------------+ + +--------------------+
+ SRS Edge Server +--+-----+ SRS Proxy(Deployment) +------+-------+ SRS Origin Server +
+------------------+ + +-----------------------+ + +--------------------+
+ +
+------------------+ + + +--------------------+
+ SRS Edge Server +--+ +-------+ SRS Origin Server +
+------------------+ +--------------------+
```
With the new Origin Cluster and Edge Cluster, you have a media system
capable of supporting a large number of streams and viewers. For
example, you can publish 10,000 streams, each with 100,000 viewers.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
The heartbeat of SRS is a timer that requests an HTTP URL. We can use
this heartbeat to report the necessary information for registering the
backend server with the proxy server.
```text
SRS(backend) --heartbeat---> Proxy server
```
A proxy server is a specialized load balancer for media servers. It
operates at the application level rather than the TCP level. For more
information about the proxy server, see issue #4158.
Note that we will merge this PR into SRS 5.0+, allowing the use of SRS
5.0+ as the backend server, not limited to SRS 7.0. However, the proxy
server is introduced in SRS 7.0.
It's also possible to implement a registration service, allowing you to
use other media servers as backend servers. For example, if you gather
information about an nginx-rtmp server and register it with the proxy
server, the proxy will forward RTMP streams to nginx-rtmp. The backend
server is not limited to SRS.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
1. Do not create a source when mounting FLV because it may not unmount
FLV when freeing the source. If you access the FLV stream without any
publisher, then wait for source cleanup and review the FLV stream again,
there is an annoying warning message.
```bash
# View HTTP FLV stream by curl, wait for stream to be ready.
# curl http://localhost:8080/live/livestream.flv -v >/dev/null
HTTP #0 127.0.0.1:58026 GET http://localhost:8080/live/livestream.flv, content-length=-1
new live source, stream_url=/live/livestream
http: mount flv stream for sid=/live/livestream, mount=/live/livestream.flv
# Cancel the curl and trigger source cleanup without http unmount.
client disconnect peer. ret=1007
Live: cleanup die source, id=[], total=1
# View the stream again, it fails.
# curl http://localhost:8080/live/livestream.flv -v >/dev/null
HTTP #0 127.0.0.1:58040 GET http://localhost:8080/live/livestream.flv, content-length=-1
serve error code=1097(NoSource)(No source found) : process request=0 : cors serve : serve http : no source for /live/livestream
serve_http() [srs_app_http_stream.cpp:641]
```
> Note: There is an inconsistency. The first time, you can access the
FLV stream and wait for the publisher, but the next time, you cannot.
2. Create a source when starting to serve the FLV client. We do not need
to create the source when creating the HTTP handler. Instead, we should
try to create the source in the cache or stream. Because the source
cleanup does not unmount the HTTP handler, the handler remains after the
source is destroyed. The next time you access the FLV stream, the source
is not found.
```cpp
srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) {
SrsSharedPtr<SrsLiveSource> live_source;
if ((err = _srs_sources->fetch_or_create(r.get(), server, live_source)) != srs_success) { }
if ((err = http_mount(r.get())) != srs_success) { }
srs_error_t SrsBufferCache::cycle() {
SrsSharedPtr<SrsLiveSource> live_source = _srs_sources->fetch(req);
if (!live_source.get()) {
return srs_error_new(ERROR_NO_SOURCE, "no source for %s", req->get_stream_url().c_str());
}
srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) {
SrsSharedPtr<SrsLiveSource> live_source = _srs_sources->fetch(req);
if (!live_source.get()) {
return srs_error_new(ERROR_NO_SOURCE, "no source for %s", req->get_stream_url().c_str());
}
```
> Note: We should not create the source in hijack, instead, we create it
in cache or stream:
```cpp
srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) {
if ((err = http_mount(r.get())) != srs_success) { }
srs_error_t SrsBufferCache::cycle() {
SrsSharedPtr<SrsLiveSource> live_source;
if ((err = _srs_sources->fetch_or_create(req, server_, live_source)) != srs_success) { }
srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) {
SrsSharedPtr<SrsLiveSource> live_source;
if ((err = _srs_sources->fetch_or_create(req, server_, live_source)) != srs_success) { }
```
> Note: This fixes the failure and annoying warning message, and
maintains consistency by always waiting for the stream to be ready if
there is no publisher.
3. Fail the http request if the HTTP handler is disposing, and also keep
the handler entry when disposing the stream, because we should dispose
the handler entry and stream at the same time.
```cpp
srs_error_t SrsHttpStreamServer::http_mount(SrsRequest* r) {
entry = streamHandlers[sid];
if (entry->disposing) {
return srs_error_new(ERROR_STREAM_DISPOSING, "stream is disposing");
}
void SrsHttpStreamServer::http_unmount(SrsRequest* r) {
std::map<std::string, SrsLiveEntry*>::iterator it = streamHandlers.find(sid);
SrsUniquePtr<SrsLiveEntry> entry(it->second);
entry->disposing = true;
```
> Note: If the disposal process takes a long time, this will prevent
unexpected behavior or access to the resource that is being disposed of.
4. In edge mode, the edge ingester will unpublish the source when the
last consumer quits, which is actually triggered by the HTTP stream.
While it also waits for the stream to quit when the HTTP unmounts, there
is a self-destruction risk: the HTTP live stream object destroys itself.
```cpp
srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) {
SrsUniquePtr<SrsLiveConsumer> consumer(consumer_raw); // Trigger destroy.
void SrsHttpStreamServer::http_unmount(SrsRequest* r) {
for (;;) { if (!cache->alive() && !stream->alive()) { break; } // A circle reference.
mux.unhandle(entry->mount, stream.get()); // Free the SrsLiveStream itself.
```
> Note: It also introduces a circular reference in the object
relationships, the stream reference to itself when unmount:
```text
SrsLiveStream::serve_http
-> SrsLiveConsumer::~SrsLiveConsumer -> SrsEdgeIngester::stop
-> SrsLiveSource::on_unpublish -> SrsHttpStreamServer::http_unmount
-> SrsLiveStream::alive
```
> Note: We should use an asynchronous worker to perform the cleanup to
avoid the stream destroying itself and to prevent self-referencing.
```cpp
void SrsHttpStreamServer::http_unmount(SrsRequest* r) {
entry->disposing = true;
if ((err = async_->execute(new SrsHttpStreamDestroy(&mux, &streamHandlers, sid))) != srs_success) { }
```
> Note: This also ensures there are no circular references and no
self-destruction.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
Edge FLV is not working because it is stuck in an infinite loop waiting.
Previously, there was no need to wait for exit since resources were not
being cleaned up. Now, since resources need to be cleaned up, it must
wait for all active connections to exit, which causes this issue.
To reproduce the issue, start SRS edge, run the bellow command and press
`CTRL+C` to stop the request:
```bash
curl http://localhost:8080/live/livestream.flv -v >/dev/null
```
It will cause edge to fetch stream from origin, and free the consumer
when client quit. When `SrsLiveStream::do_serve_http` return, it will
free the consumer:
```cpp
srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) {
SrsUniquePtr<SrsLiveConsumer> consumer(consumer_raw);
```
Keep in mind that in this moment, the stream is alive, because only set
to not alive after this function return:
```cpp
alive_viewers_++;
err = do_serve_http(w, r); // Free 'this' alive stream.
alive_viewers_--; // Crash here, because 'this' is freed.
```
When freeing the consumer, it will cause the source to unpublish and
attempt to free the HTTP handler, which ultimately waits for the stream
not to be alive:
```cpp
SrsLiveConsumer::~SrsLiveConsumer() {
source_->on_consumer_destroy(this);
void SrsLiveSource::on_consumer_destroy(SrsLiveConsumer* consumer) {
if (consumers.empty()) {
play_edge->on_all_client_stop();
void SrsLiveSource::on_unpublish() {
handler->on_unpublish(req);
void SrsHttpStreamServer::http_unmount(SrsRequest* r) {
if (stream->entry) stream->entry->enabled = false;
for (; i < 1024; i++) {
if (!cache->alive() && !stream->alive()) {
break;
}
srs_usleep(100 * SRS_UTIME_MILLISECONDS);
}
```
After 120 seconds, it will free the stream and cause SRS to crash
because the stream is still active. In order to track this potential
issue, also add an important warning log:
```cpp
srs_warn("http: try to free a alive stream, cache=%d, stream=%d", cache->alive(), stream->alive());
```
SRS may crash if got this log.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
When stopping the stream, it will wait for the HTTP Streaming to exit.
If the HTTP Streaming goroutine hangs, it will not exit automatically.
```cpp
void SrsHttpStreamServer::http_unmount(SrsRequest* r)
{
SrsUniquePtr<SrsLiveStream> stream(entry->stream);
if (stream->entry) stream->entry->enabled = false;
srs_usleep(...); // Wait for about 120s.
mux.unhandle(entry->mount, stream.get()); // Free stream.
}
srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
err = do_serve_http(w, r); // If stuck in here for 120s+
alive_viewers_--; // Crash at here, because stream has been deleted.
```
We should notify http stream connection to interrupt(expire):
```cpp
void SrsHttpStreamServer::http_unmount(SrsRequest* r)
{
SrsUniquePtr<SrsLiveStream> stream(entry->stream);
if (stream->entry) stream->entry->enabled = false;
stream->expire(); // Notify http stream to interrupt.
```
Note that we should notify all viewers pulling stream from this http
stream.
Note that we have tried to fix this issue, but only try to wait for all
viewers to quit, without interrupting the viewers, see
https://github.com/ossrs/srs/pull/4144
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
1. Remove the srs_global_dispose, which causes the crash when still
publishing when quit.
2. Always call _srs_thread_pool->initialize for single thread.
3. Support `--signal-api` to send signal by HTTP API, because CLion
eliminate the signals.
---
Co-authored-by: Jacob Su <suzp1984@gmail.com>
New features for valgrind:
1. ST: Support /api/v1/valgrind for leaking check.
2. ST: Support /api/v1/valgrind?check=full|added|changed|new|quick
To use Valgrind to detect memory leaks in SRS, even though Valgrind
hooks are supported in ST, there are still many false positives. A more
reasonable approach is to have Valgrind report incremental memory leaks.
This way, global and static variables can be avoided, and detection can
be achieved without exiting the program. Follow these steps:
1. Compile SRS with Valgrind support: `./configure --valgrind=on &&
make`
2. Start SRS with memory leak detection enabled: `valgrind
--leak-check=full ./objs/srs -c conf/console.conf`
3. Trigger memory detection by using curl to access the API and generate
calibration data. There will still be many false positives, but these
can be ignored: `curl http://127.0.0.1:1985/api/v1/valgrind?check=added`
4. Perform load testing or test the suspected leaking functionality,
such as RTMP streaming: `ffmpeg -re -i doc/source.flv -c copy -f flv
rtmp://127.0.0.1/live/livestream`
5. Stop streaming and wait for SRS to clean up the Source memory,
approximately 30 seconds.
6. Perform incremental memory leak detection. The reported leaks will be
very accurate at this point: `curl
http://127.0.0.1:1985/api/v1/valgrind?check=added`
> Note: To avoid interference from the HTTP request itself on Valgrind,
SRS uses a separate coroutine to perform periodic checks. Therefore,
after accessing the API, you may need to wait a few seconds for the
detection to be triggered.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
I did some preliminary code inspection. The two playback endpoints share
the same `SrsLiveStream` instance. After the first one disconnects,
`alive_` is set to false.
```
alive_ = true;
err = do_serve_http(w, r);
alive_ = false;
```
In the `SrsHttpStreamServer::http_unmount(SrsRequest* r)` function,
`stream->alive()` is already false, so `mux.unhandle` will free the
`SrsLiveStream`. This causes the other connection coroutine to return to
its execution environment after the `SrsLiveStream` instance has already
been freed.
```
// Wait for cache and stream to stop.
int i = 0;
for (; i < 1024; i++) {
if (!cache->alive() && !stream->alive()) {
break;
}
srs_usleep(100 * SRS_UTIME_MILLISECONDS);
}
// Unmount the HTTP handler, which will free the entry. Note that we must free it after cache and
// stream stopped for it uses it.
mux.unhandle(entry->mount, stream.get());
```
`alive_` was changed from a `bool` to an `int` to ensure that
`mux.unhandle` is only executed after each connection's `serve_http` has
exited.
---------
Co-authored-by: liumengte <liumengte@visionular.com>
Co-authored-by: winlin <winlinvip@gmail.com>
1. don't use static variable to store the result;
2. add more UT to handle the multi value and values with whitespaces;
related to #409216e569d823/trunk/src/app/srs_app_config.cpp (L71-L82)
`static SrsConfDirective* dir` removed, this static var here is to avoid
the memory leak, I add the `SrsConfDirective` instance to the `env_dirs`
directive container, which will destroy itself inside `SrsConfig`
destructor.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
When unpublishing, the handler callback that will stop the coroutine:
```cpp
_can_publish = true;
handler->on_unpublish(req);
```
In this handler, the `http_unmount` will be called:
```cpp
void SrsHttpStreamServer::http_unmount(SrsRequest* r)
cache->stop();
```
In this `http_unmount` function, there could be context switching. In
such a situation, a new connection might publish the stream while the
unpublish process is freeing the stream, leading to a crash.
To prevent a new publisher, we should change the state only after all
handlers and hooks are completed.
---------
Co-authored-by: liumengte <liumengte@visionular.com>
Co-authored-by: winlin <winlinvip@gmail.com>
1. add on_connect & on_close directives to conf/full.conf;
2. let http_hooks env overwrite support multi values; e.g.
SRS_VHOST_HTTP_HOOKS_ON_CONNECT="http://127.0.0.1/api/connecthttp://localhost/api/connect"
related to
https://github.com/ossrs/srs/issues/1222#issuecomment-2170424703
Above comments said `http_hook` env may not works as expected, as I
found there are still has some issue in `http_hooks` env configuration,
but this PR may not target above problem.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
Sorry this is another pull request with same intention. But there is
more variants of h264 und h265 codecs and I think it is good to support
them all.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
Currently only libx264 ffmpeg encoder is supported. This pull request
add also h264_qsv. But maybe a more generic solution with oder encoders
would be useful to.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
For #3369 to support an external powerful SIP server, do not use the
embedded SIP server of SRS.
For more information, detailed steps, system architecture, and
background explanation, please see
https://ossrs.net/lts/zh-cn/docs/v6/doc/gb28181#external-sip
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
Co-authored-by: winlin <winlinvip@gmail.com>
The session HLS manifest file lacks a terminating newline in the final
line.
This may cause strict players to reject it.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
## Describe ##
http_remux feature support config `has_audio`, `has_video` &
`guess_has_av` prop.
282d94d7bb/trunk/src/app/srs_app_http_stream.cpp (L630-L632)
Take `http_flv` as example, `srs` can accept both RTMP streams with only
audio, only video or both audio and video streams. It is controlled by
above three properties.
But `guess_has_av` is not implemented by `http_ts`. The problem is that
if I want publish a RTMP stream with audio or video track, the
`has_audio` and `has_video`, which are default true/on, must to be
config to match the RTMP stream, otherwise the `mpegts.js` player can't
play the `http-ts` stream.
## How to reproduce ##
1. `export SRS_VHOST_HTTP_REMUX_HAS_AUDIO=on; export
SRS_VHOST_HTTP_REMUX_HAS_VIDEO=on; export
SRS_VHOST_HTTP_REMUX_GUESS_HAS_AV=on; ./objs/srs -c
conf/http.ts.live.conf`
2. publish rtmp stream without video: `ffmpeg -re -stream_loop -1 -i
srs/trunk/doc/source.200kbps.768x320.flv -vn -acodec copy -f flv
rtmp://localhost/live/livestream`
3. open chrome browser, open
`http://localhost:8080/players/srs_player.html?schema=http`, go to
`LivePlayer`, input URL: `http://localhost:8080/live/livestream.ts`,
click play.
4. the `http://localhost:8080/live/livestream.ts` can not play.
## Solution ##
Let `http-ts` support `guess_has_av`, `http-flv` already supported. The
`guess_has_av` default value is ture/on, so the `http-ts|flv` can play
any streams with audio, video or both.
---------
Co-authored-by: Winlin <winlinvip@gmail.com>
1. Should always stop coroutine before close fd, see #511, #1784
2. When edge forwarder coroutine quit, always set the error code.
3. Do not unpublish if invalid state.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
## Cause
dash auto dispose is configured by seconds, but the code compare by
usecond, 1 second = 1,000,000 useconds.
releated to #4097
Bug introduced after #4097 supported Dash auto dispose after a timeout
without media data.
## How to reproduce
1. `./objs/srs -c conf/dash.conf`
2. publish a rtmp stream.
3. play dash stream. -> no dash stream, always 404 error.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
To manage an object:
```cpp
// Before
MyClass* ptr = new MyClass();
SrsAutoFree(MyClass, ptr);
ptr->do_something();
// Now
SrsUniquePtr<MyClass> ptr(new MyClass());
ptr->do_something();
```
To manage an array of objects:
```cpp
// Before
char* ptr = new char[10];
SrsAutoFreeA(char, ptr);
ptr[0] = 0xf;
// Now
SrsUniquePtr<char[]> ptr(new char[10]);
ptr[0] = 0xf;
```
In fact, SrsUniquePtr is a limited subset of SrsAutoFree, mainly
managing pointers and arrays. SrsUniquePtr is better than SrsAutoFree
because it has the same API to standard unique ptr.
```cpp
SrsUniquePtr<MyClass> ptr(new MyClass());
ptr->do_something();
MyClass* p = ptr.get();
```
SrsAutoFree actually uses a pointer to a pointer, so it can be set to
NULL, allowing the pointer's value to be changed later (this usage is
different from SrsUniquePtr).
```cpp
// OK to free ptr correctly.
MyClass* ptr;
SrsAutoFree(MyClass, ptr);
ptr = new MyClass();
// Crash because ptr is an invalid pointer.
MyClass* ptr;
SrsUniquePtr<MyClass> ptr(ptr);
ptr = new MyClass();
```
Additionally, SrsAutoFreeH can use specific release functions, which
SrsUniquePtr does not support.
---------
Co-authored-by: Jacob Su <suzp1984@gmail.com>
1. fix redundant null check, there is no potential risks by the way,
just redundant null check.
2. Potential use pointer after free, that's not true. So we can ignore
this one, or find a way to make stupid security tool happy.
---------
Co-authored-by: winlin <winlinvip@gmail.com>
1. Add live benchmark support in srs-bench, which only connects and
disconnects without any media transport, to test source creation and
disposal and verify source memory leaks.
2. SmartPtr: Support cleanup of HTTP-FLV stream. Unregister the HTTP-FLV
handler for the pattern and clean up the objects and resources.
3. Support benchmarking RTMP/SRT with srs-bench by integrating the gosrt
and oryx RTMP libraries.
4. Refine SRT and RTC sources by using a timer to clean up the sources,
following the same strategy as the Live source.
---------
Co-authored-by: Haibo Chen <495810242@qq.com>
Co-authored-by: Jacob Su <suzp1984@gmail.com>