GB28181: Support external SIP server. v6.0.144 ()

For  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>
pull/4127/head
Haibo Chen committed by GitHub
parent f76be5fe9b
commit 65ad907fe4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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/v6/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/v6/doc/gb28181#external-sip).
# Default: on
enabled on;
# The SIP listen port, for TCP protocol.

@ -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;
}
}

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v6-changes"></a>
## 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)

@ -22,6 +22,10 @@
#include <srs_app_pithy_print.hpp>
#include <srs_app_rtmp_conn.hpp>
#include <srs_protocol_raw_avc.hpp>
#include <srs_app_server.hpp>
#include <srs_protocol_json.hpp>
#include <srs_app_http_api.hpp>
#include <srs_app_statistic.hpp>
#include <sstream>
using namespace std;
@ -420,12 +424,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;
}
@ -441,6 +445,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;
}
@ -549,11 +571,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;
}
@ -2681,5 +2708,117 @@ 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;
SrsUniquePtr<SrsJsonObject> res(SrsJsonAny::object());
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()));
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.
SrsSharedPtr<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()) {
srs_freep(json);
return srs_error_new(ERROR_HTTP_DATA_INVALID, "invalid body %s", req_json.c_str());
}
req = SrsSharedPtr<SrsJsonObject>(json->to_object());
}
// Fetch params from req object.
SrsJsonAny* prop = NULL;
if ((prop = req->ensure_property_string("id")) == NULL) {
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_new(ERROR_HTTP_DATA_INVALID, "ssrc required");
}
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<SrsGbSession>* session = NULL;
session = dynamic_cast<SrsSharedResource<SrsGbSession>*>(_srs_gb_manager->find_by_id(id));
if (session) {
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "stream already exists");
}
session = dynamic_cast<SrsSharedResource<SrsGbSession>*>(_srs_gb_manager->find_by_fast_id(ssrc));
if (session) {
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "ssrc already exists");
}
// Create new GB session.
SrsGbSession* raw_session = new SrsGbSession();
raw_session->setup(conf_);
session = new SrsSharedResource<SrsGbSession>(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;

@ -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,
@ -191,6 +219,8 @@ public:
// 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.
@ -234,6 +264,10 @@ 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:

@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
#define VERSION_REVISION 143
#define VERSION_REVISION 144
#endif

Loading…
Cancel
Save