From 47b9fdb4e8aa4364d2a07c28cf8d86def72cdbe0 Mon Sep 17 00:00:00 2001 From: chenhaibo <495810242@qq.com> Date: Fri, 21 Jun 2024 16:00:28 +0800 Subject: [PATCH 1/8] Add SrsGoApiGbPublish class for external SIP server mode --- trunk/src/app/srs_app_gb28181.cpp | 155 ++++++++++++++++++++++++++++-- trunk/src/app/srs_app_gb28181.hpp | 33 +++++++ 2 files changed, 182 insertions(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 98bfb0d2b9..99d6621438 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -22,6 +22,10 @@ #include #include #include +#include +#include +#include +#include #include using namespace std; @@ -421,12 +425,12 @@ srs_error_t SrsGbListener::initialize(SrsConfDirective* conf) bool sip_enabled = _srs_config->get_stream_caster_sip_enable(conf); if (!sip_enabled) { - return srs_error_new(ERROR_GB_CONFIG, "GB SIP is required"); + srs_warn("GB SIP is disabled."); + } else { + int port = _srs_config->get_stream_caster_sip_listen(conf); + sip_listener_->set_endpoint(ip, port)->set_label("SIP-TCP"); } - int port = _srs_config->get_stream_caster_sip_listen(conf); - sip_listener_->set_endpoint(ip, port)->set_label("SIP-TCP"); - return err; } @@ -442,6 +446,24 @@ srs_error_t SrsGbListener::listen() return srs_error_wrap(err, "listen"); } + if ((err = listen_api()) != srs_success) { + return srs_error_wrap(err, "listen api"); + } + + return err; +} + +srs_error_t SrsGbListener::listen_api() +{ + srs_error_t err = srs_success; + + // TODO: FIXME: Fetch api from hybrid manager, not from SRS. + ISrsHttpServeMux* http_api_mux = _srs_hybrid->srs()->instance()->api_server(); + + if ((err = http_api_mux->handle("/gb/v1/publish/", new SrsGoApiGbPublish(conf_))) != srs_success) { + return srs_error_wrap(err, "handle publish"); + } + return err; } @@ -550,11 +572,16 @@ std::string SrsGbSipTcpConn::device_id() return register_->device_id(); } +void SrsGbSipTcpConn::set_device_id(const std::string &id) +{ + register_->from_address_user_ = id; +} + void SrsGbSipTcpConn::set_cid(const SrsContextId& cid) { if (owner_cid_) owner_cid_->set_cid(cid); - receiver_->set_cid(cid); - sender_->set_cid(cid); + if (receiver_) receiver_->set_cid(cid); + if (sender_) sender_->set_cid(cid); cid_ = cid; } @@ -2684,5 +2711,121 @@ void srs_sip_parse_address(const std::string& address, std::string& user, std::s } } +SrsGoApiGbPublish::SrsGoApiGbPublish(SrsConfDirective* conf) +{ + conf_ = conf->copy(); +} + +SrsGoApiGbPublish::~SrsGoApiGbPublish() +{ + srs_freep(conf_); +} + +srs_error_t SrsGoApiGbPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) +{ + srs_error_t err = srs_success; + + SrsJsonObject* res = SrsJsonAny::object(); + SrsAutoFree(SrsJsonObject, res); + + if ((err = do_serve_http(w, r, res)) != srs_success) { + srs_warn("GB error %s", srs_error_desc(err).c_str()); + res->set("code", SrsJsonAny::integer(srs_error_code(err))); + res->set("desc", SrsJsonAny::str(srs_error_code_str(err).c_str())); + srs_freep(err); + return srs_api_response(w, r, res->dumps()); + } + + return srs_api_response(w, r, res->dumps()); +} + +srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r, SrsJsonObject *res) +{ + srs_error_t err = srs_success; + + // For each GB session, we use short-term HTTP connection. + w->header()->set("Connection", "Close"); + + // Parse req, the request json object, from body. + SrsJsonObject* req = NULL; + SrsAutoFree(SrsJsonObject, req); + if (true) { + string req_json; + if ((err = r->body_read_all(req_json)) != srs_success) { + return srs_error_wrap(err, "read body"); + } + + SrsJsonAny* json = SrsJsonAny::loads(req_json); + if (!json || !json->is_object()) { + return srs_error_new(ERROR_HTTP_DATA_INVALID, "invalid body %s", req_json.c_str()); + } + + req = json->to_object(); + } + + // Fetch params from req object. + SrsJsonAny* prop = NULL; + if ((prop = req->ensure_property_string("id")) == NULL) { + return srs_error_wrap(err, "not id"); + } + string id = prop->to_str(); + + if ((prop = req->ensure_property_string("ssrc")) == NULL) { + return srs_error_wrap(err, "not ssrc"); + } + uint64_t ssrc = atoi(prop->to_str().c_str()); + + if ((err = bind_session(id, ssrc)) != srs_success) { + return srs_error_wrap(err, "bind session"); + } + + res->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); + int port = _srs_config->get_stream_caster_listen(conf_); + res->set("port", SrsJsonAny::integer(port)); + res->set("is_tcp", SrsJsonAny::boolean(true)); // only tcp supported + + srs_trace("GB publish id: %s, ssrc=%lu", id.c_str(), ssrc); + + return err; +} + +srs_error_t SrsGoApiGbPublish::bind_session(std::string id, uint64_t ssrc) +{ + srs_error_t err = srs_success; + + SrsSharedResource* session = NULL; + session = dynamic_cast*>(_srs_gb_manager->find_by_id(id)); + if (session) { + return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "stream already exists"); + } + + session = dynamic_cast*>(_srs_gb_manager->find_by_fast_id(ssrc)); + if (session) { + return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "ssrc already exists"); + } + + SrsGbSession* raw_session = NULL; + if (!session) { + // Create new GB session. + raw_session = new SrsGbSession(); + raw_session->setup(conf_); + + session = new SrsSharedResource(raw_session); + _srs_gb_manager->add_with_id(id, session); + _srs_gb_manager->add_with_fast_id(ssrc, session); + + SrsExecutorCoroutine* executor = new SrsExecutorCoroutine(_srs_gb_manager, session, raw_session, raw_session); + raw_session->setup_owner(session, executor, executor); + raw_session->sip_transport()->set_device_id(id); + + if ((err = executor->start()) != srs_success) { + srs_freep(executor); + return srs_error_wrap(err, "gb session"); + } + } + + return err; +} + SrsResourceManager* _srs_gb_manager = NULL; diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index e44b618c6d..09fdd49d06 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -89,6 +89,34 @@ enum SrsGbSipState }; std::string srs_gb_sip_state(SrsGbSipState state); +// For external SIP server mode, where SRS acts only as a media relay server +// 1. SIP server POST request via HTTP API with stream ID and SSRC +// 2. SRS create session using ID and SSRC, return a port for receiving media streams (indicated in conf). +// 3. External streaming service connect to the port, and send RTP stream (with the above SSRC) +// 4. SRS forward the stream to RTMP stream, named after ID +// +// Request: +// POST /gb/v1/publish/ +// { +// "id": "...", +// "ssrc": "..." +// } +// Response: +// {"port":9000, "is_tcp": true} +class SrsGoApiGbPublish : public ISrsHttpHandler +{ +private: + SrsConfDirective* conf_; +public: + SrsGoApiGbPublish(SrsConfDirective* conf); + virtual ~SrsGoApiGbPublish(); +public: + virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res); + srs_error_t bind_session(std::string stream, uint64_t ssrc); +}; + // The main logic object for GB, the session. // Each session contains a SIP object and a media object, that are managed by session. This means session always // lives longer than SIP and media, and session will dispose SIP and media when session disposed. In another word, @@ -187,6 +215,7 @@ class SrsGbListener : public ISrsListener, public ISrsTcpHandler public: srs_error_t initialize(SrsConfDirective* conf); srs_error_t listen(); + srs_error_t listen_api(); void close(); // Interface ISrsTcpHandler public: @@ -234,6 +263,10 @@ class SrsGbSipTcpConn : public ISrsResource, public ISrsCoroutineHandler, public public: // Get the SIP device id. std::string device_id(); + // For use with external SIP signaling server ONLY + // When using an external SIP signaling server, device id are not available, so manual configuration is required + // This id will be used as the stream name in the RTMP protocol + void set_device_id(const std::string & id); // Set the cid of all coroutines. virtual void set_cid(const SrsContextId& cid); private: From 67d0a9f8ce2168454d05eeb97b5b80eb26f00d24 Mon Sep 17 00:00:00 2001 From: chenhaibo <495810242@qq.com> Date: Mon, 24 Jun 2024 09:06:42 +0800 Subject: [PATCH 2/8] Some formatting optimizations --- trunk/src/app/srs_app_gb28181.cpp | 31 ++++++++++++++----------------- trunk/src/app/srs_app_gb28181.hpp | 5 +++-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 99d6621438..b60b7698d2 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -2739,7 +2739,7 @@ srs_error_t SrsGoApiGbPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMes return srs_api_response(w, r, res->dumps()); } -srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r, SrsJsonObject *res) +srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, SrsJsonObject* res) { srs_error_t err = srs_success; @@ -2804,26 +2804,23 @@ srs_error_t SrsGoApiGbPublish::bind_session(std::string id, uint64_t ssrc) return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "ssrc already exists"); } - SrsGbSession* raw_session = NULL; - if (!session) { - // Create new GB session. - raw_session = new SrsGbSession(); - raw_session->setup(conf_); + // Create new GB session. + SrsGbSession* raw_session = new SrsGbSession(); + raw_session->setup(conf_); - session = new SrsSharedResource(raw_session); - _srs_gb_manager->add_with_id(id, session); - _srs_gb_manager->add_with_fast_id(ssrc, session); + session = new SrsSharedResource(raw_session); + _srs_gb_manager->add_with_id(id, session); + _srs_gb_manager->add_with_fast_id(ssrc, session); - SrsExecutorCoroutine* executor = new SrsExecutorCoroutine(_srs_gb_manager, session, raw_session, raw_session); - raw_session->setup_owner(session, executor, executor); - raw_session->sip_transport()->set_device_id(id); + SrsExecutorCoroutine* executor = new SrsExecutorCoroutine(_srs_gb_manager, session, raw_session, raw_session); + raw_session->setup_owner(session, executor, executor); + raw_session->sip_transport()->set_device_id(id); - if ((err = executor->start()) != srs_success) { - srs_freep(executor); - return srs_error_wrap(err, "gb session"); - } + if ((err = executor->start()) != srs_success) { + srs_freep(executor); + return srs_error_wrap(err, "gb session"); } - + return err; } diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 09fdd49d06..be62509481 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -215,11 +215,12 @@ class SrsGbListener : public ISrsListener, public ISrsTcpHandler public: srs_error_t initialize(SrsConfDirective* conf); srs_error_t listen(); - srs_error_t listen_api(); void close(); // Interface ISrsTcpHandler public: virtual srs_error_t on_tcp_client(ISrsListener* listener, srs_netfd_t stfd); +private: + srs_error_t listen_api(); }; // A GB28181 TCP SIP connection. @@ -266,7 +267,7 @@ class SrsGbSipTcpConn : public ISrsResource, public ISrsCoroutineHandler, public // For use with external SIP signaling server ONLY // When using an external SIP signaling server, device id are not available, so manual configuration is required // This id will be used as the stream name in the RTMP protocol - void set_device_id(const std::string & id); + void set_device_id(const std::string& id); // Set the cid of all coroutines. virtual void set_cid(const SrsContextId& cid); private: From a6f0cac0b75a3542a25f0e8cde4b846a717571b2 Mon Sep 17 00:00:00 2001 From: winlin Date: Fri, 5 Jul 2024 16:09:27 +0800 Subject: [PATCH 3/8] Add conf/gb28181-without-sip.conf --- trunk/conf/gb28181-without-sip.conf | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 trunk/conf/gb28181-without-sip.conf diff --git a/trunk/conf/gb28181-without-sip.conf b/trunk/conf/gb28181-without-sip.conf new file mode 100644 index 0000000000..1b19326b43 --- /dev/null +++ b/trunk/conf/gb28181-without-sip.conf @@ -0,0 +1,53 @@ + +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; + +stream_caster { + enabled on; + caster gb28181; + output rtmp://127.0.0.1/live/[stream]; + listen 9000; + sip { + enabled off; + } +} + +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} + +http_api { + enabled on; + listen 1985; +} +stats { + network 0; +} +rtc_server { + enabled on; + listen 8000; # UDP port + # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate + candidate $CANDIDATE; +} + +vhost __defaultVhost__ { + rtc { + enabled on; + # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc + rtmp_to_rtc on; + # @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp + rtc_to_rtmp on; + } + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } + hls { + enabled on; + } +} + From 3fa141f24b0c36752ceec5b1634f17769d5eefc7 Mon Sep 17 00:00:00 2001 From: chenhaibo <495810242@qq.com> Date: Sat, 6 Jul 2024 15:11:19 +0800 Subject: [PATCH 4/8] fix bug --- trunk/src/app/srs_app_gb28181.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index b60b7698d2..7d7fcf0d79 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -2766,12 +2766,12 @@ srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttp // Fetch params from req object. SrsJsonAny* prop = NULL; if ((prop = req->ensure_property_string("id")) == NULL) { - return srs_error_wrap(err, "not id"); + return srs_error_new(ERROR_HTTP_DATA_INVALID, "id required"); } string id = prop->to_str(); if ((prop = req->ensure_property_string("ssrc")) == NULL) { - return srs_error_wrap(err, "not ssrc"); + return srs_error_new(ERROR_HTTP_DATA_INVALID, "ssrc required"); } uint64_t ssrc = atoi(prop->to_str().c_str()); From 7393105346bfb601ad9a98f85cd2ff21a50bd155 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 27 Jul 2024 08:26:23 +0800 Subject: [PATCH 5/8] Update release to v6.0.144 --- trunk/doc/CHANGELOG.md | 1 + trunk/src/core/srs_core_version6.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 6931694460..ce56b82aa9 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2024-07-27, Merge [#4101](https://github.com/ossrs/srs/pull/4101): GB28181: Support external SIP server. v6.0.144 (#4101) * v6.0, 2024-07-24, Merge [#4115](https://github.com/ossrs/srs/pull/4115): HLS: Add missing newline to end of session manifest. v6.0.143 (#4115) * v6.0, 2024-07-24, Merge [#4029](https://github.com/ossrs/srs/pull/4029): Player: Fix empty img tag occupy 20px size in safari. v6.0.142 (#4029) * v6.0, 2024-07-24, Merge [#4063](https://github.com/ossrs/srs/pull/4063): let http-remux ts stream support guess_has_av feature;. v6.0.141 (#4063) diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index 021f633e7e..c6259016b7 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 143 +#define VERSION_REVISION 144 #endif From 4ec9c2a9e5acdca0ce576e919db9a4478f9b3617 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 27 Jul 2024 08:31:30 +0800 Subject: [PATCH 6/8] Update --- trunk/conf/full.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index db3965000a..7432ec1158 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -676,9 +676,11 @@ stream_caster { listen 9000; # SIP server for GB28181. Please note that this is only a demonstrated SIP server, please never use it in your # online production environment. Instead please use [jsip](https://github.com/usnistgov/jsip) and there is a demo - # [srs-sip](https://github.com/ossrs/srs-sip) also base on it. + # [srs-sip](https://github.com/ossrs/srs-sip) also base on it, for more information please see project + # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v5/doc/gb28181#external-sip). sip { - # Whether enable embedded SIP server. + # Whether enable embedded SIP server. Please disable it if you want to use your own SIP server, see + # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v5/doc/gb28181#external-sip). # Default: on enabled on; # The SIP listen port, for TCP protocol. From 8634cf2481f1e02dddab1a97f51643f6d25ecdc0 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 27 Jul 2024 08:32:02 +0800 Subject: [PATCH 7/8] Update --- trunk/conf/full.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 7432ec1158..0579788fac 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -677,10 +677,10 @@ stream_caster { # SIP server for GB28181. Please note that this is only a demonstrated SIP server, please never use it in your # online production environment. Instead please use [jsip](https://github.com/usnistgov/jsip) and there is a demo # [srs-sip](https://github.com/ossrs/srs-sip) also base on it, for more information please see project - # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v5/doc/gb28181#external-sip). + # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v6/doc/gb28181#external-sip). sip { # Whether enable embedded SIP server. Please disable it if you want to use your own SIP server, see - # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v5/doc/gb28181#external-sip). + # [GB: External SIP](https://ossrs.net/lts/zh-cn/docs/v6/doc/gb28181#external-sip). # Default: on enabled on; # The SIP listen port, for TCP protocol. From 560259d42fb1735d75ac0d94c04eac3bf319d802 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 27 Jul 2024 08:38:00 +0800 Subject: [PATCH 8/8] Fix build fail. --- trunk/src/app/srs_app_gb28181.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index a05e74f3d1..abe1eefe0f 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -2722,10 +2722,9 @@ srs_error_t SrsGoApiGbPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMes { srs_error_t err = srs_success; - SrsJsonObject* res = SrsJsonAny::object(); - SrsAutoFree(SrsJsonObject, res); + SrsUniquePtr res(SrsJsonAny::object()); - if ((err = do_serve_http(w, r, res)) != srs_success) { + if ((err = do_serve_http(w, r, res.get())) != srs_success) { srs_warn("GB error %s", srs_error_desc(err).c_str()); res->set("code", SrsJsonAny::integer(srs_error_code(err))); res->set("desc", SrsJsonAny::str(srs_error_code_str(err).c_str())); @@ -2744,8 +2743,7 @@ srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttp w->header()->set("Connection", "Close"); // Parse req, the request json object, from body. - SrsJsonObject* req = NULL; - SrsAutoFree(SrsJsonObject, req); + SrsSharedPtr req; if (true) { string req_json; if ((err = r->body_read_all(req_json)) != srs_success) { @@ -2754,10 +2752,11 @@ srs_error_t SrsGoApiGbPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttp SrsJsonAny* json = SrsJsonAny::loads(req_json); if (!json || !json->is_object()) { + srs_freep(json); return srs_error_new(ERROR_HTTP_DATA_INVALID, "invalid body %s", req_json.c_str()); } - req = json->to_object(); + req = SrsSharedPtr(json->to_object()); } // Fetch params from req object.