mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
SmartPtr: Support load test for source by srs-bench. v6.0.130 (#4097)
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>
This commit is contained in:
parent
e3d74fb045
commit
1f9309ae25
508 changed files with 6805 additions and 3299 deletions
|
@ -6863,7 +6863,7 @@ srs_utime_t SrsConfig::get_dash_dispose(std::string vhost)
|
|||
{
|
||||
SRS_OVERWRITE_BY_ENV_SECONDS("srs.vhost.dash.dash_dispose"); // SRS_VHOST_DASH_DASH_DISPOSE
|
||||
|
||||
static srs_utime_t DEFAULT = 0;
|
||||
static srs_utime_t DEFAULT = 120;
|
||||
|
||||
SrsConfDirective* conf = get_dash(vhost);
|
||||
if (!conf) {
|
||||
|
|
|
@ -705,6 +705,10 @@ void SrsDash::dispose()
|
|||
srs_error_t SrsDash::cycle()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!enabled) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (last_update_time_ <= 0) {
|
||||
last_update_time_ = srs_get_system_time();
|
||||
|
@ -734,6 +738,16 @@ srs_error_t SrsDash::cycle()
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_utime_t SrsDash::cleanup_delay()
|
||||
{
|
||||
if (!enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We use larger timeout to cleanup the HLS, after disposed it if required.
|
||||
return _srs_config->get_dash_dispose(req->vhost) * 1.1;
|
||||
}
|
||||
|
||||
srs_error_t SrsDash::initialize(SrsOriginHub* h, SrsRequest* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
|
|
@ -162,6 +162,7 @@ public:
|
|||
public:
|
||||
virtual void dispose();
|
||||
virtual srs_error_t cycle();
|
||||
srs_utime_t cleanup_delay();
|
||||
public:
|
||||
// Initalize the encoder.
|
||||
virtual srs_error_t initialize(SrsOriginHub* h, SrsRequest* r);
|
||||
|
|
|
@ -1237,6 +1237,10 @@ srs_error_t SrsHls::cycle()
|
|||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if (!enabled) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (last_update_time <= 0) {
|
||||
last_update_time = srs_get_system_time();
|
||||
}
|
||||
|
@ -1272,6 +1276,16 @@ srs_error_t SrsHls::cycle()
|
|||
return err;
|
||||
}
|
||||
|
||||
srs_utime_t SrsHls::cleanup_delay()
|
||||
{
|
||||
if (!enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We use larger timeout to cleanup the HLS, after disposed it if required.
|
||||
return _srs_config->get_hls_dispose(req->vhost) * 1.1;
|
||||
}
|
||||
|
||||
srs_error_t SrsHls::initialize(SrsOriginHub* h, SrsRequest* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
|
|
@ -313,6 +313,7 @@ private:
|
|||
public:
|
||||
virtual void dispose();
|
||||
virtual srs_error_t cycle();
|
||||
srs_utime_t cleanup_delay();
|
||||
public:
|
||||
// Initialize the hls by handler and source.
|
||||
virtual srs_error_t initialize(SrsOriginHub* h, SrsRequest* r);
|
||||
|
|
|
@ -77,6 +77,22 @@ srs_error_t SrsBufferCache::start()
|
|||
return err;
|
||||
}
|
||||
|
||||
void SrsBufferCache::stop()
|
||||
{
|
||||
trd->stop();
|
||||
}
|
||||
|
||||
bool SrsBufferCache::alive()
|
||||
{
|
||||
srs_error_t err = trd->pull();
|
||||
if (err == srs_success) {
|
||||
return true;
|
||||
}
|
||||
|
||||
srs_freep(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
srs_error_t SrsBufferCache::dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -561,6 +577,7 @@ SrsLiveStream::SrsLiveStream(SrsRequest* r, SrsBufferCache* c)
|
|||
cache = c;
|
||||
req = r->copy()->as_http();
|
||||
security_ = new SrsSecurity();
|
||||
alive_ = false;
|
||||
}
|
||||
|
||||
SrsLiveStream::~SrsLiveStream()
|
||||
|
@ -610,14 +627,21 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
|
|||
if ((err = http_hooks_on_play(r)) != srs_success) {
|
||||
return srs_error_wrap(err, "http hook");
|
||||
}
|
||||
|
||||
|
||||
alive_ = true;
|
||||
err = do_serve_http(w, r);
|
||||
alive_ = false;
|
||||
|
||||
http_hooks_on_stop(r);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool SrsLiveStream::alive()
|
||||
{
|
||||
return alive_;
|
||||
}
|
||||
|
||||
srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -929,19 +953,19 @@ SrsHttpStreamServer::~SrsHttpStreamServer()
|
|||
|
||||
if (true) {
|
||||
std::map<std::string, SrsLiveEntry*>::iterator it;
|
||||
for (it = tflvs.begin(); it != tflvs.end(); ++it) {
|
||||
for (it = templateHandlers.begin(); it != templateHandlers.end(); ++it) {
|
||||
SrsLiveEntry* entry = it->second;
|
||||
srs_freep(entry);
|
||||
}
|
||||
tflvs.clear();
|
||||
templateHandlers.clear();
|
||||
}
|
||||
if (true) {
|
||||
std::map<std::string, SrsLiveEntry*>::iterator it;
|
||||
for (it = sflvs.begin(); it != sflvs.end(); ++it) {
|
||||
for (it = streamHandlers.begin(); it != streamHandlers.end(); ++it) {
|
||||
SrsLiveEntry* entry = it->second;
|
||||
srs_freep(entry);
|
||||
}
|
||||
sflvs.clear();
|
||||
streamHandlers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -967,12 +991,12 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsRequest* r)
|
|||
SrsLiveEntry* entry = NULL;
|
||||
|
||||
// create stream from template when not found.
|
||||
if (sflvs.find(sid) == sflvs.end()) {
|
||||
if (tflvs.find(r->vhost) == tflvs.end()) {
|
||||
if (streamHandlers.find(sid) == streamHandlers.end()) {
|
||||
if (templateHandlers.find(r->vhost) == templateHandlers.end()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsLiveEntry* tmpl = tflvs[r->vhost];
|
||||
SrsLiveEntry* tmpl = templateHandlers[r->vhost];
|
||||
|
||||
std::string mount = tmpl->mount;
|
||||
|
||||
|
@ -999,16 +1023,16 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsRequest* r)
|
|||
srs_freep(tmpl->req);
|
||||
|
||||
tmpl->req = r->copy()->as_http();
|
||||
|
||||
sflvs[sid] = entry;
|
||||
|
||||
|
||||
streamHandlers[sid] = entry;
|
||||
|
||||
// mount the http flv stream.
|
||||
// we must register the handler, then start the thread,
|
||||
// for the thread will cause thread switch context.
|
||||
if ((err = mux.handle(mount, entry->stream)) != srs_success) {
|
||||
return srs_error_wrap(err, "http: mount flv stream for vhost=%s failed", sid.c_str());
|
||||
}
|
||||
|
||||
|
||||
// start http stream cache thread
|
||||
if ((err = entry->cache->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "http: start stream cache failed");
|
||||
|
@ -1016,7 +1040,7 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsRequest* r)
|
|||
srs_trace("http: mount flv stream for sid=%s, mount=%s", sid.c_str(), mount.c_str());
|
||||
} else {
|
||||
// The entry exists, we reuse it and update the request of stream and cache.
|
||||
entry = sflvs[sid];
|
||||
entry = streamHandlers[sid];
|
||||
entry->stream->update_auth(r);
|
||||
entry->cache->update_auth(r);
|
||||
}
|
||||
|
@ -1032,13 +1056,40 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsRequest* r)
|
|||
void SrsHttpStreamServer::http_unmount(SrsRequest* r)
|
||||
{
|
||||
std::string sid = r->get_stream_url();
|
||||
|
||||
if (sflvs.find(sid) == sflvs.end()) {
|
||||
|
||||
std::map<std::string, SrsLiveEntry*>::iterator it = streamHandlers.find(sid);
|
||||
if (it == streamHandlers.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SrsLiveEntry* entry = sflvs[sid];
|
||||
entry->stream->entry->enabled = false;
|
||||
|
||||
// Free all HTTP resources.
|
||||
SrsLiveEntry* entry = it->second;
|
||||
SrsAutoFree(SrsLiveEntry, entry);
|
||||
streamHandlers.erase(it);
|
||||
|
||||
SrsLiveStream* stream = entry->stream;
|
||||
SrsAutoFree(SrsLiveStream, stream);
|
||||
|
||||
SrsBufferCache* cache = entry->cache;
|
||||
SrsAutoFree(SrsBufferCache, cache);
|
||||
|
||||
// Unmount the HTTP handler.
|
||||
mux.unhandle(entry->mount, stream);
|
||||
|
||||
// Notify cache and stream to stop.
|
||||
if (stream->entry) stream->entry->enabled = false;
|
||||
cache->stop();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
srs_trace("http: unmount flv stream for sid=%s, i=%d", sid.c_str(), i);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph)
|
||||
|
@ -1067,8 +1118,8 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle
|
|||
SrsLiveEntry* entry = NULL;
|
||||
if (true) {
|
||||
// no http streaming on vhost, ignore.
|
||||
std::map<std::string, SrsLiveEntry*>::iterator it = tflvs.find(vhost->arg0());
|
||||
if (it == tflvs.end()) {
|
||||
std::map<std::string, SrsLiveEntry*>::iterator it = templateHandlers.find(vhost->arg0());
|
||||
if (it == templateHandlers.end()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1124,8 +1175,8 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle
|
|||
std::string sid = r->get_stream_url();
|
||||
// check whether the http remux is enabled,
|
||||
// for example, user disable the http flv then reload.
|
||||
if (sflvs.find(sid) != sflvs.end()) {
|
||||
SrsLiveEntry* s_entry = sflvs[sid];
|
||||
if (streamHandlers.find(sid) != streamHandlers.end()) {
|
||||
SrsLiveEntry* s_entry = streamHandlers[sid];
|
||||
if (!s_entry->stream->entry->enabled) {
|
||||
// only when the http entry is disabled, check the config whether http flv disable,
|
||||
// for the http flv edge use hijack to trigger the edge ingester, we always mount it
|
||||
|
@ -1154,8 +1205,8 @@ srs_error_t SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandle
|
|||
|
||||
// use the handler if exists.
|
||||
if (ph) {
|
||||
if (sflvs.find(sid) != sflvs.end()) {
|
||||
entry = sflvs[sid];
|
||||
if (streamHandlers.find(sid) != streamHandlers.end()) {
|
||||
entry = streamHandlers[sid];
|
||||
*ph = entry->stream;
|
||||
}
|
||||
}
|
||||
|
@ -1198,8 +1249,8 @@ srs_error_t SrsHttpStreamServer::initialize_flv_entry(std::string vhost)
|
|||
}
|
||||
|
||||
SrsLiveEntry* entry = new SrsLiveEntry(_srs_config->get_vhost_http_remux_mount(vhost));
|
||||
|
||||
tflvs[vhost] = entry;
|
||||
|
||||
templateHandlers[vhost] = entry;
|
||||
srs_trace("http flv live stream, vhost=%s, mount=%s", vhost.c_str(), entry->mount.c_str());
|
||||
|
||||
return err;
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
virtual srs_error_t update_auth(SrsRequest* r);
|
||||
public:
|
||||
virtual srs_error_t start();
|
||||
virtual void stop();
|
||||
virtual bool alive();
|
||||
virtual srs_error_t dump_cache(SrsLiveConsumer* consumer, SrsRtmpJitterAlgorithm jitter);
|
||||
// Interface ISrsEndlessThreadHandler.
|
||||
public:
|
||||
|
@ -179,12 +181,14 @@ private:
|
|||
SrsRequest* req;
|
||||
SrsBufferCache* cache;
|
||||
SrsSecurity* security_;
|
||||
bool alive_;
|
||||
public:
|
||||
SrsLiveStream(SrsRequest* r, SrsBufferCache* c);
|
||||
virtual ~SrsLiveStream();
|
||||
virtual srs_error_t update_auth(SrsRequest* r);
|
||||
public:
|
||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||
virtual bool alive();
|
||||
private:
|
||||
virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||
virtual srs_error_t http_hooks_on_play(ISrsHttpMessage* r);
|
||||
|
@ -230,9 +234,9 @@ private:
|
|||
public:
|
||||
SrsHttpServeMux mux;
|
||||
// The http live streaming template, to create streams.
|
||||
std::map<std::string, SrsLiveEntry*> tflvs;
|
||||
// The http live streaming streams, crote by template.
|
||||
std::map<std::string, SrsLiveEntry*> sflvs;
|
||||
std::map<std::string, SrsLiveEntry*> templateHandlers;
|
||||
// The http live streaming streams, created by template.
|
||||
std::map<std::string, SrsLiveEntry*> streamHandlers;
|
||||
public:
|
||||
SrsHttpStreamServer(SrsServer* svr);
|
||||
virtual ~SrsHttpStreamServer();
|
||||
|
|
|
@ -71,6 +71,9 @@ using namespace std;
|
|||
const int kRtpMaxPayloadSize = kRtpPacketSize - 300;
|
||||
#endif
|
||||
|
||||
// the time to cleanup source.
|
||||
#define SRS_RTC_SOURCE_CLEANUP (3 * SRS_UTIME_SECONDS)
|
||||
|
||||
// TODO: Add this function into SrsRtpMux class.
|
||||
srs_error_t aac_raw_append_adts_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format, char** pbuf, int* pnn_buf)
|
||||
{
|
||||
|
@ -244,11 +247,56 @@ void SrsRtcConsumer::on_stream_change(SrsRtcSourceDescription* desc)
|
|||
SrsRtcSourceManager::SrsRtcSourceManager()
|
||||
{
|
||||
lock = srs_mutex_new();
|
||||
timer_ = new SrsHourGlass("sources", this, 1 * SRS_UTIME_SECONDS);
|
||||
}
|
||||
|
||||
SrsRtcSourceManager::~SrsRtcSourceManager()
|
||||
{
|
||||
srs_mutex_destroy(lock);
|
||||
srs_freep(timer_);
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSourceManager::initialize()
|
||||
{
|
||||
return setup_ticks();
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSourceManager::setup_ticks()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = timer_->tick(1, 3 * SRS_UTIME_SECONDS)) != srs_success) {
|
||||
return srs_error_wrap(err, "tick");
|
||||
}
|
||||
|
||||
if ((err = timer_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "timer");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSourceManager::notify(int event, srs_utime_t interval, srs_utime_t tick)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::map< std::string, SrsSharedPtr<SrsRtcSource> >::iterator it;
|
||||
for (it = pool.begin(); it != pool.end();) {
|
||||
SrsSharedPtr<SrsRtcSource>& source = it->second;
|
||||
|
||||
// When source expired, remove it.
|
||||
// @see https://github.com/ossrs/srs/issues/713
|
||||
if (source->stream_is_dead()) {
|
||||
SrsContextId cid = source->source_id();
|
||||
if (cid.empty()) cid = source->pre_source_id();
|
||||
srs_trace("RTC: cleanup die source, id=[%s], total=%d", cid.c_str(), (int)pool.size());
|
||||
pool.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSourceManager::fetch_or_create(SrsRequest* r, SrsSharedPtr<SrsRtcSource>& pps)
|
||||
|
@ -305,19 +353,6 @@ SrsSharedPtr<SrsRtcSource> SrsRtcSourceManager::fetch(SrsRequest* r)
|
|||
return source;
|
||||
}
|
||||
|
||||
void SrsRtcSourceManager::eliminate(SrsRequest* r)
|
||||
{
|
||||
// Use lock to protect coroutine switch.
|
||||
// @bug https://github.com/ossrs/srs/issues/1230
|
||||
SrsLocker(lock);
|
||||
|
||||
string stream_url = r->get_stream_url();
|
||||
std::map< std::string, SrsSharedPtr<SrsRtcSource> >::iterator it = pool.find(stream_url);
|
||||
if (it != pool.end()) {
|
||||
pool.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
SrsRtcSourceManager* _srs_rtc_sources = NULL;
|
||||
|
||||
ISrsRtcPublishStream::ISrsRtcPublishStream()
|
||||
|
@ -351,6 +386,7 @@ SrsRtcSource::SrsRtcSource()
|
|||
#endif
|
||||
|
||||
pli_for_rtmp_ = pli_elapsed_ = 0;
|
||||
stream_die_at_ = 0;
|
||||
}
|
||||
|
||||
SrsRtcSource::~SrsRtcSource()
|
||||
|
@ -365,6 +401,10 @@ SrsRtcSource::~SrsRtcSource()
|
|||
srs_freep(bridge_);
|
||||
srs_freep(req);
|
||||
srs_freep(stream_desc_);
|
||||
|
||||
SrsContextId cid = _source_id;
|
||||
if (cid.empty()) cid = _pre_source_id;
|
||||
srs_trace("free rtc source id=[%s]", cid.c_str());
|
||||
}
|
||||
|
||||
srs_error_t SrsRtcSource::initialize(SrsRequest* r)
|
||||
|
@ -380,6 +420,27 @@ srs_error_t SrsRtcSource::initialize(SrsRequest* r)
|
|||
return err;
|
||||
}
|
||||
|
||||
bool SrsRtcSource::stream_is_dead()
|
||||
{
|
||||
// still publishing?
|
||||
if (is_created_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// has any consumers?
|
||||
if (!consumers.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delay cleanup source.
|
||||
srs_utime_t now = srs_get_system_time();
|
||||
if (now < stream_die_at_ + SRS_RTC_SOURCE_CLEANUP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SrsRtcSource::init_for_play_before_publishing()
|
||||
{
|
||||
// If the stream description has already been setup by RTC publisher,
|
||||
|
@ -493,6 +554,8 @@ srs_error_t SrsRtcSource::create_consumer(SrsRtcConsumer*& consumer)
|
|||
consumer = new SrsRtcConsumer(this);
|
||||
consumers.push_back(consumer);
|
||||
|
||||
stream_die_at_ = 0;
|
||||
|
||||
// TODO: FIXME: Implements edge cluster.
|
||||
|
||||
return err;
|
||||
|
@ -526,7 +589,7 @@ void SrsRtcSource::on_consumer_destroy(SrsRtcConsumer* consumer)
|
|||
|
||||
// Destroy and cleanup source when no publishers and consumers.
|
||||
if (!is_created_ && consumers.empty()) {
|
||||
_srs_rtc_sources->eliminate(req);
|
||||
stream_die_at_ = srs_get_system_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -629,8 +692,8 @@ void SrsRtcSource::on_unpublish()
|
|||
stat->on_stream_close(req);
|
||||
|
||||
// Destroy and cleanup source when no publishers and consumers.
|
||||
if (!is_created_ && consumers.empty()) {
|
||||
_srs_rtc_sources->eliminate(req);
|
||||
if (consumers.empty()) {
|
||||
stream_die_at_ = srs_get_system_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,14 +111,21 @@ public:
|
|||
void on_stream_change(SrsRtcSourceDescription* desc);
|
||||
};
|
||||
|
||||
class SrsRtcSourceManager
|
||||
class SrsRtcSourceManager : public ISrsHourGlass
|
||||
{
|
||||
private:
|
||||
srs_mutex_t lock;
|
||||
std::map< std::string, SrsSharedPtr<SrsRtcSource> > pool;
|
||||
SrsHourGlass* timer_;
|
||||
public:
|
||||
SrsRtcSourceManager();
|
||||
virtual ~SrsRtcSourceManager();
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
// interface ISrsHourGlass
|
||||
private:
|
||||
virtual srs_error_t setup_ticks();
|
||||
virtual srs_error_t notify(int event, srs_utime_t interval, srs_utime_t tick);
|
||||
public:
|
||||
// create source when fetch from cache failed.
|
||||
// @param r the client request.
|
||||
|
@ -127,9 +134,6 @@ public:
|
|||
public:
|
||||
// Get the exists source, NULL when not exists.
|
||||
virtual SrsSharedPtr<SrsRtcSource> fetch(SrsRequest* r);
|
||||
public:
|
||||
// Dispose and destroy the source.
|
||||
virtual void eliminate(SrsRequest* r);
|
||||
};
|
||||
|
||||
// Global singleton instance.
|
||||
|
@ -195,11 +199,17 @@ private:
|
|||
// The PLI for RTC2RTMP.
|
||||
srs_utime_t pli_for_rtmp_;
|
||||
srs_utime_t pli_elapsed_;
|
||||
private:
|
||||
// The last die time, while die means neither publishers nor players.
|
||||
srs_utime_t stream_die_at_;
|
||||
public:
|
||||
SrsRtcSource();
|
||||
virtual ~SrsRtcSource();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsRequest* r);
|
||||
public:
|
||||
// Whether stream is dead, which is no publisher or player.
|
||||
virtual bool stream_is_dead();
|
||||
private:
|
||||
void init_for_play_before_publishing();
|
||||
public:
|
||||
|
|
|
@ -40,10 +40,14 @@ using namespace std;
|
|||
#ifdef SRS_RTC
|
||||
#include <srs_app_rtc_network.hpp>
|
||||
#include <srs_app_rtc_server.hpp>
|
||||
#include <srs_app_rtc_source.hpp>
|
||||
#endif
|
||||
#ifdef SRS_GB28181
|
||||
#include <srs_app_gb28181.hpp>
|
||||
#endif
|
||||
#ifdef SRS_SRT
|
||||
#include <srs_app_srt_source.hpp>
|
||||
#endif
|
||||
|
||||
SrsSignalManager* SrsSignalManager::instance = NULL;
|
||||
|
||||
|
@ -806,9 +810,21 @@ srs_error_t SrsServer::start(SrsWaitGroup* wg)
|
|||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = _srs_sources->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "sources");
|
||||
return srs_error_wrap(err, "live sources");
|
||||
}
|
||||
|
||||
#ifdef SRS_SRT
|
||||
if ((err = _srs_srt_sources->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "srt sources");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SRS_RTC
|
||||
if ((err = _srs_rtc_sources->initialize()) != srs_success) {
|
||||
return srs_error_wrap(err, "rtc sources");
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((err = trd_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "start");
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ using namespace std;
|
|||
#define SRS_MIX_CORRECT_PURE_AV 10
|
||||
|
||||
// the time to cleanup source.
|
||||
#define SRS_SOURCE_CLEANUP (30 * SRS_UTIME_SECONDS)
|
||||
#define SRS_SOURCE_CLEANUP (3 * SRS_UTIME_SECONDS)
|
||||
|
||||
int srs_time_jitter_string2int(std::string time_jitter)
|
||||
{
|
||||
|
@ -910,6 +910,13 @@ bool SrsOriginHub::active()
|
|||
return is_active;
|
||||
}
|
||||
|
||||
srs_utime_t SrsOriginHub::cleanup_delay()
|
||||
{
|
||||
srs_utime_t hls_delay = hls->cleanup_delay();
|
||||
srs_utime_t dash_delay = dash->cleanup_delay();
|
||||
return srs_max(hls_delay, dash_delay);
|
||||
}
|
||||
|
||||
srs_error_t SrsOriginHub::on_meta_data(SrsSharedPtrMessage* shared_metadata, SrsOnMetaDataPacket* packet)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -1827,7 +1834,7 @@ srs_error_t SrsLiveSourceManager::setup_ticks()
|
|||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = timer_->tick(1, 1 * SRS_UTIME_SECONDS)) != srs_success) {
|
||||
if ((err = timer_->tick(1, 3 * SRS_UTIME_SECONDS)) != srs_success) {
|
||||
return srs_error_wrap(err, "tick");
|
||||
}
|
||||
|
||||
|
@ -1848,24 +1855,21 @@ srs_error_t SrsLiveSourceManager::notify(int event, srs_utime_t interval, srs_ut
|
|||
|
||||
// Do cycle source to cleanup components, such as hls dispose.
|
||||
if ((err = source->cycle()) != srs_success) {
|
||||
return srs_error_wrap(err, "source=%s/%s cycle", source->source_id().c_str(), source->pre_source_id().c_str());
|
||||
SrsContextId cid = source->source_id();
|
||||
if (cid.empty()) cid = source->pre_source_id();
|
||||
return srs_error_wrap(err, "source cycle, id=[%s]", cid.c_str());
|
||||
}
|
||||
|
||||
// See SrsSrtSource::on_consumer_destroy
|
||||
// TODO: FIXME: support source cleanup.
|
||||
// @see https://github.com/ossrs/srs/issues/713
|
||||
#if 1
|
||||
// When source expired, remove it.
|
||||
// @see https://github.com/ossrs/srs/issues/713
|
||||
if (source->stream_is_dead()) {
|
||||
const SrsContextId& cid = source->source_id();
|
||||
srs_trace("cleanup die source, id=[%s], total=%d", cid.c_str(), (int)pool.size());
|
||||
SrsContextId cid = source->source_id();
|
||||
if (cid.empty()) cid = source->pre_source_id();
|
||||
srs_trace("Live: cleanup die source, id=[%s], total=%d", cid.c_str(), (int)pool.size());
|
||||
pool.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
#else
|
||||
++it;
|
||||
#endif
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -1923,6 +1927,10 @@ SrsLiveSource::~SrsLiveSource()
|
|||
|
||||
srs_freep(req);
|
||||
srs_freep(bridge_);
|
||||
|
||||
SrsContextId cid = _source_id;
|
||||
if (cid.empty()) cid = _pre_source_id;
|
||||
srs_trace("free live source id=[%s]", cid.c_str());
|
||||
}
|
||||
|
||||
void SrsLiveSource::dispose()
|
||||
|
@ -1944,11 +1952,6 @@ srs_error_t SrsLiveSource::cycle()
|
|||
|
||||
bool SrsLiveSource::stream_is_dead()
|
||||
{
|
||||
// unknown state?
|
||||
if (stream_die_at_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// still publishing?
|
||||
if (!_can_publish || !publish_edge->can_publish()) {
|
||||
return false;
|
||||
|
@ -1958,13 +1961,19 @@ bool SrsLiveSource::stream_is_dead()
|
|||
if (!consumers.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Delay cleanup source.
|
||||
srs_utime_t now = srs_get_system_time();
|
||||
if (now > stream_die_at_ + SRS_SOURCE_CLEANUP) {
|
||||
return true;
|
||||
if (now < stream_die_at_ + SRS_SOURCE_CLEANUP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Origin hub delay cleanup.
|
||||
if (now < stream_die_at_ + hub->cleanup_delay()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SrsLiveSource::publisher_is_idle_for(srs_utime_t timeout)
|
||||
|
@ -2726,6 +2735,11 @@ void SrsLiveSource::on_consumer_destroy(SrsLiveConsumer* consumer)
|
|||
if (consumers.empty()) {
|
||||
play_edge->on_all_client_stop();
|
||||
|
||||
// If no publishers, the stream is die.
|
||||
if (_can_publish) {
|
||||
stream_die_at_ = srs_get_system_time();
|
||||
}
|
||||
|
||||
// For edge server, the stream die when the last player quit, because the edge stream is created by player
|
||||
// activities, so it should die when all players quit.
|
||||
if (_srs_config->get_vhost_is_edge(req->vhost)) {
|
||||
|
|
|
@ -355,6 +355,8 @@ public:
|
|||
virtual srs_error_t cycle();
|
||||
// Whether the stream hub is active, or stream is publishing.
|
||||
virtual bool active();
|
||||
// The delay cleanup time.
|
||||
srs_utime_t cleanup_delay();
|
||||
public:
|
||||
// When got a parsed metadata.
|
||||
virtual srs_error_t on_meta_data(SrsSharedPtrMessage* shared_metadata, SrsOnMetaDataPacket* packet);
|
||||
|
|
|
@ -20,6 +20,9 @@ using namespace std;
|
|||
#include <srs_app_statistic.hpp>
|
||||
#include <srs_app_pithy_print.hpp>
|
||||
|
||||
// the time to cleanup source.
|
||||
#define SRS_SRT_SOURCE_CLEANUP (3 * SRS_UTIME_SECONDS)
|
||||
|
||||
SrsSrtPacket::SrsSrtPacket()
|
||||
{
|
||||
shared_buffer_ = NULL;
|
||||
|
@ -95,11 +98,56 @@ int SrsSrtPacket::size()
|
|||
SrsSrtSourceManager::SrsSrtSourceManager()
|
||||
{
|
||||
lock = srs_mutex_new();
|
||||
timer_ = new SrsHourGlass("sources", this, 1 * SRS_UTIME_SECONDS);
|
||||
}
|
||||
|
||||
SrsSrtSourceManager::~SrsSrtSourceManager()
|
||||
{
|
||||
srs_mutex_destroy(lock);
|
||||
srs_freep(timer_);
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSourceManager::initialize()
|
||||
{
|
||||
return setup_ticks();
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSourceManager::setup_ticks()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
if ((err = timer_->tick(1, 3 * SRS_UTIME_SECONDS)) != srs_success) {
|
||||
return srs_error_wrap(err, "tick");
|
||||
}
|
||||
|
||||
if ((err = timer_->start()) != srs_success) {
|
||||
return srs_error_wrap(err, "timer");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSourceManager::notify(int event, srs_utime_t interval, srs_utime_t tick)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::map< std::string, SrsSharedPtr<SrsSrtSource> >::iterator it;
|
||||
for (it = pool.begin(); it != pool.end();) {
|
||||
SrsSharedPtr<SrsSrtSource>& source = it->second;
|
||||
|
||||
// When source expired, remove it.
|
||||
// @see https://github.com/ossrs/srs/issues/713
|
||||
if (source->stream_is_dead()) {
|
||||
SrsContextId cid = source->source_id();
|
||||
if (cid.empty()) cid = source->pre_source_id();
|
||||
srs_trace("SRT: cleanup die source, id=[%s], total=%d", cid.c_str(), (int)pool.size());
|
||||
pool.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSourceManager::fetch_or_create(SrsRequest* r, SrsSharedPtr<SrsSrtSource>& pps)
|
||||
|
@ -137,19 +185,6 @@ srs_error_t SrsSrtSourceManager::fetch_or_create(SrsRequest* r, SrsSharedPtr<Srs
|
|||
return err;
|
||||
}
|
||||
|
||||
void SrsSrtSourceManager::eliminate(SrsRequest* r)
|
||||
{
|
||||
// Use lock to protect coroutine switch.
|
||||
// @bug https://github.com/ossrs/srs/issues/1230
|
||||
SrsLocker(lock);
|
||||
|
||||
string stream_url = r->get_stream_url();
|
||||
std::map< std::string, SrsSharedPtr<SrsSrtSource> >::iterator it = pool.find(stream_url);
|
||||
if (it != pool.end()) {
|
||||
pool.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
SrsSrtSourceManager* _srs_srt_sources = NULL;
|
||||
|
||||
SrsSrtConsumer::SrsSrtConsumer(SrsSrtSource* s)
|
||||
|
@ -873,6 +908,7 @@ SrsSrtSource::SrsSrtSource()
|
|||
can_publish_ = true;
|
||||
frame_builder_ = NULL;
|
||||
bridge_ = NULL;
|
||||
stream_die_at_ = 0;
|
||||
}
|
||||
|
||||
SrsSrtSource::~SrsSrtSource()
|
||||
|
@ -884,6 +920,10 @@ SrsSrtSource::~SrsSrtSource()
|
|||
srs_freep(frame_builder_);
|
||||
srs_freep(bridge_);
|
||||
srs_freep(req);
|
||||
|
||||
SrsContextId cid = _source_id;
|
||||
if (cid.empty()) cid = _pre_source_id;
|
||||
srs_trace("free srt source id=[%s]", cid.c_str());
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::initialize(SrsRequest* r)
|
||||
|
@ -895,6 +935,27 @@ srs_error_t SrsSrtSource::initialize(SrsRequest* r)
|
|||
return err;
|
||||
}
|
||||
|
||||
bool SrsSrtSource::stream_is_dead()
|
||||
{
|
||||
// still publishing?
|
||||
if (!can_publish_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// has any consumers?
|
||||
if (!consumers.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delay cleanup source.
|
||||
srs_utime_t now = srs_get_system_time();
|
||||
if (now < stream_die_at_ + SRS_SRT_SOURCE_CLEANUP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
srs_error_t SrsSrtSource::on_source_id_changed(SrsContextId id)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
@ -949,6 +1010,8 @@ srs_error_t SrsSrtSource::create_consumer(SrsSrtConsumer*& consumer)
|
|||
consumer = new SrsSrtConsumer(this);
|
||||
consumers.push_back(consumer);
|
||||
|
||||
stream_die_at_ = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -972,7 +1035,7 @@ void SrsSrtSource::on_consumer_destroy(SrsSrtConsumer* consumer)
|
|||
|
||||
// Destroy and cleanup source when no publishers and consumers.
|
||||
if (can_publish_ && consumers.empty()) {
|
||||
_srs_srt_sources->eliminate(req);
|
||||
stream_die_at_ = srs_get_system_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1029,8 +1092,8 @@ void SrsSrtSource::on_unpublish()
|
|||
}
|
||||
|
||||
// Destroy and cleanup source when no publishers and consumers.
|
||||
if (can_publish_ && consumers.empty()) {
|
||||
_srs_srt_sources->eliminate(req);
|
||||
if (consumers.empty()) {
|
||||
stream_die_at_ = srs_get_system_time();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <srs_protocol_st.hpp>
|
||||
#include <srs_app_stream_bridge.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_app_hourglass.hpp>
|
||||
|
||||
class SrsSharedPtrMessage;
|
||||
class SrsRequest;
|
||||
|
@ -47,21 +48,26 @@ private:
|
|||
int actual_buffer_size_;
|
||||
};
|
||||
|
||||
class SrsSrtSourceManager
|
||||
class SrsSrtSourceManager : public ISrsHourGlass
|
||||
{
|
||||
private:
|
||||
srs_mutex_t lock;
|
||||
std::map< std::string, SrsSharedPtr<SrsSrtSource> > pool;
|
||||
SrsHourGlass* timer_;
|
||||
public:
|
||||
SrsSrtSourceManager();
|
||||
virtual ~SrsSrtSourceManager();
|
||||
public:
|
||||
virtual srs_error_t initialize();
|
||||
// interface ISrsHourGlass
|
||||
private:
|
||||
virtual srs_error_t setup_ticks();
|
||||
virtual srs_error_t notify(int event, srs_utime_t interval, srs_utime_t tick);
|
||||
public:
|
||||
// create source when fetch from cache failed.
|
||||
// @param r the client request.
|
||||
// @param pps the matched source, if success never be NULL.
|
||||
virtual srs_error_t fetch_or_create(SrsRequest* r, SrsSharedPtr<SrsSrtSource>& pps);
|
||||
// Dispose and destroy the source.
|
||||
virtual void eliminate(SrsRequest* r);
|
||||
};
|
||||
|
||||
// Global singleton instance.
|
||||
|
@ -156,6 +162,9 @@ public:
|
|||
virtual ~SrsSrtSource();
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsRequest* r);
|
||||
public:
|
||||
// Whether stream is dead, which is no publisher or player.
|
||||
virtual bool stream_is_dead();
|
||||
public:
|
||||
// The source id changed.
|
||||
virtual srs_error_t on_source_id_changed(SrsContextId id);
|
||||
|
@ -190,6 +199,8 @@ private:
|
|||
// To delivery packets to clients.
|
||||
std::vector<SrsSrtConsumer*> consumers;
|
||||
bool can_publish_;
|
||||
// The last die time, while die means neither publishers nor players.
|
||||
srs_utime_t stream_die_at_;
|
||||
private:
|
||||
SrsSrtFrameBuilder* frame_builder_;
|
||||
ISrsStreamBridge* bridge_;
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 6
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 129
|
||||
#define VERSION_REVISION 130
|
||||
|
||||
#endif
|
||||
|
|
|
@ -691,14 +691,12 @@ srs_error_t SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handle
|
|||
srs_assert(handler);
|
||||
|
||||
if (pattern.empty()) {
|
||||
srs_freep(handler);
|
||||
return srs_error_new(ERROR_HTTP_PATTERN_EMPTY, "empty pattern");
|
||||
}
|
||||
|
||||
if (entries.find(pattern) != entries.end()) {
|
||||
SrsHttpMuxEntry* exists = entries[pattern];
|
||||
if (exists->explicit_match) {
|
||||
srs_freep(handler);
|
||||
return srs_error_new(ERROR_HTTP_PATTERN_DUPLICATED, "pattern=%s exists", pattern.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -754,6 +752,32 @@ srs_error_t SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handle
|
|||
return srs_success;
|
||||
}
|
||||
|
||||
void SrsHttpServeMux::unhandle(std::string pattern, ISrsHttpHandler* handler)
|
||||
{
|
||||
if (true) {
|
||||
std::map<std::string, SrsHttpMuxEntry*>::iterator it = entries.find(pattern);
|
||||
if (it != entries.end()) {
|
||||
SrsHttpMuxEntry* entry = it->second;
|
||||
entries.erase(it);
|
||||
|
||||
// We don't free the handler, because user should free it.
|
||||
if (entry->handler != handler) {
|
||||
srs_freep(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string vhost = pattern;
|
||||
if (pattern.at(0) != '/') {
|
||||
if (pattern.find("/") != string::npos) {
|
||||
vhost = pattern.substr(0, pattern.find("/"));
|
||||
}
|
||||
|
||||
std::map<std::string, ISrsHttpHandler*>::iterator it = vhosts.find(vhost);
|
||||
if (it != vhosts.end()) vhosts.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
|
|
@ -470,6 +470,8 @@ public:
|
|||
// Handle registers the handler for the given pattern.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
virtual srs_error_t handle(std::string pattern, ISrsHttpHandler* handler);
|
||||
// Remove the handler for pattern. Note that this will not free the handler.
|
||||
void unhandle(std::string pattern, ISrsHttpHandler* handler);
|
||||
// Interface ISrsHttpServeMux
|
||||
public:
|
||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||
|
|
|
@ -748,6 +748,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerImplicitHandler)
|
|||
HELPER_ASSERT_SUCCESS(s.handle("/api/", h0));
|
||||
|
||||
MockHttpHandler* h1 = new MockHttpHandler("Done");
|
||||
SrsAutoFree(MockHttpHandler, h1);
|
||||
HELPER_EXPECT_FAILED(s.handle("/api/", h1));
|
||||
}
|
||||
|
||||
|
@ -892,6 +893,7 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxerBasic)
|
|||
HELPER_ASSERT_SUCCESS(s.initialize());
|
||||
|
||||
MockHttpHandler* h0 = new MockHttpHandler("Hello, world!");
|
||||
SrsAutoFree(MockHttpHandler, h0);
|
||||
HELPER_EXPECT_FAILED(s.handle("", h0));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue