From 6e08afa75727300020d396a3c99385440832dfa6 Mon Sep 17 00:00:00 2001 From: Haibo Chen <495810242@qq.com> Date: Sat, 27 Jul 2024 08:20:28 +0800 Subject: [PATCH] chore: Add a section for an external SIP signaling server to the GB28181 document. (#67) * chore: Add a section for an external SIP signaling server to the GB28181 document. * chore: Update GB28181 document with improved wording and formatting * chore: Update GB28181 document with repository link * chore: update register * Update README for GB SIP. * Update --------- Co-authored-by: winlin --- .../current/doc/gb28181.md | 145 ++++++------------ 1 file changed, 49 insertions(+), 96 deletions(-) diff --git a/i18n/zh-cn/docusaurus-plugin-content-docs/current/doc/gb28181.md b/i18n/zh-cn/docusaurus-plugin-content-docs/current/doc/gb28181.md index 0d523a8f..be0a7ca4 100644 --- a/i18n/zh-cn/docusaurus-plugin-content-docs/current/doc/gb28181.md +++ b/i18n/zh-cn/docusaurus-plugin-content-docs/current/doc/gb28181.md @@ -138,11 +138,57 @@ GB相关的协议如下: * [GB28181-2016: 公共安全视频监控联网系统信息传输、交换、控制技术要求](https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=469659DC56B9B8187671FF08748CEC89) * [ISO13818-1-2000](https://ossrs.net/lts/zh-cn/assets/files/hls-mpeg-ts-iso13818-1-d21d1e9765012a327f03b43ce460079a.pdf): MPEGPS(Program Stream), PS媒体流规范。 +## External Sip + +> Note: SRS 6.0.135+支持外部SIP,若需要使用这个功能,请升级到这个版本。 + +目前SRS内置的SIP服务器仅实现了简单的`Register`、`Invite`指令,而要实现GB/T-28181的全部功能,势必会引入复杂的上层业务逻辑。 +因此,我们开发了一个独立的外置的SIP服务器。而SRS,只需开放几个简单的API接口,这样既保证了其媒体转发服务器的单一属性, +又兼顾了与第三方SIP信令服务器对接的需求。 + +播放器请求SRS-SIP,SRS-SIP向SRS Server申请媒体端口,然后邀请GB28181 Device设备推流。设备推流到SRS后,播放器直接从SRS播放流。 +下面是几个组件的关系图,详细的交互时序图参考[srs-sip](https://github.com/ossrs/srs-sip#sequence) + +```text + +-------------API/Media--------------------+ + | | ++----------+ +------------+ +------+------+ +----------------+ +| Player +--API--+ SRS-SIP +---API--+ SRS Server +----Media----+ GB28181 Device + ++----------+ +-----+------+ +-------------+ +-------+--------+ + | | + +------------------SIP-------------------------+ +``` + +> Note: 暂时没有实现鉴权功能,敬请期待。 + +摄像头上面的配置方法同上,仅需将SIP服务器地址从SRS改成SRS-SIP。 + +首先启动SRS,请确认版本为`6.0.135+`,使用配置`conf/gb28181-without-sip.conf`,参考[Usage](#usage)。 + +```bash +./objs/srs -c conf/gb28181-without-sip.conf +``` + +然后启动SRS-SIP,参考[srs-sip](https://github.com/ossrs/srs-sip#usage)。 + +```bash +./bin/srs-sip -sip-port 5060 -media-addr 127.0.0.1:1985 -api-port 2020 -http-server-port 8888 +``` + +* `-sip-port`是SIP服务器的端口,默认是5060。GB摄像头和这个SIP服务器通信,完成设备注册等能力。 +* `-media-addr`是SRS的媒体服务器地址,SIP服务器返回这个地址给GB摄像头,GB摄像头推流到这个地址。 +* `-api-port`是SIP服务器的API端口,默认是2020。这个API是给Player和用户使用的,比如查询设备列表、要求摄像头推流等。 +* `-http-server-port`是SIP服务器的Web端口,默认是8888。这个HTTP服务器是提供网页的web服务器,用户通过网页访问摄像头。 + +启动GB28181设备,将SIP服务器地址改成SRS-SIP的地址,端口为5060。 + +现在,可以通过SRS-SIP内置的网页播放器测试 [http://localhost:8888](http://localhost:8888) + ## SIP Parser -C++没有特别好的SIP库,这也是SIP处理不稳定的一个原因。 +SRS本身也内嵌了一个简单的SIP服务器,支持部分SIP协议的解析;不过C++没有特别好的SIP库,这也是之前SIP处理不稳定的一个原因。 -不过发现SIP协议和HTTP协议结构非常一致,因此SRS采用[http-parser](https://github.com/ossrs/http-parser)解析SIP,这个库是nodejs维护的,之前好像是NGINX中扒出来的,所以稳定性还是非常高的。 +调研发现,SIP协议和HTTP协议结构非常一致,因此SRS采用[http-parser](https://github.com/ossrs/http-parser)解析SIP,这个库是nodejs维护的,之前好像是NGINX中扒出来的,所以稳定性还是非常高的。 当然用HTTP解析SIP,需要有些修改,主要是以下修改: @@ -324,100 +370,7 @@ GB存在和[Source清理](https://github.com/ossrs/srs/issues/413#issuecomment-1 Source清理的问题,本质上是多个协程之间生命周期不同步,所以如果释放Source后可能有些协程活得比Source更久,就可能出现野指针引用。详细请查看[#413](https://github.com/ossrs/srs/issues/413)的描述。 -而这些问题的解决方案都是Lazy Sweep,也就是互相持有的不是裸指针,而是Wrapper指针(有点像C++ 11的智能指针只是没有什么智能可言),因为Wrapper释放后Resource还是可用的,其他Wrapper对象还是可以使用Resource,我们在释放Resource时,等所有Wrapper都释放后再安全释放,也就是Lazy Sweep。 - -先在SRS 5.0 GB上实现这个机制,估计在6.0就可以比较容易实现Source的释放了。 - -比如有两个资源,互相依赖,定义如下: - -```cpp -class SrsLazyGbSipTcpConn : public ISrsLazyResource { -private: - SrsLazyGbSessionWrapper* session_; -}; - -class SrsLazyGbSession : public ISrsLazyResource { -private: - SrsLazyGbSipTcpConnWrapper* sip_; -}; -``` - -它们对应的Wrapper定义如下,使用宏定义会很简单: - -```cpp -class SrsLazyGbSipTcpConnWrapper : public ISrsResource, public ISrsGbSipConnWrapper { - SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSipTcpConn, SrsLazyGbSipTcpConnWrapper, SrsLazyGbSipTcpConn); -}; - -class SrsLazyGbSessionWrapper : public ISrsResource { - SRS_LAZY_WRAPPER_GENERATOR(SrsLazyGbSession, SrsLazyGbSessionWrapper, SrsLazyGbSession); -}; -``` - -比如sip对象创建Session,放到全局对象管理manager里面,然后自己拷贝一份引用: - -```cpp -SrsLazyGbSessionWrapper* session = new SrsLazyGbSessionWrapper(); -_srs_gb_manager->add_with_id(device, session); -session_ = session->copy(); -``` - -这样在释放sip对象时,是直接释放的session的wrapper,而在sip对象生命周期中,session一直是可用的: - -```cpp -SrsLazyGbMediaTcpConn::~SrsLazyGbMediaTcpConn() { - srs_freep(session_); -} - -srs_error_t SrsLazyGbSipTcpConn::cycle() { - session_->resource()->on_sip_dispose(); -``` - -同样,session对象拥有的是sip对象的wrapper,是sip自己传过来的,session会持有一份拷贝: - -```cpp -void SrsLazyGbSession::on_sip_transport(SrsLazyGbSipTcpConnWrapper* sip) { - srs_freep(sip_); - sip_ = sip->copy(); -} -``` - -这样在session释放时,也是直接释放sip的wrapper,在session生命周期中,sip一直都是可用的: - -```cpp -SrsLazyGbSession::~SrsLazyGbSession() { - srs_freep(sip_); -} - -srs_error_t SrsLazyGbSession::cycle() { - sip_->resource()->interrupt(); - -srs_error_t SrsLazyGbSession::do_cycle() { - while (true) { - if (sip_->resource()->is_bye()) { - -srs_error_t SrsLazyGbSession::drive_state() { - if (state_ == SrsGbSessionStateInit) { - if (sip_->resource()->is_registered()) { -``` - -而sip和session在释放自己的wrapper时,也不用关注谁在使用,只需要根据自己的创建选择对应的释放即可,比如一般使用manager管理对象,那么就应该使用manager释放对象: - -```cpp -srs_error_t SrsLazyGbSipTcpConn::cycle() { - // Note that we added wrapper to manager, so we must free the wrapper, not this connection. - SrsLazyGbSipTcpConnWrapper* wrapper = dynamic_cast(gc_creator_wrapper()); - _srs_gb_manager->remove(wrapper); - -srs_error_t SrsLazyGbSession::cycle() { - // Note that we added wrapper to manager, so we must free the wrapper, not this connection. - SrsLazyGbSessionWrapper* wrapper = dynamic_cast(gc_creator_wrapper()); - _srs_gb_manager->remove(wrapper); -``` - -此外,wrapper对象就是普通的对象,可以直接被释放,也可以用manager释放,都是可以的。 - -这个方案和直接裸指针引用的差别,在于增加了一个wrapper对象,没有任何其他特别的地方,看起来只是加了一层函数而已,是最简单的方案。 +SRS 6.0引入了Smart Pointer,解决了Source清理的问题,具体参考[SmartPtr for GB](https://github.com/ossrs/srs/commit/6834ec208d67fa47c21536d1f1041bb6d60c1834)的修改。 ## Benchmark