diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 2dcd3aa52..c7467666d 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -226,64 +226,134 @@ http_server { ############################################################################################# # the streamer cast stream from other protocol to SRS over RTMP. # @see https://github.com/ossrs/srs/tree/develop#stream-architecture + +# MPEGTS over UDP stream_caster { # whether stream caster is enabled. # default: off - enabled off; + enabled on; # the caster type of stream, the casters: # mpegts_over_udp, MPEG-TS over UDP caster. - # rtsp, Real Time Streaming Protocol (RTSP). - # flv, FLV over HTTP by POST. caster mpegts_over_udp; # the output rtmp url. # for mpegts_over_udp caster, the typically output url: # rtmp://127.0.0.1/live/livestream + output rtmp://127.0.0.1/live/livestream; + # the listen port for stream caster. + # for mpegts_over_udp caster, listen at udp port. for example, 8935. + listen 8935; +} + +# RTSP +stream_caster { + # whether stream caster is enabled. + # default: off + enabled on; + # the caster type of stream, the casters: + # rtsp, Real Time Streaming Protocol (RTSP). + caster rtsp; + # the output rtmp url. # for rtsp caster, the typically output url: # rtmp://127.0.0.1/[app]/[stream] # for example, the rtsp url: # rtsp://192.168.1.173:8544/live/livestream.sdp # where the [app] is "live" and [stream] is "livestream", output is: # rtmp://127.0.0.1/live/livestream - # for flv caster, the typically output url: - # rtmp://127.0.0.1/[app]/[stream] - # for example, POST to url: - # http://127.0.0.1:8936/live/livestream.flv - # where the [app] is "live" and [stream] is "livestream", output is: - # rtmp://127.0.0.1/live/livestream - output rtmp://127.0.0.1/live/livestream; + output rtmp://127.0.0.1/[app]/[stream]; # the listen port for stream caster. - # for mpegts_over_udp caster, listen at udp port. for example, 8935. # for rtsp caster, listen at tcp port. for example, 554. - # for flv caster, listen at tcp port. for example, 8936. - # TODO: support listen at <[ip:]port> - listen 8935; + listen 554; # for the rtsp caster, the rtp server local port over udp, # which reply the rtsp setup request message, the port will be used: # [rtp_port_min, rtp_port_max) rtp_port_min 57200; rtp_port_max 57300; } + +# FLV stream_caster { - enabled off; - caster mpegts_over_udp; - output rtmp://127.0.0.1/live/livestream; - listen 8935; -} -stream_caster { - enabled off; - caster rtsp; - output rtmp://127.0.0.1/[app]/[stream]; - listen 554; - rtp_port_min 57200; - rtp_port_max 57300; -} -stream_caster { - enabled off; + # whether stream caster is enabled. + # default: off + enabled on; + # the caster type of stream, the casters: + # flv, FLV over HTTP by POST. caster flv; + # the output rtmp url. + # for flv caster, the typically output url: + # rtmp://127.0.0.1/[app]/[stream] + # for example, POST to url: + # http://127.0.0.1:8936/live/livestream.flv + # where the [app] is "live" and [stream] is "livestream", output is: + # rtmp://127.0.0.1/live/livestream output rtmp://127.0.0.1/[app]/[stream]; + # the listen port for stream caster. + # for flv caster, listen at tcp port. for example, 8936. listen 8936; } +# GB28181 +stream_caster { + # whether stream caster is enabled. + # default: off + enabled on; + # the caster type of stream, the casters: + # gb28181, Push GB28181 to SRS. + caster gb28181; + # the output rtmp url. + # for gb28181 caster, the typically output url: + # rtmp://127.0.0.1/live/[stream] + # where the [stream] is the VideoChannelCodecID. + output rtmp://127.0.0.1/live/[stream]; + # the listen port for stream caster. + # for gb28181 caster, listen at udp port. for example, 9000. + # @remark We can bundle all gb28181 to this port, to reuse this port. + # User can choose to bundle port in API port_mode or SIP invite_port_fixed. + listen 9000; + # If not bundle ports, use specified ports for each stream. + rtp_port_min 58200; + rtp_port_max 58300; + # Whether wait for keyframe then forward to RTMP. + wait_keyframe off; + # Max timeout in seconds for RTP stream, if timeout, RTCP bye and close stream. + # default: 30 + rtp_idle_timeout 30; + # Whether has audio. + # @remark Flash/RTMP only supports 11025 22050 44100 sample rate, if not the audio may corrupt. + # default: off + audio_enable off; + # The exposed IP to receive media stream. + host 192.168.1.3; + + sip { + # Whether enable embeded SIP server. + # default: on + enabled on; + # The SIP listen port. + # default: 5060 + listen 5060; + # The SIP server ID. + # default: 34020000002000000001 + serial 34020000002000000001; + # The SIP server domain. + # default: 3402000000 + realm 3402000000; + # The SIP ACK response timeout in seconds. + # default: 30 + ack_timeout 30; + # The keepalive timeout in seconds. + # default: 120 + keepalive_timeout 120; + # Whether print SIP logs. + print_sip_message off; + # Whether play immediately after registered. + # default: on + auto_play on; + # Whether bundle media stream port. + # default: on + invite_port_fixed on; + } +} + ############################################################################################# # SRT server section ############################################################################################# diff --git a/trunk/conf/push.gb28181.conf b/trunk/conf/push.gb28181.conf new file mode 100644 index 000000000..a106016c3 --- /dev/null +++ b/trunk/conf/push.gb28181.conf @@ -0,0 +1,99 @@ +# push gb28281 stream to SRS. + +listen 1935; +max_connections 1000; +daemon off; +srs_log_tank console; + +http_api { + enabled on; + listen 1985; +} + +stream_caster { + enabled on; + caster gb28181; + + # 转发流到rtmp服务器地址与端口 + # TODO: https://github.com/ossrs/srs/pull/1679/files#r400875104 + # [stream] is VideoChannelCodecID(视频通道编码ID) + output 127.0.0.1:1935; + + # 接收设备端rtp流的多路复用端口 + listen 9000; + + # rtp接收监听端口范围,最小值 + rtp_port_min 58200; + # rtp接收监听端口范围,最大值 + rtp_port_max 58300; + + # 是否等待关键帧之后,再转发, + # off:不需等待,直接转发 + # on:等第一个关键帧后,再转发 + wait_keyframe off; + + # rtp包空闲等待时间,如果指定时间没有收到任何包 + # rtp监听连接自动停止,发送BYE命令 + rtp_idle_timeout 30; + + # 是否转发音频流 + # 目前只支持aac格式,所以需要设备支持aac格式 + # on:转发音频 + # off:不转发音频,只有视频 + # *注意*!!!:flv 只支持11025 22050 44100 三种 + # 如果设备端没有三种中任何一个,转发时为自动选择一种格式 + # 同时也会将adts的头封装在flv aac raw数据中 + # 这样的话播放器为自动通过adts头自动选择采样频率 + # 像ffplay, vlc都可以,但是flash是没有声音, + # 因为flash,只支持11025 22050 44100 + audio_enable off; + + # 服务器主机号,可以域名或ip地址 + # 也就是设备端将媒体发送的地址,如果是服务器是内外网 + # 需要写外网地址, + # 调用api创建stream session时返回ip地址也是host + # TODO: https://github.com/ossrs/srs/pull/1679/files#r400917594 + host 192.168.1.27; + + sip { + # 是否启用srs内部sip信令 + # 为on信令走srs, off 只转发ps流 + enabled on; + + # sip监听udp端口 + listen 5060; + + # SIP server ID(SIP服务器ID). + # 设备端配置编号需要与该值一致,否则无法注册 + serial 34020000002000000001; + + # SIP server domain(SIP服务器域) + realm 3402000000; + + # 服务端发送ack后,接收回应的超时时间,单位为秒 + # 如果指定时间没有回应,认为失败 + ack_timeout 30; + + # 设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 + # 认为设备离线 + keepalive_timeout 120; + + # 日志打印是否打印sip信息 + # off:不打印 + # on:打印接收或发送sip命令信息 + # TODO: https://github.com/ossrs/srs/pull/1679/files#r400929300 + print_sip_message off; + + # 注册之后是否自动给设备端发送invite + # on: 是 off 不是,需要通过api控制 + auto_play on; + + # 设备将流发送的端口,是否固定 + # on 发送流到多路复用端口 如9000 + # off 自动从rtp_mix_port - rtp_max_port 之间的值中 + # 选一个可以用的端口 + invite_port_fixed on; + } +} +vhost __defaultVhost__ { +} diff --git a/trunk/configure b/trunk/configure index 752bbbf23..6d67ac5b5 100755 --- a/trunk/configure +++ b/trunk/configure @@ -224,7 +224,7 @@ MODULE_DEPENDS=("CORE" "KERNEL") ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_protocol_amf0" "srs_protocol_io" "srs_rtmp_stack" "srs_rtmp_handshake" "srs_protocol_utility" "srs_rtmp_msg_array" "srs_protocol_stream" - "srs_raw_avc" "srs_rtsp_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json" + "srs_raw_avc" "srs_rtsp_stack" "srs_sip_stack" "srs_http_stack" "srs_protocol_kbps" "srs_protocol_json" "srs_protocol_format") if [[ $SRS_RTC == YES ]]; then MODULE_FILES+=("srs_stun_stack") @@ -277,7 +277,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then "srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call" "srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec" "srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr" - "srs_app_coworkers" "srs_app_hybrid") + "srs_app_coworkers" "srs_app_hybrid" "srs_app_gb28181" "srs_app_gb28181_sip") if [[ $SRS_RTC == YES ]]; then MODULE_FILES+=("srs_app_rtc" "srs_app_rtc_conn" "srs_app_dtls" "srs_app_audio_recode" "srs_app_sdp") fi diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index c5a0e3a3c..32467ab06 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -275,6 +275,11 @@ bool srs_stream_caster_is_flv(string caster) return caster == "flv"; } +bool srs_stream_caster_is_gb28181(string caster) +{ + return caster == "gb28181"; +} + bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req) { static bool DEFAULT = true; @@ -2137,7 +2142,30 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) sobj->set(sdir->name, sdir->dumps_arg0_to_integer()); } else if (sdir->name == "rtp_port_max") { sobj->set(sdir->name, sdir->dumps_arg0_to_integer()); + } else if (sdir->name == "rtp_idle_timeout") { + sobj->set(sdir->name, sdir->dumps_arg0_to_integer()); + } else if (sdir->name == "ack_timeout") { + sobj->set(sdir->name, sdir->dumps_arg0_to_integer()); + } else if (sdir->name == "keepalive_timeout") { + sobj->set(sdir->name, sdir->dumps_arg0_to_integer()); + } else if (sdir->name == "audio_enable") { + sobj->set(sdir->name, sdir->dumps_arg0_to_boolean()); + } else if (sdir->name == "host") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "serial") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "realm") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "wait_keyframe") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "print_sip_message") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "invite_port_fixed") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); + } else if (sdir->name == "auto_play") { + sobj->set(sdir->name, sdir->dumps_arg0_to_str()); } + } obj->set(dir->name, sobj); } else { @@ -3650,9 +3678,25 @@ srs_error_t SrsConfig::check_normal_config() SrsConfDirective* conf = stream_caster->at(i); string n = conf->name; if (n != "enabled" && n != "caster" && n != "output" - && n != "listen" && n != "rtp_port_min" && n != "rtp_port_max") { + && n != "listen" && n != "rtp_port_min" && n != "rtp_port_max" + && n != "rtp_idle_timeout" && n != "sip" + && n != "audio_enable" && n != "wait_keyframe" + && n != "host") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str()); } + + if (n == "sip") { + for (int j = 0; j < (int)conf->directives.size(); j++) { + string m = conf->at(j)->name; + if (m != "enabled" && m != "listen" + && m != "ack_timeout" && m != "keepalive_timeout" + && m != "host" && m != "serial" && m != "realm" + && m != "print_sip_message" && m != "auto_play" + && m != "invite_port_fixed") { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", m.c_str()); + } + } + } } } @@ -4273,6 +4317,262 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf) return ::atoi(conf->arg0().c_str()); } +srs_utime_t SrsConfig::get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf) +{ + static srs_utime_t DEFAULT = 30 * SRS_UTIME_SECONDS; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("rtp_idle_timeout"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS); +} + +int SrsConfig::get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf) +{ + static int DEFAULT = 30; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("ack_timeout"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* conf) +{ + static int DEFAULT = 120; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("keepalive_timeout"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +string SrsConfig::get_stream_caster_gb28181_host(SrsConfDirective* conf) +{ + static string DEFAULT = ""; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("host"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + +string SrsConfig::get_stream_caster_gb28181_serial(SrsConfDirective* conf) +{ + static string DEFAULT = "34020000002000000001"; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("serial"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + +string SrsConfig::get_stream_caster_gb28181_realm(SrsConfDirective* conf) +{ + static string DEFAULT = "3402000000"; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("realm"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + +bool SrsConfig::get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf) +{ + static bool DEFAULT = false; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("audio_enable"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_stream_caster_gb28181_print_sip_message(SrsConfDirective* conf) +{ + static bool DEFAULT = false; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("print_sip_message"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf) +{ + static bool DEFAULT = false; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("wait_keyframe"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_stream_caster_gb28181_sip_enable(SrsConfDirective* conf) +{ + static bool DEFAULT = true; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("enabled"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); +} + +bool SrsConfig::get_stream_caster_gb28181_sip_auto_play(SrsConfDirective* conf) +{ + static bool DEFAULT = true; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("auto_play"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); + +} + +int SrsConfig::get_stream_caster_gb28181_sip_listen(SrsConfDirective* conf) +{ + static int DEFAULT = 5060; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("listen"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); + +} + +bool SrsConfig::get_stream_caster_gb28181_sip_invite_port_fixed(SrsConfDirective* conf) +{ + static bool DEFAULT = true; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("invite_port_fixed"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_FALSE(conf->arg0()); + +} + int SrsConfig::get_rtc_server_enabled() { SrsConfDirective* conf = root->get("rtc_server"); @@ -4282,45 +4582,45 @@ int SrsConfig::get_rtc_server_enabled() bool SrsConfig::get_rtc_server_enabled(SrsConfDirective* conf) { static bool DEFAULT = false; - + if (!conf) { return DEFAULT; } - + conf = conf->get("enabled"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return SRS_CONF_PERFER_FALSE(conf->arg0()); } int SrsConfig::get_rtc_server_listen() { static int DEFAULT = 8000; - + SrsConfDirective* conf = root->get("rtc_server"); if (!conf) { return DEFAULT; } - + conf = conf->get("listen"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return ::atoi(conf->arg0().c_str()); } std::string SrsConfig::get_rtc_server_candidates() { static string DEFAULT = "*"; - + SrsConfDirective* conf = root->get("rtc_server"); if (!conf) { return DEFAULT; } - + conf = conf->get("candidate"); if (!conf || conf->arg0().empty()) { return DEFAULT; @@ -4335,7 +4635,7 @@ std::string SrsConfig::get_rtc_server_candidates() if (srs_string_starts_with(conf->arg0(), "$")) { return DEFAULT; } - + return (conf->arg0().c_str()); } @@ -4348,36 +4648,36 @@ SrsConfDirective* SrsConfig::get_rtc(string vhost) bool SrsConfig::get_rtc_enabled(string vhost) { static bool DEFAULT = false; - + SrsConfDirective* conf = get_rtc(vhost); - + if (!conf) { return DEFAULT; } - + conf = conf->get("enabled"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return SRS_CONF_PERFER_FALSE(conf->arg0()); } bool SrsConfig::get_rtc_bframe_discard(string vhost) { static bool DEFAULT = false; - + SrsConfDirective* conf = get_rtc(vhost); - + if (!conf) { return DEFAULT; } - + conf = conf->get("bframe"); if (!conf || conf->arg0().empty()) { return DEFAULT; } - + return conf->arg0() == "discard"; } diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index e58777dfa..35637a2ff 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -119,6 +119,7 @@ extern bool srs_config_dvr_is_plan_session(std::string plan); extern bool srs_stream_caster_is_udp(std::string caster); extern bool srs_stream_caster_is_rtsp(std::string caster); extern bool srs_stream_caster_is_flv(std::string caster); +extern bool srs_stream_caster_is_gb28181(std::string caster); // Whether the dvr_apply active the stream specified by req. extern bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req); @@ -499,6 +500,20 @@ public: // Get the max udp port for rtp of stream caster rtsp. virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf); + virtual srs_utime_t get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf); + virtual int get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf); + virtual int get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf); + virtual std::string get_stream_caster_gb28181_host(SrsConfDirective* conf); + virtual std::string get_stream_caster_gb28181_serial(SrsConfDirective* conf); + virtual std::string get_stream_caster_gb28181_realm(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_print_sip_message(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_sip_enable(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_sip_auto_play(SrsConfDirective* conf); + virtual int get_stream_caster_gb28181_sip_listen(SrsConfDirective* conf); + virtual bool get_stream_caster_gb28181_sip_invite_port_fixed(SrsConfDirective* conf); + // rtc section public: virtual int get_rtc_server_enabled(); diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp new file mode 100644 index 000000000..29373ae16 --- /dev/null +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -0,0 +1,1647 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +//#define W_PS_FILE +//#define W_VIDEO_FILE +//#define W_AUDIO_FILE + +SrsPsRtpPacket::SrsPsRtpPacket() +{ +} + +SrsPsRtpPacket::~SrsPsRtpPacket() +{ +} + +srs_error_t SrsPsRtpPacket::decode(SrsBuffer* stream) +{ + srs_error_t err = srs_success; + + // 12bytes header + if (!stream->require(12)) { + return srs_error_new(ERROR_RTP_HEADER_CORRUPT, "requires 12 only %d bytes", stream->left()); + } + + int8_t vv = stream->read_1bytes(); + version = (vv >> 6) & 0x03; + padding = (vv >> 5) & 0x01; + extension = (vv >> 4) & 0x01; + csrc_count = vv & 0x0f; + + int8_t mv = stream->read_1bytes(); + marker = (mv >> 7) & 0x01; + payload_type = mv & 0x7f; + + sequence_number = stream->read_2bytes(); + timestamp = stream->read_4bytes(); + ssrc = stream->read_4bytes(); + + // TODO: FIXME: check sequence number. + + // video codec. + if (payload_type == 96) { + // ps stream playload atleast 1bytes content. + if (!stream->require(1)) { + return srs_error_new(ERROR_RTP_TYPE96_CORRUPT, "requires 1 only %d bytes", stream->left()); + } + // append left bytes to payload. + payload->append(stream->data() + stream->pos() , stream->size()-stream->pos()); + } + return err; +} + +//SrsPsRtpListener +SrsPsRtpListener::SrsPsRtpListener(SrsGb28181Config* c, int p, std::string s) +{ + rtp_processor = new SrsGb28181PsRtpProcessor(c, s); + _port = p; + // TODO: support listen at <[ip:]port> + listener = new SrsUdpListener(this, srs_any_address_for_listener(), p); +} + +SrsPsRtpListener::~SrsPsRtpListener() +{ + srs_freep(listener); + srs_freep(rtp_processor); +} + +int SrsPsRtpListener::port() +{ + return _port; +} + +srs_error_t SrsPsRtpListener::listen() +{ + return listener->listen(); +} + +srs_error_t SrsPsRtpListener::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf){ + srs_error_t err = srs_success; + if (rtp_processor){ + err = rtp_processor->on_udp_packet(from, fromlen, buf, nb_buf); + } + return err; +} + +//SrsGb28181RtpMuxService +SrsGb28181RtpMuxService::SrsGb28181RtpMuxService(SrsConfDirective* c) +{ + config = new SrsGb28181Config(c); + rtp_processor = new SrsGb28181PsRtpProcessor(config,""); +} + +SrsGb28181RtpMuxService::~SrsGb28181RtpMuxService() +{ + srs_freep(config); + srs_freep(rtp_processor); +} + +srs_error_t SrsGb28181RtpMuxService::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf){ + srs_error_t err = srs_success; + if (rtp_processor){ + err = rtp_processor->on_udp_packet(from, fromlen, buf, nb_buf); + } + return err; +} + +//SrsGb28181PsRtpProcessor +SrsGb28181PsRtpProcessor::SrsGb28181PsRtpProcessor(SrsGb28181Config* c, std::string id) +{ + config = c; + pprint = SrsPithyPrint::create_caster(); + channel_id = id; +} + +SrsGb28181PsRtpProcessor::~SrsGb28181PsRtpProcessor() +{ + dispose(); + srs_freep(pprint); +} + +void SrsGb28181PsRtpProcessor::dispose() +{ + map::iterator it2; + for (it2 = cache_ps_rtp_packet.begin(); it2 != cache_ps_rtp_packet.end(); ++it2) { + srs_freep(it2->second); + } + cache_ps_rtp_packet.clear(); + + return; +} + +srs_error_t SrsGb28181PsRtpProcessor::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +{ + srs_error_t err = srs_success; + bool completed = false; + + pprint->elapse(); + + char address_string[64]; + char port_string[16]; + if (getnameinfo(from, fromlen, + (char*)&address_string, sizeof(address_string), + (char*)&port_string, sizeof(port_string), + NI_NUMERICHOST|NI_NUMERICSERV)){ + return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); + } + + int peer_port = atoi(port_string); + + if (true) { + SrsBuffer stream(buf, nb_buf); + SrsPsRtpPacket pkt; + + if ((err = pkt.decode(&stream)) != srs_success) { + return srs_error_wrap(err, "ps rtp decode error"); + } + + //TODO: fixme: the same device uses the same SSRC to send with different local ports + + std::stringstream ss; + ss << pkt.ssrc << ":" << pkt.timestamp << ":" << port_string; + std::string pkt_key = ss.str(); + + std::stringstream ss2; + ss2 << pkt.ssrc << ":" << port_string; + std::string pre_pkt_key = ss2.str(); + + if (pre_packet.find(pre_pkt_key) == pre_packet.end()){ + pre_packet[pre_pkt_key] = new SrsPsRtpPacket(); + pre_packet[pre_pkt_key]->copy(&pkt); + } + //cache pkt by ssrc and timestamp + if (cache_ps_rtp_packet.find(pkt_key) == cache_ps_rtp_packet.end()) { + cache_ps_rtp_packet[pkt_key] = new SrsPsRtpPacket(); + } + + //get previous timestamp by ssrc + uint32_t pre_timestamp = pre_packet[pre_pkt_key]->timestamp; + uint32_t pre_sequence_number = pre_packet[pre_pkt_key]->sequence_number; + + //TODO: check sequence number out of order + //it may be out of order, or multiple streaming ssrc are the same + // if (pre_sequence_number > pkt.sequence_number){ + // srs_info("gb28281: ps sequence_number out of order, ssrc=%#x, pre=%u, cur=%u, addr=%s,port=%s", + // pkt.ssrc, pre_sequence_number, pkt.sequence_number, address_string, port_string); + // //return err; + // } + + //copy header to cache + cache_ps_rtp_packet[pkt_key]->copy(&pkt); + //accumulate one frame of data, to payload cache + cache_ps_rtp_packet[pkt_key]->payload->append(pkt.payload); + + //detect whether it is a completed frame + if (pkt.marker) {// rtp maker is true, is a completed frame + completed = true; + }else if (pre_timestamp != pkt.timestamp){ + //current timestamp is different from previous timestamp + //previous timestamp, is a completed frame + std::stringstream ss; + ss << pkt.ssrc << ":" << pre_timestamp << ":" << port_string; + pkt_key = ss.str(); + if (cache_ps_rtp_packet.find(pkt_key) != cache_ps_rtp_packet.end()) { + completed = true; + } + } + + if (pprint->can_print()) { + srs_trace("<- " SRS_CONSTS_LOG_STREAM_CASTER " gb28181: client_id %s, ps rtp packet %dB, age=%d, vt=%d/%u, sts=%u/%u/%#x, paylod=%dB", + channel_id.c_str(), nb_buf, pprint->age(), pkt.version, pkt.payload_type, pkt.sequence_number, pkt.timestamp, pkt.ssrc, + pkt.payload->length() + ); + } + + //current packet becomes previous packet + srs_freep(pre_packet[pre_pkt_key]); + pre_packet[pre_pkt_key] = new SrsPsRtpPacket(); + pre_packet[pre_pkt_key]->copy(&pkt);; + + if (!completed){ + return err; + } + + //process completed frame data + //clear processed one ps frame + //on completed frame data rtp packet in muxer enqueue + map::iterator key = cache_ps_rtp_packet.find(pkt_key); + if(key != cache_ps_rtp_packet.end()) + { + SrsGb28181RtmpMuxer* muxer = NULL; + + //First, search according to the channel_id. Otherwise, search according to the SSRC. + //Some channel_id are created by RTP pool, which are different ports. + //No channel_id are created by multiplexing ports, which are the same port + if (!channel_id.empty()){ + muxer = _srs_gb28181->fetch_rtmpmuxer(channel_id); + }else { + muxer = _srs_gb28181->fetch_rtmpmuxer_by_ssrc(pkt.ssrc); + + } + + if (muxer){ + //TODO: fixme: the same device uses the same SSRC to send with different local ports + //record the first peer port + muxer->set_channel_peer_port(peer_port); + muxer->set_channel_peer_ip(address_string); + //not the first peer port's non processing + if (muxer->channel_peer_port() != peer_port){ + srs_warn("<- " SRS_CONSTS_LOG_STREAM_CASTER " gb28181: client_id %s, ssrc=%#x, first peer_port=%d cur peer_port=%d", + muxer->get_channel_id().c_str(), pkt.ssrc, muxer->channel_peer_port(), peer_port); + srs_freep(key->second); + }else { + //put it in queue, wait for conn to process, and then free + muxer->ps_packet_enqueue(key->second); + } + }else{ + //no connection process it, discarded + srs_freep(key->second); + } + cache_ps_rtp_packet.erase(pkt_key); + } + } + return err; +} + +//ISrsPsStreamHander ps stream raw video/audio hander interface +ISrsPsStreamHander::ISrsPsStreamHander() +{ +} + +ISrsPsStreamHander::~ISrsPsStreamHander() +{ +} + +//SrsPsStreamDemixer ps stream parse to h264/aac +SrsPsStreamDemixer::SrsPsStreamDemixer(ISrsPsStreamHander *h, std::string id, bool a, bool k) +{ + hander = h; + audio_enable = a; + wait_first_keyframe = k; + channel_id = id; +} + +SrsPsStreamDemixer::~SrsPsStreamDemixer() +{ +} + +bool SrsPsStreamDemixer::can_send_ps_av_packet(){ + if (!wait_first_keyframe) + return true; + + if (first_keyframe_flag) + return true; + + return false; +} + +int64_t SrsPsStreamDemixer::parse_ps_timestamp(const uint8_t* p) +{ + unsigned long b; + //total 33 bits + unsigned long val, val2, val3; + + //1st byte, 5、6、7 bit + b = *p++; + val = (b & 0x0e); + + //2 byte, all bit + b = (*(p++)) << 8; + //3 bytes 1--7 bit + b += *(p++); + val2 = (b & 0xfffe) >> 1; + + //4 byte, all bit + b = (*(p++)) << 8; + //5 byte 1--7 bit + b += *(p++); + val3 = (b & 0xfffe) >> 1; + + //<32--val--30> <29----val2----15> <14----val3----0> + val = (val << 29) | (val2 << 15) | val3; + return val; +} + + +srs_error_t SrsPsStreamDemixer::on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp, uint32_t ssrc) +{ + srs_error_t err = srs_success; + + int complete_len = 0; + int incomplete_len = ps_size; + char *next_ps_pack = ps_data; + + SrsSimpleStream video_stream; + SrsSimpleStream audio_stream; + uint64_t audio_pts = 0; + uint64_t video_pts = 0; + int pse_index = 0; + +#ifdef W_PS_FILE + if (!ps_fw.is_open()) { + std::string filename = "test_ps_" + channel_id + ".mpg"; + ps_fw.open(filename.c_str()); + } + ps_fw.write(ps_data, ps_size, NULL); +#endif + + while(incomplete_len >= sizeof(SrsPsPacketStartCode)) + { + if (next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xBA) + { + //ps header + SrsPsPacketHeader *head = (SrsPsPacketHeader *)next_ps_pack; + unsigned char pack_stuffing_length = head->stuffing_length & 0x07; + + next_ps_pack = next_ps_pack + sizeof(SrsPsPacketHeader) + pack_stuffing_length; + complete_len = complete_len + sizeof(SrsPsPacketHeader) + pack_stuffing_length; + incomplete_len = ps_size - complete_len; + } + else if(next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xBB) + { + //ps system header + SrsPsPacketBBHeader *bbhead=(SrsPsPacketBBHeader *)(next_ps_pack); + int bbheaderlen = htons(bbhead->length); + next_ps_pack = next_ps_pack + sizeof(SrsPsPacketBBHeader) + bbheaderlen; + complete_len = complete_len + sizeof(SrsPsPacketBBHeader) + bbheaderlen; + incomplete_len = ps_size - complete_len; + + first_keyframe_flag = true; + } + else if(next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xBC) + { + //program stream map + + SrsPsMapPacket* psmap_pack = (SrsPsMapPacket*)next_ps_pack; + + psmap_pack->length = htons(psmap_pack->length); + + next_ps_pack = next_ps_pack + psmap_pack->length + sizeof(SrsPsMapPacket); + complete_len = complete_len + psmap_pack->length + sizeof(SrsPsMapPacket); + incomplete_len = ps_size - complete_len; + + } + else if(next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xE0) + { + //pse video stream + SrsPsePacket* pse_pack = (SrsPsePacket*)next_ps_pack; + + unsigned char pts_dts_flags = (pse_pack->info[0] & 0xF0) >> 6; + //in a frame of data, pts is obtained from the first PSE packet + if (pse_index == 0 && pts_dts_flags > 0) { + video_pts = parse_ps_timestamp((unsigned char*)next_ps_pack + 9); + srs_info("gb28181: ps stream video ts=%u pkt_ts=%u", pts, timestamp); + } + pse_index +=1; + + int packlength = htons(pse_pack->length); + int payloadlen = packlength - 2 - 1 - pse_pack->stuffing_length; + + next_ps_pack = next_ps_pack + 9 + pse_pack->stuffing_length; + complete_len = complete_len + 9 + pse_pack->stuffing_length; + + video_stream.append(next_ps_pack, payloadlen); + +#ifdef W_VIDEO_FILE + if (!video_fw.is_open()) { + std::string filename = "test_video_" + channel_id + ".h264"; + video_fw.open(filename.c_str()); + } + video_fw.write(next_ps_pack, payloadlen, NULL); +#endif + + next_ps_pack = next_ps_pack + payloadlen; + complete_len = complete_len + payloadlen; + incomplete_len = ps_size - complete_len; + } + else if (next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xBD) + { + //private stream + + SrsPsePacket* pse_pack = (SrsPsePacket*)next_ps_pack; + + int packlength = htons(pse_pack->length); + int payload_len = packlength - 2 - 1 - pse_pack->stuffing_length; + + next_ps_pack = next_ps_pack + payload_len + 9 + pse_pack->stuffing_length; + complete_len = complete_len + (payload_len + 9 + pse_pack->stuffing_length); + incomplete_len = ps_size - complete_len; + } + else if (next_ps_pack + && next_ps_pack[0] == (char)0x00 + && next_ps_pack[1] == (char)0x00 + && next_ps_pack[2] == (char)0x01 + && next_ps_pack[3] == (char)0xC0) + { + //audio stream + + SrsPsePacket* pse_pack = (SrsPsePacket*)next_ps_pack; + + unsigned char pts_dts_flags = (pse_pack->info[0] & 0xF0) >> 6; + if (pts_dts_flags > 0 ) { + audio_pts = parse_ps_timestamp((unsigned char*)next_ps_pack + 9); + srs_info("gb28181: ps stream video ts=%u pkt_ts=%u", audio_pts, timestamp); + } + + int packlength = htons(pse_pack->length); + int payload_len = packlength - 2 - 1 - pse_pack->stuffing_length; + next_ps_pack = next_ps_pack + 9 + pse_pack->stuffing_length; + + audio_stream.append(next_ps_pack, payload_len); + +#ifdef W_AUDIO_FILE + if (!audio_fw.is_open()) { + std::string filename = "test_audio_" + channel_id + ".aac"; + audio_fw.open(filename.c_str()); + } + audio_fw.write(next_ps_pack, payload_len, NULL); +#endif + + next_ps_pack = next_ps_pack + payload_len; + complete_len = complete_len + (payload_len + 9 + pse_pack->stuffing_length); + incomplete_len = ps_size - complete_len; + + if (hander && audio_enable && audio_stream.length() && can_send_ps_av_packet()) { + if ((err = hander->on_rtp_audio(&audio_stream, audio_pts)) != srs_success) { + return srs_error_wrap(err, "process ps audio packet"); + } + } + } + else + { + srs_trace("gb28181: client_id %s, unkonw ps data %02x %02x %02x %02x\n", + channel_id.c_str(), next_ps_pack[0], next_ps_pack[1], next_ps_pack[2], next_ps_pack[3]); + break; + } + } + + if (complete_len != ps_size){ + srs_trace("gb28181: client_id %s decode ps packet error! ps_size=%d complete=%d \n", + channel_id.c_str(), ps_size, complete_len); + }else if (hander && video_stream.length() && can_send_ps_av_packet()) { + if ((err = hander->on_rtp_video(&video_stream, video_pts)) != srs_success) { + return srs_error_wrap(err, "process ps video packet"); + } + } + + return err; +} + + +//Gb28181 Config +SrsGb28181Config::SrsGb28181Config(SrsConfDirective* c) +{ + // TODO: FIXME: support reload. + host = _srs_config->get_stream_caster_gb28181_host(c); + output = _srs_config->get_stream_caster_output(c); + rtp_mux_port = _srs_config->get_stream_caster_listen(c); + rtp_port_min = _srs_config->get_stream_caster_rtp_port_min(c); + rtp_port_max = _srs_config->get_stream_caster_rtp_port_max(c); + rtp_idle_timeout = _srs_config->get_stream_caster_gb28181_rtp_idle_timeout(c); + + wait_keyframe = _srs_config->get_stream_caster_gb28181_wait_keyframe(c); + audio_enable = _srs_config->get_stream_caster_gb28181_audio_enable(c); + + //sip config + sip_enable = _srs_config->get_stream_caster_gb28181_sip_enable(c); + sip_port = _srs_config->get_stream_caster_gb28181_sip_listen(c); + sip_realm = _srs_config->get_stream_caster_gb28181_realm(c); + sip_serial = _srs_config->get_stream_caster_gb28181_serial(c); + sip_auto_play = _srs_config->get_stream_caster_gb28181_sip_auto_play(c); + sip_ack_timeout = _srs_config->get_stream_caster_gb28181_ack_timeout(c); + sip_keepalive_timeout = _srs_config->get_stream_caster_gb28181_keepalive_timeout(c); + print_sip_message = _srs_config->get_stream_caster_gb28181_print_sip_message(c); + sip_invite_port_fixed = _srs_config->get_stream_caster_gb28181_sip_invite_port_fixed(c); +} + +SrsGb28181Config::~SrsGb28181Config() +{ + +} + + +//SrsGb28181RtmpMuxer gb28181 rtmp muxer, process ps stream to rtmp +SrsGb28181RtmpMuxer::SrsGb28181RtmpMuxer(SrsGb28181Manger* c, std::string id, bool a, bool k) +{ + channel_id = id; + gb28181_manger = c; + channel = new SrsGb28181StreamChannel(); + + pprint = SrsPithyPrint::create_caster(); + trd = new SrsSTCoroutine("gb28181rtmpmuxer", this); + + sdk = NULL; + vjitter = new SrsRtspJitter(); + ajitter = new SrsRtspJitter(); + + avc = new SrsRawH264Stream(); + aac = new SrsRawAacStream(); + + ps_demixer = new SrsPsStreamDemixer(this, id, a, k); + wait_ps_queue = srs_cond_new(); + + h264_sps_changed = false; + h264_pps_changed = false; + h264_sps_pps_sent = false; + + stream_idle_timeout = -1; + recv_stream_time = 0; + + _rtmp_url = ""; +} + +SrsGb28181RtmpMuxer::~SrsGb28181RtmpMuxer() +{ + close(); + destroy(); + srs_cond_destroy(wait_ps_queue); + + srs_freep(channel); + srs_freep(ps_demixer); + srs_freep(trd); + srs_freep(sdk); + srs_freep(vjitter); + srs_freep(ajitter); + srs_freep(pprint); +} + +srs_error_t SrsGb28181RtmpMuxer::serve() +{ + srs_error_t err = srs_success; + + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "gb28181rtmpmuxer"); + } + + return err; +} + +std::string SrsGb28181RtmpMuxer::remote_ip() +{ + return ""; +} + +std::string SrsGb28181RtmpMuxer::get_channel_id() +{ + return channel_id; +} + +void SrsGb28181RtmpMuxer::copy_channel(SrsGb28181StreamChannel *s) +{ + channel->copy(s); +} + +SrsGb28181StreamChannel SrsGb28181RtmpMuxer::get_channel() +{ + return *channel; +} + +void SrsGb28181RtmpMuxer::set_channel_peer_ip(std::string ip) +{ + if (channel->get_rtp_peer_ip().empty()){ + channel->set_rtp_peer_ip(ip); + } +} + +void SrsGb28181RtmpMuxer::set_channel_peer_port(int port) +{ + if (channel->get_rtp_peer_port() == 0){ + channel->set_rtp_peer_port(port); + } +} + +int SrsGb28181RtmpMuxer::channel_peer_port() +{ + return channel->get_rtp_peer_port(); +} + +std::string SrsGb28181RtmpMuxer::channel_peer_ip() +{ + return channel->get_rtp_peer_ip(); +} + +void SrsGb28181RtmpMuxer::set_rtmp_url(std::string url) +{ + _rtmp_url = url; +} +std::string SrsGb28181RtmpMuxer::rtmp_url() +{ + return _rtmp_url; +} + + +void SrsGb28181RtmpMuxer::destroy() +{ + while(!ps_queue.empty()){ + SrsPsRtpPacket* pkt = ps_queue.front(); + ps_queue.pop(); + //must be free pkt + srs_freep(pkt); + } +} + +srs_error_t SrsGb28181RtmpMuxer::do_cycle() +{ + srs_error_t err = srs_success; + recv_stream_time = srs_get_system_time(); + + //consume ps stream, and check status + while (true) { + + pprint->elapse(); + + if ((err = trd->pull()) != srs_success) { + return srs_error_wrap(err, "gb28181 rtmp muxer cycle"); + } + + //demix ps to h264/aac, to rtmp + while(!ps_queue.empty()){ + SrsPsRtpPacket* pkt = ps_queue.front(); + if (pkt){ + if ((err = ps_demixer->on_ps_stream(pkt->payload->bytes(), + pkt->payload->length(), pkt->timestamp, pkt->ssrc)) != srs_success){ + srs_warn("gb28181: demix ps stream error:%s", srs_error_desc(err).c_str()); + srs_freep(err); + }; + } + ps_queue.pop(); + //must be free pkt + srs_freep(pkt); + } + + if (pprint->can_print()) { + srs_trace("gb28181: client id=%s, rtmp muxer is alive", channel_id.c_str()); + } + + srs_utime_t now = srs_get_system_time(); + srs_utime_t duration = now - recv_stream_time; + + SrsGb28181Config config = gb28181_manger->get_gb28181_config(); + if (duration > config.rtp_idle_timeout){ + srs_trace("gb28181: client id=%s, stream idle timeout, stop!!!", channel_id.c_str()); + break; + } + + srs_cond_timedwait(wait_ps_queue, 5 * SRS_UTIME_MILLISECONDS); + //srs_usleep(1000 * 5); + } + + return err; +} + + +void SrsGb28181RtmpMuxer::stop() +{ + if (trd){ + trd->interrupt(); + } + //stop rtmp publish + close(); +} + +void SrsGb28181RtmpMuxer::ps_packet_enqueue(SrsPsRtpPacket *pkt) +{ + srs_assert(pkt); + + //prevent consumers from being unable to process data + //and accumulating in the queue + uint32_t size = ps_queue.size(); + if (size > 100){ + srs_warn("gb28181: rtmpmuxer too much queue data, need to clear!!!"); + while(ps_queue.empty()) { + SrsPsRtpPacket* pkt = ps_queue.front(); + ps_queue.pop(); + srs_freep(pkt); + } + } + + ps_queue.push(pkt); + srs_cond_signal(wait_ps_queue); +} + +srs_error_t SrsGb28181RtmpMuxer::cycle() +{ + // serve the rtmp muxer. + srs_error_t err = do_cycle(); + + gb28181_manger->stop_rtp_listen(channel_id); + + gb28181_manger->remove(this); + srs_trace("gb28181: client id=%s rtmp muxer is remove", channel_id.c_str()); + + if (err == srs_success) { + srs_trace("client finished."); + } else if (srs_is_client_gracefully_close(err)) { + srs_warn("client disconnect peer. code=%d", srs_error_code(err)); + srs_freep(err); + } + + return err; +} + +srs_error_t SrsGb28181RtmpMuxer::on_rtp_video(SrsSimpleStream *stream, int64_t fpts) +{ + srs_error_t err = srs_success; + + // ensure rtmp connected. + if ((err = connect()) != srs_success) { + //after the connection fails, need to clear flag + //and send the av header again next time + h264_sps = ""; + h264_pps = ""; + aac_specific_config = ""; + return srs_error_wrap(err, "connect"); + } + + if ((err = vjitter->correct(fpts)) != srs_success) { + return srs_error_wrap(err, "jitter"); + } + + // ts tbn to flv tbn. + uint32_t dts = (uint32_t)(fpts / 90); + uint32_t pts = (uint32_t)(fpts / 90); + srs_info("gb28181rtmpmuxer: on_rtp_video dts=%u", dts); + + recv_stream_time = srs_get_system_time(); + + SrsBuffer *avs = new SrsBuffer(stream->bytes(), stream->length()); + SrsAutoFree(SrsBuffer, avs); + // send each frame. + while (!avs->empty()) { + char* frame = NULL; + int frame_size = 0; + if ((err = avc->annexb_demux(avs, &frame, &frame_size)) != srs_success) { + return srs_error_wrap(err, "demux annexb"); + } + + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f); + + // ignore the nalu type sei(6) aud(9) + if (nal_unit_type == SrsAvcNaluTypeAccessUnitDelimiter || + nal_unit_type == SrsAvcNaluTypeSEI) { + continue; + } + + // for sps + if (avc->is_sps(frame, frame_size)) { + std::string sps; + if ((err = avc->sps_demux(frame, frame_size, sps)) != srs_success) { + return srs_error_wrap(err, "demux sps"); + } + + if (h264_sps == sps) { + continue; + } + h264_sps_changed = true; + h264_sps = sps; + + if ((err = write_h264_sps_pps(dts, pts)) != srs_success) { + return srs_error_wrap(err, "write sps/pps"); + } + continue; + } + + // for pps + if (avc->is_pps(frame, frame_size)) { + std::string pps; + if ((err = avc->pps_demux(frame, frame_size, pps)) != srs_success) { + return srs_error_wrap(err, "demux pps"); + } + + if (h264_pps == pps) { + continue; + } + h264_pps_changed = true; + h264_pps = pps; + + if ((err = write_h264_sps_pps(dts, pts)) != srs_success) { + return srs_error_wrap(err, "write sps/pps"); + } + continue; + } + + // ibp frame. + srs_info("gb28181: demux avc ibp frame size=%d, dts=%d", frame_size, dts); + if ((err = write_h264_ipb_frame(frame, frame_size, dts, pts)) != srs_success) { + return srs_error_wrap(err, "write frame"); + } + } + + return err; +} + +srs_error_t SrsGb28181RtmpMuxer::on_rtp_audio(SrsSimpleStream* stream, int64_t fdts) +{ + srs_error_t err = srs_success; + + // ensure rtmp connected. + if ((err = connect()) != srs_success) { + //after the connection fails, need to clear flag + //and send the av header again next time + h264_sps = ""; + h264_pps = ""; + aac_specific_config = ""; + return srs_error_wrap(err, "connect"); + } + + if ((err = ajitter->correct(fdts)) != srs_success) { + return srs_error_wrap(err, "jitter"); + } + + recv_stream_time = srs_get_system_time(); + + uint32_t dts = (uint32_t)(fdts / 90); + + // send each frame. + SrsBuffer *avs = new SrsBuffer(stream->bytes(), stream->length()); + SrsAutoFree(SrsBuffer, avs); + if (!avs->empty()) { + char* frame = NULL; + int frame_size = 0; + SrsRawAacStreamCodec codec; + if ((err = aac->adts_demux(avs, &frame, &frame_size, codec)) != srs_success) { + return srs_error_wrap(err, "demux adts"); + } + + if (frame_size <= 0) { + return err; + } + + bool send_adts = false; + static int srs_aac_srates[] = { + 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, + 7350, 0, 0, 0 + }; + switch (srs_aac_srates[codec.sampling_frequency_index]) { + case 11025: + codec.sound_rate = SrsAudioSampleRate11025; + break; + case 22050: + codec.sound_rate = SrsAudioSampleRate22050; + break; + case 44100: + codec.sound_rate = SrsAudioSampleRate44100; + break; + default: + send_adts = true; //raw with adts + break; + }; + + std::string sh; + if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) { + return srs_error_wrap(err, "mux sequence header"); + } + + if (aac_specific_config != sh){ + std::string sh; + if ((err = aac->mux_sequence_header(&codec, sh)) != srs_success) { + return srs_error_wrap(err, "mux sequence header"); + } + aac_specific_config = sh; + codec.aac_packet_type = 0; + if ((err = write_audio_raw_frame((char*)sh.data(), (int)sh.length(), &codec, dts)) != srs_success) { + return srs_error_wrap(err, "write raw audio frame"); + } + } + + codec.aac_packet_type = 1; + if (send_adts) { // audio raw data. with adts header + if ((err = write_audio_raw_frame(stream->bytes(), stream->length(), &codec, dts)) != srs_success) { + return srs_error_wrap(err, "write audio raw frame"); + } + }else { // audio raw data. without adts header + if ((err = write_audio_raw_frame(frame, frame_size, &codec, dts)) != srs_success) { + return srs_error_wrap(err, "write audio raw frame"); + } + } + }//end if (!avs->empty()) + + return err; +} + +srs_error_t SrsGb28181RtmpMuxer::write_h264_sps_pps(uint32_t dts, uint32_t pts) +{ + srs_error_t err = srs_success; + + if (!h264_sps_changed || !h264_pps_changed) { + return err; + } + + // h264 raw to h264 packet. + std::string sh; + if ((err = avc->mux_sequence_header(h264_sps, h264_pps, dts, pts, sh)) != srs_success) { + return srs_error_wrap(err, "mux sequence header"); + } + + // h264 packet to flv packet. + int8_t frame_type = SrsVideoAvcFrameTypeKeyFrame; + int8_t avc_packet_type = SrsVideoAvcFrameTraitSequenceHeader; + char* flv = NULL; + int nb_flv = 0; + if ((err = avc->mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + return srs_error_wrap(err, "mux avc to flv"); + } + + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + if ((err = rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv)) != srs_success) { + return srs_error_wrap(err, "write packet"); + } + + // reset sps and pps. + h264_sps_changed = false; + h264_pps_changed = false; + h264_sps_pps_sent = true; + + return err; +} + +srs_error_t SrsGb28181RtmpMuxer::write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts) +{ + srs_error_t err = srs_success; + + // 5bits, 7.3.1 NAL unit syntax, + // ISO_IEC_14496-10-AVC-2003.pdf, page 44. + // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame + SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f); + + // for IDR frame, the frame is keyframe. + SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame; + if (nal_unit_type == SrsAvcNaluTypeIDR) { + frame_type = SrsVideoAvcFrameTypeKeyFrame; + } + + std::string ibp; + if ((err = avc->mux_ipb_frame(frame, frame_size, ibp)) != srs_success) { + return srs_error_wrap(err, "mux ibp frame"); + } + + int8_t avc_packet_type = SrsVideoAvcFrameTraitNALU; + char* flv = NULL; + int nb_flv = 0; + if ((err = avc->mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != srs_success) { + return srs_error_wrap(err, "mux avc to flv"); + } + + // the timestamp in rtmp message header is dts. + uint32_t timestamp = dts; + return rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv); +} + +srs_error_t SrsGb28181RtmpMuxer::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts) +{ + srs_error_t err = srs_success; + + char* data = NULL; + int size = 0; + if ((err = aac->mux_aac2flv(frame, frame_size, codec, dts, &data, &size)) != srs_success) { + return srs_error_wrap(err, "mux aac to flv"); + } + + return rtmp_write_packet(SrsFrameTypeAudio, dts, data, size); +} + +srs_error_t SrsGb28181RtmpMuxer::rtmp_write_packet(char type, uint32_t timestamp, char* data, int size) +{ + srs_error_t err = srs_success; + + if ((err = connect()) != srs_success) { + return srs_error_wrap(err, "connect"); + } + + SrsSharedPtrMessage* msg = NULL; + + if ((err = srs_rtmp_create_msg(type, timestamp, data, size, sdk->sid(), &msg)) != srs_success) { + return srs_error_wrap(err, "create message"); + } + srs_assert(msg); + + // send out encoded msg. + if ((err = sdk->send_and_free_message(msg)) != srs_success) { + close(); + return srs_error_wrap(err, "write message"); + } + + return err; +} + +srs_error_t SrsGb28181RtmpMuxer::connect() +{ + srs_error_t err = srs_success; + + // Ignore when connected. + if (sdk) { + return err; + } + + // generate rtmp url to connect to. + std::string url = _rtmp_url; + + // connect host. + srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT; + srs_utime_t sto = SRS_CONSTS_RTMP_PULSE; + sdk = new SrsSimpleRtmpClient(url, cto, sto); + + srs_trace("gb28181: rtmp connect url=%s", url.c_str()); + if ((err = sdk->connect()) != srs_success) { + close(); + return srs_error_wrap(err, "connect %s failed, cto=%dms, sto=%dms.", url.c_str(), srsu2msi(cto), srsu2msi(sto)); + } + + // publish. + if ((err = sdk->publish(SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE)) != srs_success) { + close(); + return srs_error_wrap(err, "publish %s failed", url.c_str()); + } + + return err; +} + +void SrsGb28181RtmpMuxer::close() +{ + srs_freep(sdk); +} + +void SrsGb28181RtmpMuxer::rtmp_close(){ + close(); +} + +SrsGb28181StreamChannel::SrsGb28181StreamChannel(){ + channel_id = ""; + port_mode = ""; + app = ""; + stream = ""; + ip = ""; + rtp_port = 0; + rtmp_port = 0; + ssrc = 0; + rtp_peer_port = 0; + rtp_peer_ip = ""; +} + +SrsGb28181StreamChannel::~SrsGb28181StreamChannel() +{ + +} + +void SrsGb28181StreamChannel::copy(const SrsGb28181StreamChannel *s){ + channel_id = s->get_channel_id(); + port_mode = s->get_port_mode(); + app = s->get_app(); + stream = s->get_stream(); + + ip = s->get_ip(); + rtp_port = s->get_rtp_port(); + rtmp_port = s->get_rtmp_port(); + ssrc = s->get_ssrc(); + + rtp_peer_ip = s->get_rtp_peer_ip(); + rtp_peer_port = s->get_rtp_peer_port(); +} + +void SrsGb28181StreamChannel::dumps(SrsJsonObject* obj) +{ + obj->set("id", SrsJsonAny::str(channel_id.c_str())); + obj->set("ip", SrsJsonAny::str(ip.c_str())); + obj->set("rtmp_port", SrsJsonAny::integer(rtmp_port)); + obj->set("app", SrsJsonAny::str(app.c_str())); + obj->set("stream", SrsJsonAny::str(stream.c_str())); + + obj->set("ssrc", SrsJsonAny::integer(ssrc)); + obj->set("rtp_port", SrsJsonAny::integer(rtp_port)); + obj->set("port_mode", SrsJsonAny::str(port_mode.c_str())); + obj->set("rtp_peer_port", SrsJsonAny::integer(rtp_peer_port)); + obj->set("rtp_peer_ip", SrsJsonAny::str(rtp_peer_ip.c_str())); +} + + +//Global Singleton instance, init in SrsServer +SrsGb28181Manger* _srs_gb28181 = NULL; + +//SrsGb28181Manger +SrsGb28181Manger::SrsGb28181Manger(SrsConfDirective* c) +{ + // TODO: FIXME: support reload. + config = new SrsGb28181Config(c); + manager = new SrsCoroutineManager(); +} + +SrsGb28181Manger::~SrsGb28181Manger() +{ + used_ports.clear(); + + srs_freep(manager); + srs_freep(config); + + destroy(); +} + +srs_error_t SrsGb28181Manger::initialize() +{ + srs_error_t err = srs_success; + if ((err = manager->start()) != srs_success) { + return srs_error_wrap(err, "start manager"); + } + + return err; +} + +SrsGb28181Config SrsGb28181Manger::get_gb28181_config() +{ + return *config; +} + +void SrsGb28181Manger::alloc_port(int* pport) +{ + // use a pair of port. + for (int i = config->rtp_port_min; i < config->rtp_port_max - 1; i += 2) { + if (!used_ports[i]) { + used_ports[i] = true; + used_ports[i + 1] = true; + *pport = i; + break; + } + } + srs_info("gb28181: alloc port=%d-%d", *pport, *pport + 1); +} + +void SrsGb28181Manger::free_port(int lpmin, int lpmax) +{ + for (int i = lpmin; i < lpmax; i++) { + used_ports[i] = false; + } + srs_trace("gb28181: free rtp port=%d-%d", lpmin, lpmax); +} + +uint32_t SrsGb28181Manger::hash_code(std::string str) +{ + uint32_t h = 0; + int len = str.length(); + + if (h == 0) { + int off = 0; + const char *val = str.c_str(); + + for (int i = 0; i < len; i++) { + h = 31 * h + val[off++]; + } + } + return h; +} + +uint32_t SrsGb28181Manger::generate_ssrc(std::string id) +{ + srand(uint(time(0))); + // TODO: SSRC rules can be customized, + //uint8_t index = uint8_t(rand() % (0x0F - 0x01 + 1) + 0x01); + //uint32_t ssrc = 0x00FFFFF0 & (hash_code(id) << 4) | index; + uint32_t ssrc = 0x00FFFFFF & (hash_code(id)); + srs_trace("gb28181: generate ssrc id=%s, ssrc=%u", id.c_str(), ssrc); + return ssrc; +} + +srs_error_t SrsGb28181Manger::fetch_or_create_rtmpmuxer(std::string id, SrsGb28181RtmpMuxer** gb28181) +{ + srs_error_t err = srs_success; + + SrsGb28181RtmpMuxer* muxer = NULL; + if ((muxer = fetch_rtmpmuxer(id)) != NULL) { + *gb28181 = muxer; + return err; + } + + muxer = new SrsGb28181RtmpMuxer(this, id, config->audio_enable, config->wait_keyframe); + if ((err = muxer->serve()) != srs_success) { + return srs_error_wrap(err, "gb28281: rtmp muxer serve %s", id.c_str()); + } + rtmpmuxers[id] = muxer; + *gb28181 = muxer; + + return err; +} + +SrsGb28181RtmpMuxer* SrsGb28181Manger::fetch_rtmpmuxer(std::string id) +{ + SrsGb28181RtmpMuxer* muxer = NULL; + + if (rtmpmuxers.find(id) == rtmpmuxers.end()) { + return NULL; + } + + muxer = rtmpmuxers[id]; + return muxer; +} + +SrsGb28181RtmpMuxer* SrsGb28181Manger::fetch_rtmpmuxer_by_ssrc(uint32_t ssrc) +{ + SrsGb28181RtmpMuxer* muxer = NULL; + if (rtmpmuxers_ssrc.find(ssrc) == rtmpmuxers_ssrc.end()) { + return NULL; + } + + muxer = rtmpmuxers_ssrc[ssrc]; + return muxer; +} + +void SrsGb28181Manger::rtmpmuxer_map_by_ssrc(SrsGb28181RtmpMuxer*muxer, uint32_t ssrc) +{ + if (rtmpmuxers_ssrc.find(ssrc) == rtmpmuxers_ssrc.end()) { + rtmpmuxers_ssrc[ssrc] = muxer; + } +} + +void SrsGb28181Manger::rtmpmuxer_unmap_by_ssrc(uint32_t ssrc) +{ + std::map::iterator it = rtmpmuxers_ssrc.find(ssrc); + if (it != rtmpmuxers_ssrc.end()) { + rtmpmuxers_ssrc.erase(it); + } +} + +void SrsGb28181Manger::destroy() +{ + //destory ps rtp listen + std::map::iterator it; + for (it = rtp_pool.begin(); it != rtp_pool.end(); ++it) { + SrsPsRtpListener* listener = it->second; + srs_freep(listener); + } + rtp_pool.clear(); + + //destory gb28181 muxer + std::map::iterator it2; + for (it2 = rtmpmuxers.begin(); it2 != rtmpmuxers.end(); ++it2) { + SrsGb28181RtmpMuxer* muxer = it2->second; + SrsGb28181StreamChannel sess = muxer->get_channel(); + rtmpmuxer_unmap_by_ssrc(sess.get_ssrc()); + manager->remove(muxer); + } + rtmpmuxers.clear(); +} + +void SrsGb28181Manger::remove(SrsGb28181RtmpMuxer* muxer) +{ + std::string id = muxer->get_channel_id(); + + map::iterator it = rtmpmuxers.find(id); + if (it != rtmpmuxers.end()) { + SrsGb28181RtmpMuxer* muxer = it->second; + SrsGb28181StreamChannel sess = muxer->get_channel(); + rtmpmuxer_unmap_by_ssrc(sess.get_ssrc()); + rtmpmuxers.erase(it); + } + manager->remove(muxer); +} + + +srs_error_t SrsGb28181Manger::start_ps_rtp_listen(std::string id, int port) +{ + srs_error_t err = srs_success; + if (port == config->rtp_mux_port) { + return srs_error_wrap(err, "start rtp listen port is mux port"); + } + + map::iterator key = rtmpmuxers.find(id); + if (key == rtmpmuxers.end()){ + return srs_error_wrap(err, "start rtp listen port rtmp muxer is null"); + } + + if (rtp_pool.find(port) == rtp_pool.end()) + { + SrsPsRtpListener* rtp = new SrsPsRtpListener(this->config, port, id); + rtp_pool[port] = rtp; + if ((err = rtp_pool[port]->listen()) != srs_success) { + stop_rtp_listen(id); + return srs_error_wrap(err, "rtp listen"); + } + + srs_trace("gb28181: start rtp ps stream over server-port=%d", port); + } + + return err; +} + +void SrsGb28181Manger::stop_rtp_listen(std::string id) +{ + srs_error_t err = srs_success; + + map::iterator it = rtmpmuxers.find(id); + if (it == rtmpmuxers.end()){ + return; + } + + SrsGb28181RtmpMuxer* muxer = it->second; + SrsGb28181StreamChannel sess = muxer->get_channel(); + + int port = sess.get_rtp_port(); + if (port == config->rtp_mux_port) { + return; + } + + map::iterator it2 = rtp_pool.find(port); + if (it2 != rtp_pool.end()){ + srs_freep(it2->second); + rtp_pool.erase(it2); + } + + free_port(port, port+1); +} + +//api +uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channel) +{ + srs_assert(channel); + + std::string id = channel->get_channel_id(); + SrsGb28181RtmpMuxer *muxer = NULL; + + muxer = fetch_rtmpmuxer(id); + if (muxer){ + SrsGb28181StreamChannel s = muxer->get_channel(); + channel->copy(&s); + //return ERROR_GB28181_SESSION_IS_EXIST; + return ERROR_SUCCESS; + } + + if (channel->get_stream().empty()){ + channel->set_stream("[stream]"); + } + + if (channel->get_app().empty()){ + channel->set_stream("[app]"); + } + + if (channel->get_port_mode().empty()){ + channel->set_port_mode(RTP_PORT_MODE_FIXED); + } + + //create on rtmp muxer, gb28281 stream to rtmp + srs_error_t err = srs_success; + if ((err = fetch_or_create_rtmpmuxer(id, &muxer)) != srs_success){ + srs_warn("gb28181: create rtmp muxer error, %s", srs_error_desc(err).c_str()); + srs_freep(err); + return ERROR_GB28281_CREATER_RTMPMUXER_FAILED; + } + + //Start RTP listening port, receive gb28181 stream, + //fixed is mux port, + //random is random allocation port + int rtp_port = 0; + std::string port_mode = channel->get_port_mode(); + + if (port_mode == RTP_PORT_MODE_RANDOM){ + alloc_port(&rtp_port); + if (rtp_port <= 0){ + return ERROR_GB28181_RTP_PORT_FULL; + } + srs_error_t err = srs_success; + if ((err = start_ps_rtp_listen(id, rtp_port)) != srs_success){ + srs_warn("gb28181: start ps rtp listen error, %s", srs_error_desc(err).c_str()); + srs_freep(err); + free_port(rtp_port, rtp_port + 1); + return ERROR_GB28281_CREATER_RTMPMUXER_FAILED; + } + } + else if(port_mode == RTP_PORT_MODE_FIXED) { + rtp_port = config->rtp_mux_port; + } + else{ + return ERROR_GB28181_PORT_MODE_INVALID; + } + + //Generate SSRC according to the hash code, + //of the string value of the id + uint32_t ssrc = generate_ssrc(id); + rtmpmuxer_map_by_ssrc(muxer, ssrc); + + //Generate RTMP push stream address, + std::string app = channel->get_app(); + std::string stream = channel->get_stream(); + app = srs_string_replace(app, "[app]", "live"); + stream = srs_string_replace(stream, "[stream]", id); + + std::string url = "rtmp://" + config->output + "/" + app + "/" + stream; + int rtmp_port; + if (true) { + std::string schema, host, vhost, param, _app, _stream; + srs_discovery_tc_url(url, schema, host, vhost, _app, _stream, rtmp_port, param); + url = srs_generate_rtmp_url(host, rtmp_port, "", "", app, stream, ""); + std::stringstream ss; + ss << ssrc; + url = srs_string_replace(url, "[ssrc]", ss.str()); + url = srs_path_build_timestamp(url); + } + muxer->set_rtmp_url(url); + srs_trace("gb28181: create new stream channel id:%s rtmp url=%s", id.c_str(), muxer->rtmp_url().c_str()); + + //generate the value returned to the api response + channel->set_app(app); + channel->set_stream(stream); + channel->set_rtp_port(rtp_port); + channel->set_rtmp_port(rtmp_port); + channel->set_ip(config->host); + channel->set_ssrc(ssrc); + + muxer->copy_channel(channel); + + return ERROR_SUCCESS; +} + +uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) +{ + //notify the device to stop streaming + //if an internal sip service controlled channel + notify_sip_bye(id); + + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + if (muxer){ + stop_rtp_listen(id); + muxer->stop(); + return ERROR_SUCCESS; + }else { + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } +} + + +uint32_t SrsGb28181Manger::queue_stream_channel(std::string id, SrsJsonArray* arr) +{ + if (!id.empty()){ + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + if (!muxer){ + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } + SrsJsonObject* obj = SrsJsonAny::object(); + arr->append(obj); + muxer->get_channel().dumps(obj); + }else { + std::map::iterator it2; + for (it2 = rtmpmuxers.begin(); it2 != rtmpmuxers.end(); ++it2) { + SrsGb28181RtmpMuxer* muxer = it2->second; + SrsJsonObject* obj = SrsJsonAny::object(); + arr->append(obj); + muxer->get_channel().dumps(obj); + } + } + + return ERROR_SUCCESS; +} + +uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + //if RTMP Muxer does not exist, you need to create + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + + if (!muxer){ + //if there is an invalid parameter, the channel will be created automatically + if (ip.empty() || port == 0 || ssrc == 0){ + //channel not exist + SrsGb28181StreamChannel channel; + channel.set_channel_id(id); + channel.set_app("live"); + channel.set_stream(id); + int code = create_stream_channel(&channel); + if (code != ERROR_SUCCESS){ + return code; + } + + ip = channel.get_ip(); + port = channel.get_rtp_port(); + ssrc = channel.get_ssrc(); + } + }else { + //channel exit, use channel config + SrsGb28181StreamChannel channel = muxer->get_channel(); + ip = channel.get_ip(); + port = channel.get_rtp_port(); + ssrc = channel.get_ssrc(); + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_invite(&req, ip, port, ssrc); + +} + +uint32_t SrsGb28181Manger::notify_sip_bye(std::string id) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + if (muxer){ + muxer->rtmp_close(); + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_bye(&req); +} + +uint32_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_sip_raw_data(&req, data); + +} + +uint32_t SrsGb28181Manger::notify_sip_unregister(std::string id) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + delete_stream_channel(id); + sip_service->remove_session(id); + return ERROR_SUCCESS; +} diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp new file mode 100644 index 000000000..812f390fb --- /dev/null +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -0,0 +1,438 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_GB28181_HPP +#define SRS_APP_GB28181_HPP + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTP_PORT_MODE_FIXED "fixed" +#define RTP_PORT_MODE_RANDOM "random" + +class SrsConfDirective; +class SrsRtpPacket; +class SrsRtmpClient; +class SrsRawH264Stream; +class SrsRawAacStream; +struct SrsRawAacStreamCodec; +class SrsSharedPtrMessage; +class SrsAudioFrame; +class SrsSimpleStream; +class SrsPithyPrint; +class SrsSimpleRtmpClient; +class SrsSipStack; +class SrsGb28181Manger; +class SrsRtspJitter; +class SrsSipRequest; +class SrsGb28181RtmpMuxer; +class SrsGb28181Config; +class SrsGb28181PsRtpProcessor; +class SrsGb28181SipService; +class SrsGb28181StreamChannel; + +//ps rtp header packet parse +class SrsPsRtpPacket: public SrsRtpPacket +{ +public: + SrsPsRtpPacket(); + virtual ~SrsPsRtpPacket(); +public: + virtual srs_error_t decode(SrsBuffer* stream); +}; + +//randomly assigned ports receive gb28281 device streams +class SrsPsRtpListener: public ISrsUdpHandler +{ +private: + SrsUdpListener* listener; + SrsGb28181PsRtpProcessor* rtp_processor; + int _port; +public: + SrsPsRtpListener(SrsGb28181Config* c, int p, std::string s); + virtual ~SrsPsRtpListener(); +public: + virtual int port(); + virtual srs_error_t listen(); +// Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +}; + +//multiplexing service, single port receiving all gb28281 device streams +class SrsGb28181RtpMuxService : public ISrsUdpHandler +{ +private: + SrsGb28181Config *config; + SrsGb28181PsRtpProcessor *rtp_processor; +public: + SrsGb28181RtpMuxService(SrsConfDirective* c); + virtual ~SrsGb28181RtpMuxService(); + + // Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +}; + + +//process gb28281 RTP package, generate a completed PS stream data, +//call the PS stream parser, parse the original video and audio +class SrsGb28181PsRtpProcessor: public ISrsUdpHandler +{ +private: + SrsPithyPrint* pprint; + SrsGb28181Config* config; + std::map cache_ps_rtp_packet; + std::map pre_packet; + std::string channel_id; + bool auto_create_channel; +public: + SrsGb28181PsRtpProcessor(SrsGb28181Config* c, std::string sid); + virtual ~SrsGb28181PsRtpProcessor(); +private: + bool can_send_ps_av_packet(); + void dispose(); +// Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +}; + +//ps stream processing parsing interface +class ISrsPsStreamHander +{ +public: + ISrsPsStreamHander(); + virtual ~ISrsPsStreamHander(); +public: + virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts)=0; + virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts)=0; +}; + +//analysis of PS stream and +//extraction of H264 raw data and audio data +//then process the flow through PS stream hander, +//such as RTMP multiplexer, and composited into RTMP av stream +class SrsPsStreamDemixer +{ +public: + // gb28181 program stream struct define + struct SrsPsPacketStartCode + { + uint8_t start_code[3]; + uint8_t stream_id[1]; + }; + + struct SrsPsPacketHeader + { + SrsPsPacketStartCode start;// 4 + uint8_t info[9]; + uint8_t stuffing_length; + }; + + struct SrsPsPacketBBHeader + { + SrsPsPacketStartCode start; + uint16_t length; + }; + + struct SrsPsePacket + { + SrsPsPacketStartCode start; + uint16_t length; + uint8_t info[2]; + uint8_t stuffing_length; + }; + + struct SrsPsMapPacket + { + SrsPsPacketStartCode start; + uint16_t length; + }; + +private: + SrsFileWriter ps_fw; + SrsFileWriter video_fw; + SrsFileWriter audio_fw; + + bool first_keyframe_flag; + bool wait_first_keyframe; + bool audio_enable; + std::string channel_id; + + ISrsPsStreamHander *hander; +public: + SrsPsStreamDemixer(ISrsPsStreamHander *h, std::string sid, bool a, bool k); + virtual ~SrsPsStreamDemixer(); +private: + bool can_send_ps_av_packet(); +public: + int64_t parse_ps_timestamp(const uint8_t* p); + virtual srs_error_t on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp, uint32_t ssrc); +}; + + +//RTMP multiplexer, which processes the raw H264 / AAC, +//then publish it to RTMP server +class SrsGb28181RtmpMuxer : public ISrsCoroutineHandler, + public ISrsConnection, public ISrsPsStreamHander +{ +private: + SrsPithyPrint* pprint; + SrsGb28181StreamChannel *channel; + int stream_idle_timeout; + srs_utime_t recv_stream_time; +private: + std::string channel_id; + std::string _rtmp_url; + std::string video_ssrc; + std::string audio_ssrc; + int audio_sample_rate; + int audio_channel; + + SrsGb28181Manger* gb28181_manger; + SrsCoroutine* trd; + SrsPsStreamDemixer* ps_demixer; + srs_cond_t wait_ps_queue; + + SrsSimpleRtmpClient* sdk; + SrsRtspJitter* vjitter; + SrsRtspJitter* ajitter; + + SrsRawH264Stream* avc; + std::string h264_sps; + std::string h264_pps; + bool h264_sps_changed; + bool h264_pps_changed; + bool h264_sps_pps_sent; + + SrsRawAacStream* aac; + std::string aac_specific_config; + +public: + std::queue ps_queue; + +public: + SrsGb28181RtmpMuxer(SrsGb28181Manger* m, std::string id, bool a, bool k); + virtual ~SrsGb28181RtmpMuxer(); + +public: + virtual srs_error_t serve(); + virtual void stop(); + + virtual std::string get_channel_id(); + virtual void ps_packet_enqueue(SrsPsRtpPacket *pkt); + virtual void copy_channel(SrsGb28181StreamChannel *s); + virtual void set_channel_peer_ip(std::string ip); + virtual void set_channel_peer_port(int port); + virtual int channel_peer_port(); + virtual std::string channel_peer_ip(); + virtual void set_rtmp_url(std::string url); + virtual std::string rtmp_url(); + virtual SrsGb28181StreamChannel get_channel(); + +private: + virtual srs_error_t do_cycle(); + virtual void destroy(); + +// Interface ISrsOneCycleThreadHandler +public: + virtual srs_error_t cycle(); + virtual std::string remote_ip(); +public: + virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts); + virtual srs_error_t on_rtp_audio(SrsSimpleStream* stream, int64_t dts); +private: + virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_t pts); + virtual srs_error_t write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts); + virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts); + virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size); +private: + // Connect to RTMP server. + virtual srs_error_t connect(); + // Close the connection to RTMP server. + virtual void close(); +public: + virtual void rtmp_close(); +}; + +//system parameter configuration of gb28281 module, +//read file from configuration file to generate +class SrsGb28181Config +{ +public: + std::string host; + srs_utime_t rtp_idle_timeout; + bool audio_enable; + bool wait_keyframe; + std::string output; + int rtp_port_min; + int rtp_port_max; + int rtp_mux_port; + + //sip config + int sip_port; + std::string sip_serial; + std::string sip_realm; + bool sip_enable; + int sip_ack_timeout; + int sip_keepalive_timeout; + bool print_sip_message; + bool sip_auto_play; + bool sip_invite_port_fixed; + +public: + SrsGb28181Config(SrsConfDirective* c); + virtual ~SrsGb28181Config(); +}; + +class SrsGb28181StreamChannel +{ +private: + std::string channel_id; + std::string port_mode; + std::string app; + std::string stream; + + std::string ip; + int rtp_port; + int rtmp_port; + uint32_t ssrc; + + //send rtp stream client local port + int rtp_peer_port; + //send rtp stream client local ip + std::string rtp_peer_ip; + +public: + SrsGb28181StreamChannel(); + virtual ~SrsGb28181StreamChannel(); + + std::string get_channel_id() const { return channel_id; } + std::string get_port_mode() const { return port_mode; } + std::string get_app() const { return app; } + std::string get_stream() const { return stream; } + std::string get_ip() const { return ip; } + int get_rtp_port() const { return rtp_port; } + int get_rtmp_port() const { return rtmp_port; } + uint32_t get_ssrc() const { return ssrc; } + uint32_t get_rtp_peer_port() const { return rtp_peer_port; } + std::string get_rtp_peer_ip() const { return rtp_peer_ip; } + + void set_channel_id(const std::string &i) { channel_id = i; } + void set_port_mode(const std::string &p) { port_mode = p; } + void set_app(const std::string &a) { app = a; } + void set_stream(const std::string &s) { stream = s; } + void set_ip(const std::string &i) { ip = i; } + void set_rtp_port( const int &p) { rtp_port = p; } + void set_rtmp_port( const int &p) { rtmp_port = p; } + void set_ssrc( const int &s) { ssrc = s;} + void set_rtp_peer_ip( const std::string &p) { rtp_peer_ip = p; } + void set_rtp_peer_port( const int &s) { rtp_peer_port = s;} + + void copy(const SrsGb28181StreamChannel *s); + void dumps(SrsJsonObject* obj); + +}; + +// Global singleton instance. +extern SrsGb28181Manger* _srs_gb28181; + +//gb28181 module management, management of all RTMP multiplexers, +//random assignment of RTP listeners, and external control interfaces +class SrsGb28181Manger +{ +private: + SrsGb28181Config *config; + // The key: port, value: whether used. + std::map used_ports; + std::map rtp_pool; + std::map rtmpmuxers_ssrc; + std::map rtmpmuxers; + SrsCoroutineManager* manager; + + SrsGb28181SipService* sip_service; + +public: + SrsGb28181Manger(SrsConfDirective* c); + virtual ~SrsGb28181Manger(); + +public: + srs_error_t fetch_or_create_rtmpmuxer(std::string id, SrsGb28181RtmpMuxer** gb28181); + SrsGb28181RtmpMuxer* fetch_rtmpmuxer(std::string id); + SrsGb28181RtmpMuxer* fetch_rtmpmuxer_by_ssrc(uint32_t ssrc); + void rtmpmuxer_map_by_ssrc(SrsGb28181RtmpMuxer*muxer, uint32_t ssrc); + void rtmpmuxer_unmap_by_ssrc(uint32_t ssrc); + uint32_t generate_ssrc(std::string id); + uint32_t hash_code(std::string str); + + void set_sip_service(SrsGb28181SipService *s) { sip_service = s; } + SrsGb28181SipService* get_sip_service() { return sip_service; } + +public: + //stream channel api + uint32_t create_stream_channel(SrsGb28181StreamChannel *channel); + uint32_t delete_stream_channel(std::string id); + uint32_t queue_stream_channel(std::string id, SrsJsonArray* arr); + //sip api + uint32_t notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc); + uint32_t notify_sip_bye(std::string id); + uint32_t notify_sip_raw_data(std::string id, std::string data); + uint32_t notify_sip_unregister(std::string id); + +private: + void destroy(); + +public: + // Alloc a rtp port from local ports pool. + // @param pport output the rtp port. + void alloc_port(int* pport); + // Free the alloced rtp port. + void free_port(int lpmin, int lpmax); + srs_error_t initialize(); + + SrsGb28181Config get_gb28181_config(); + srs_error_t start_ps_rtp_listen(std::string id, int port); + void stop_rtp_listen(std::string id); + +public: + void remove(SrsGb28181RtmpMuxer* conn); + +}; + +#endif + diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp new file mode 100644 index 000000000..72a55685c --- /dev/null +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -0,0 +1,471 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipRequest* r) +{ + caster = c; + req = new SrsSipRequest(); + req->copy(r); + + _register_status = SrsGb28181SipSessionUnkonw; + _alive_status = SrsGb28181SipSessionUnkonw; + _invite_status = SrsGb28181SipSessionUnkonw; + _register_time = 0; + _alive_time = 0; + _invite_time = 0; + _recv_rtp_time = 0; + _reg_expires = 0; + + _peer_ip = ""; + _peer_port = 0; + + _from = NULL; + _fromlen = 0; +} + +SrsGb28181SipSession::~SrsGb28181SipSession() +{ + srs_freep(req); +} + +//gb28181 sip Service +SrsGb28181SipService::SrsGb28181SipService(SrsConfDirective* c) +{ + // TODO: FIXME: support reload. + config = new SrsGb28181Config(c); + sip = new SrsSipStack(); + + if (_srs_gb28181){ + _srs_gb28181->set_sip_service(this); + } +} + +SrsGb28181SipService::~SrsGb28181SipService() +{ + destroy(); + srs_freep(sip); + srs_freep(config); +} + +void SrsGb28181SipService::set_stfd(srs_netfd_t fd) +{ + lfd = fd; +} + +srs_error_t SrsGb28181SipService::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +{ + char address_string[64]; + char port_string[16]; + if(getnameinfo(from, fromlen, + (char*)&address_string, sizeof(address_string), + (char*)&port_string, sizeof(port_string), + NI_NUMERICHOST|NI_NUMERICSERV)) { + return srs_error_new(ERROR_SYSTEM_IP_INVALID, "bad address"); + } + std::string peer_ip = std::string(address_string); + int peer_port = atoi(port_string); + + srs_error_t err = on_udp_sip(peer_ip, peer_port, buf, nb_buf, (sockaddr*)from, fromlen); + if (err != srs_success) { + return srs_error_wrap(err, "process udp"); + } + return err; +} + +srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, + char* buf, int nb_buf, sockaddr* from, const int fromlen) +{ + srs_error_t err = srs_success; + + if (config->print_sip_message) + { + srs_trace("gb28181: request peer_ip=%s, peer_port=%d nbbuf=%d", peer_ip.c_str(), peer_port, nb_buf); + srs_trace("gb28181: request recv message=%s", buf); + } + + if (nb_buf < 10) { + return err; + } + + SrsSipRequest* req = NULL; + + if ((err = sip->parse_request(&req, buf, nb_buf)) != srs_success) { + return srs_error_wrap(err, "parse sip request"); + } + + if (config->print_sip_message) + { + srs_trace("gb28181: %s method=%s, uri=%s, version=%s ", + req->get_cmdtype_str().c_str(), req->method.c_str(), req->uri.c_str(), req->version.c_str()); + srs_trace("gb28281: request client id=%s", req->sip_auth_id.c_str()); + } + + req->peer_ip = peer_ip; + req->peer_port = peer_port; + SrsAutoFree(SrsSipRequest, req); + + std::string session_id = req->sip_auth_id; + + if (req->is_register()) { + std::vector serial = srs_string_split(srs_string_replace(req->uri,"sip:", ""), "@"); + if (serial.at(0) != config->sip_serial){ + srs_trace("gb28181: client:%s request serial and server serial inconformity(%s:%s)", + req->sip_auth_id.c_str(), serial.at(0).c_str(), config->sip_serial.c_str()); + return err; + } + + srs_trace("gb28181: request peer_ip=%s, peer_port=%d", peer_ip.c_str(), peer_port, nb_buf); + srs_trace("gb28181: request %s method=%s, uri=%s, version=%s ", + req->get_cmdtype_str().c_str(), req->method.c_str(), req->uri.c_str(), req->version.c_str()); + srs_trace("gb28281: request client id=%s", req->sip_auth_id.c_str()); + + SrsGb28181SipSession* sip_session = create_sip_session(req); + if (!sip_session) { + srs_trace("gb28181: create sip session faild:%s", req->uri.c_str()); + return err; + } + + send_status(req, from, fromlen); + sip_session->set_register_status(SrsGb28181SipSessionRegisterOk); + sip_session->set_register_time(srs_get_system_time()); + sip_session->set_reg_expires(req->expires); + sip_session->set_sockaddr(from); + sip_session->set_sockaddr_len(fromlen); + sip_session->set_peer_ip(peer_ip); + sip_session->set_peer_port(peer_port); + }else if (req->is_message()) { + SrsGb28181SipSession* sip_session = fetch(session_id); + if (!sip_session || sip_session->register_status() == SrsGb28181SipSessionUnkonw){ + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); + return err; + } + + //reponse status + send_status(req, from, fromlen); + sip_session->set_register_status(SrsGb28181SipSessionRegisterOk); + sip_session->set_register_time(srs_get_system_time()); + sip_session->set_alive_status(SrsGb28181SipSessionAliveOk); + sip_session->set_alive_time(srs_get_system_time()); + sip_session->set_sockaddr(from); + sip_session->set_sockaddr_len(fromlen); + sip_session->set_peer_port(peer_port); + sip_session->set_peer_ip(peer_ip); + + //send invite, play client av + //start ps rtp listen, recv ps stream + if (config->sip_auto_play && sip_session->register_status() == SrsGb28181SipSessionRegisterOk && + sip_session->alive_status() == SrsGb28181SipSessionAliveOk && + sip_session->invite_status() == SrsGb28181SipSessionUnkonw) + { + //stop the possible stream and push a new stream + //send_bye(req, from, fromlen); + + SrsGb28181StreamChannel ch; + ch.set_channel_id(session_id); + ch.set_ip(config->host); + ch.set_stream(session_id); + ch.set_app("live"); + if (config->sip_invite_port_fixed){ + ch.set_port_mode(RTP_PORT_MODE_FIXED); + }else { + ch.set_port_mode(RTP_PORT_MODE_RANDOM); + } + + int code = _srs_gb28181->create_stream_channel(&ch); + if (code == ERROR_SUCCESS){ + code = send_invite(req, ch.get_ip(), + ch.get_rtp_port(), ch.get_ssrc()); + } + + if (code == ERROR_SUCCESS){ + sip_session->set_invite_status(SrsGb28181SipSessionTrying); + sip_session->set_invite_time(srs_get_system_time()); + } + + } + }else if (req->is_invite()) { + SrsGb28181SipSession* sip_session = fetch(session_id); + + srs_trace("gb28181: request peer_ip=%s, peer_port=%d", peer_ip.c_str(), peer_port, nb_buf); + srs_trace("gb28181: request %s method=%s, uri=%s, version=%s ", + req->get_cmdtype_str().c_str(), req->method.c_str(), req->uri.c_str(), req->version.c_str()); + srs_trace("gb28281: request client id=%s", req->sip_auth_id.c_str()); + + if (!sip_session){ + send_bye(req); + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); + return err; + } + + if (sip_session->register_status() == SrsGb28181SipSessionUnkonw || + sip_session->alive_status() == SrsGb28181SipSessionUnkonw) { + srs_trace("gb28181: %s client not registered or not alive", req->sip_auth_id.c_str()); + return err; + } + + if (req->cmdtype == SrsSipCmdRespone && req->status == "200") { + srs_trace("gb28181: INVITE response %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str()); + send_ack(req, from, fromlen); + sip_session->set_invite_status(SrsGb28181SipSessionInviteOk); + sip_session->set_invite_time(srs_get_system_time()); + //Record tag and branch, which are required by the 'bye' command, + sip_session->set_request(req); + }else{ + sip_session->set_invite_status(SrsGb28181SipSessionUnkonw); + sip_session->set_invite_time(0); + } + }else if (req->is_bye()) { + srs_trace("gb28181: request peer_ip=%s, peer_port=%d", peer_ip.c_str(), peer_port, nb_buf); + srs_trace("gb28181: request %s method=%s, uri=%s, version=%s ", + req->get_cmdtype_str().c_str(), req->method.c_str(), req->uri.c_str(), req->version.c_str()); + srs_trace("gb28281: request client id=%s", req->sip_auth_id.c_str()); + + SrsGb28181SipSession* sip_session = fetch(session_id); + send_status(req, from, fromlen); + + if (!sip_session){ + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); + return err; + } + + sip_session->set_invite_status(SrsGb28181SipSessionBye); + sip_session->set_invite_time(0); + + }else{ + srs_trace("gb28181: ingor request method=%s", req->method.c_str()); + } + + return err; +} + +int SrsGb28181SipService::send_message(sockaddr* from, int fromlen, std::stringstream& ss) +{ + std::string str = ss.str(); + if (config->print_sip_message) + srs_trace("gb28181: send_message:%s", str.c_str()); + srs_assert(!str.empty()); + + int ret = srs_sendto(lfd, (char*)str.c_str(), (int)str.length(), from, fromlen, SRS_UTIME_NO_TIMEOUT); + if (ret <= 0){ + srs_trace("gb28181: send_message falid (%d)", ret); + } + + return ret; +} + + +int SrsGb28181SipService::send_ack(SrsSipRequest *req, sockaddr *f, int l) +{ + srs_assert(req); + + std::stringstream ss; + + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->resp_ack(ss, req); + return send_message(f, l, ss); +} + +int SrsGb28181SipService::send_status(SrsSipRequest *req, sockaddr *f, int l) +{ + srs_assert(req); + + std::stringstream ss; + + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->resp_status(ss, req); + return send_message(f, l, ss); +} + + +int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, uint32_t ssrc) +{ + srs_assert(req); + + SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); + + if (!sip_session){ + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } + + //if you are inviting or succeed in invite, + //you cannot invite again. you need to 'bye' and try again + if (sip_session->invite_status() == SrsGb28181SipSessionTrying || + sip_session->invite_status() == SrsGb28181SipSessionInviteOk){ + return ERROR_GB28281_SIP_IS_INVITING; + } + + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + std::stringstream ss; + sip->req_invite(ss, req, ip, port, ssrc); + + if (send_message(sip_session->sockaddr_from(), sip_session->sockaddr_fromlen(), ss) <= 0) + { + return ERROR_GB28281_SIP_INVITE_FAILED; + } + + sip_session->set_invite_status(SrsGb28181SipSessionTrying); + + return ERROR_SUCCESS; + +} + +int SrsGb28181SipService::send_bye(SrsSipRequest *req) +{ + srs_assert(req); + + SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); + + if (!sip_session){ + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } + + //prame branch, from_tag, to_tag, call_id, + //The parameter of 'bye' must be the same as 'invite' + SrsSipRequest r = sip_session->request(); + req->copy(&r); + + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + //get protocol stack + std::stringstream ss; + sip->req_bye(ss, req); + + if (send_message(sip_session->sockaddr_from(), sip_session->sockaddr_fromlen(), ss) <= 0) + { + return ERROR_GB28281_SIP_BYE_FAILED; + } + + return ERROR_SUCCESS; + + +} + + +int SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::string data) +{ + srs_assert(req); + + SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); + + if (!sip_session){ + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } + + std::stringstream ss; + ss << data; + + if (send_message(sip_session->sockaddr_from(), sip_session->sockaddr_fromlen(), ss) <= 0) + { + return ERROR_GB28281_SIP_BYE_FAILED; + } + + return ERROR_SUCCESS; +} + +SrsGb28181SipSession* SrsGb28181SipService::create_sip_session(SrsSipRequest *req) +{ + SrsGb28181SipSession *sess = NULL; + + std::map::iterator it = sessions.find(req->sip_auth_id); + if (it == sessions.end()){ + sess = new SrsGb28181SipSession(this, req); + }else{ + return it->second; + } + + sessions[req->sip_auth_id] = sess; + return sess; +} + +SrsGb28181SipSession* SrsGb28181SipService::fetch(std::string sid) +{ + std::map::iterator it = sessions.find(sid); + if (it == sessions.end()){ + return NULL; + }else{ + return it->second; + } +} + +void SrsGb28181SipService::remove_session(std::string sid) +{ + std::map::iterator it = sessions.find(sid); + if (it != sessions.end()){ + srs_freep(it->second); + sessions.erase(it); + } +} + + +void SrsGb28181SipService::destroy() +{ + //destory all sip session + std::map::iterator it; + for (it = sessions.begin(); it != sessions.end(); ++it) { + srs_freep(it->second); + } + sessions.clear(); +} + + diff --git a/trunk/src/app/srs_app_gb28181_sip.hpp b/trunk/src/app/srs_app_gb28181_sip.hpp new file mode 100644 index 000000000..9206e66c9 --- /dev/null +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -0,0 +1,161 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_APP_GB28181_SIP_HPP +#define SRS_APP_GB28181_SIP_HPP + +#include + +#include +#include +#include + +#include +#include +#include + + +class SrsConfDirective; +class SrsSipRequest; +class SrsGb28181Config; +class SrsSipStack; +class SrsGb28181SipService; + +enum SrsGb28281SipSessionStatusType{ + SrsGb28181SipSessionUnkonw = 0, + SrsGb28181SipSessionRegisterOk = 1, + SrsGb28181SipSessionAliveOk = 2, + SrsGb28181SipSessionInviteOk = 3, + SrsGb28181SipSessionTrying = 4, + SrsGb28181SipSessionBye = 5, +}; + +class SrsGb28181SipSession +{ +private: + //SrsSipRequest *req; + SrsGb28181SipService *caster; + std::string session_id; +private: + SrsGb28281SipSessionStatusType _register_status; + SrsGb28281SipSessionStatusType _alive_status; + SrsGb28281SipSessionStatusType _invite_status; + srs_utime_t _register_time; + srs_utime_t _alive_time; + srs_utime_t _invite_time; + srs_utime_t _recv_rtp_time; + int _reg_expires; + + std::string _peer_ip; + int _peer_port; + + sockaddr *_from; + int _fromlen; + SrsSipRequest *req; + +public: + void set_register_status(SrsGb28281SipSessionStatusType s) { _register_status = s;} + void set_alive_status(SrsGb28281SipSessionStatusType s) { _alive_status = s;} + void set_invite_status(SrsGb28281SipSessionStatusType s) { _invite_status = s;} + void set_register_time(srs_utime_t t) { _register_time = t;} + void set_alive_time(srs_utime_t t) { _alive_time = t;} + void set_invite_time(srs_utime_t t) { _invite_time = t;} + void set_recv_rtp_time(srs_utime_t t) { _recv_rtp_time = t;} + void set_reg_expires(int e) { _reg_expires = e;} + void set_peer_ip(std::string i) { _peer_ip = i;} + void set_peer_port(int o) { _peer_port = o;} + void set_sockaddr(sockaddr *f) { _from = f;} + void set_sockaddr_len(int l) { _fromlen = l;} + void set_request(SrsSipRequest *r) { req->copy(r);} + + SrsGb28281SipSessionStatusType register_status() { return _register_status;} + SrsGb28281SipSessionStatusType alive_status() { return _alive_status;} + SrsGb28281SipSessionStatusType invite_status() { return _invite_status;} + srs_utime_t register_time() { return _register_time;} + srs_utime_t alive_time() { return _alive_time;} + srs_utime_t invite_time() { return _invite_time;} + srs_utime_t recv_rtp_time() { return _recv_rtp_time;} + int reg_expires() { return _reg_expires;} + std::string peer_ip() { return _peer_ip;} + int peer_port() { return _peer_port;} + sockaddr* sockaddr_from() { return _from;} + int sockaddr_fromlen() { return _fromlen;} + SrsSipRequest request() { return *req;} + +public: + SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipRequest* r); + virtual ~SrsGb28181SipSession(); + +}; + +class SrsGb28181SipService : public ISrsUdpHandler +{ +private: + SrsSipStack *sip; + SrsGb28181Config *config; + srs_netfd_t lfd; + + std::map sessions; +public: + SrsGb28181SipService(SrsConfDirective* c); + virtual ~SrsGb28181SipService(); + + // Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); + virtual void set_stfd(srs_netfd_t fd); +private: + void destroy(); + srs_error_t on_udp_sip(std::string host, int port, char* buf, int nb_buf, sockaddr* from, int fromlen); +public: + int send_message(sockaddr* f, int l, std::stringstream& ss); + + int send_ack(SrsSipRequest *req, sockaddr *f, int l); + int send_status(SrsSipRequest *req, sockaddr *f, int l); + + int send_invite(SrsSipRequest *req, std::string ip, int port, uint32_t ssrc); + int send_bye(SrsSipRequest *req); + + // The SIP command is transmitted through HTTP API, + // and the body content is transmitted to the device, + // mainly for testing and debugging, For example, here is HTTP body: + // BYE sip:34020000001320000003@3402000000 SIP/2.0 + // Via: SIP/2.0/UDP 39.100.155.146:15063;rport;branch=z9hG4bK34205410 + // From: ;tag=512355410 + // To: ;tag=680367414 + // Call-ID: 200003304 + // CSeq: 21 BYE + // Max-Forwards: 70 + // User-Agent: SRS/4.0.4(Leo) + // Content-Length: 0 + // + // + int send_sip_raw_data(SrsSipRequest *req, std::string data); + + SrsGb28181SipSession* create_sip_session(SrsSipRequest *req); + SrsGb28181SipSession* fetch(std::string id); + void remove_session(std::string id); +}; + +#endif + diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 52e25afb6..2b4e09afe 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1580,6 +1580,131 @@ srs_error_t SrsGoApiError::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage return srs_api_response_code(w, r, 100); } +SrsGoApiGb28181::SrsGoApiGb28181() +{ +} + +SrsGoApiGb28181::~SrsGoApiGb28181() +{ +} + +srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + SrsJsonObject* obj = SrsJsonAny::object(); + SrsAutoFree(SrsJsonObject, obj); + + obj->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); + SrsJsonObject* data = SrsJsonAny::object(); + obj->set("data", data); + + string id = r->query_get("id"); + string action = r->query_get("action"); + string vhost = r->query_get("vhost"); + string app = r->query_get("app"); + string stream = r->query_get("stream"); + //fixed, random + string port_mode = r->query_get("port_mode"); + + if (_srs_gb28181) { + if(action == "create_channel"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + SrsGb28181StreamChannel channel; + channel.set_channel_id(id); + channel.set_app(app); + channel.set_stream(stream); + channel.set_port_mode(port_mode); + + uint32_t code = _srs_gb28181->create_stream_channel(&channel); + if (code != ERROR_SUCCESS) { + return srs_api_response_code(w, r, code); + } + + data->set("query", SrsJsonAny::object() + ->set("id", SrsJsonAny::str(channel.get_channel_id().c_str())) + ->set("ip", SrsJsonAny::str(channel.get_ip().c_str())) + ->set("rtmp_port", SrsJsonAny::integer(channel.get_rtmp_port())) + ->set("app", SrsJsonAny::str(channel.get_app().c_str())) + ->set("stream", SrsJsonAny::str(channel.get_stream().c_str())) + ->set("rtp_port", SrsJsonAny::integer(channel.get_rtp_port())) + ->set("ssrc", SrsJsonAny::integer(channel.get_ssrc()))); + return srs_api_response(w, r, obj->dumps()); + + } + else if(action == "delete_channel"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + uint32_t code = _srs_gb28181->delete_stream_channel(id); + return srs_api_response_code(w, r, code); + } + else if(action == "query_channel") { + SrsJsonArray* arr = SrsJsonAny::array(); + data->set("channels", arr); + + uint32_t code = _srs_gb28181->queue_stream_channel(id, arr); + if (code != ERROR_SUCCESS) { + return srs_api_response_code(w, r, code); + } + + return srs_api_response(w, r, obj->dumps()); + } + else if(action == "sip_invite"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + string ssrc = r->query_get("ssrc"); + string rtp_port = r->query_get("rtp_port"); + string ip = r->query_get("ip"); + + int _port = strtoul(rtp_port.c_str(), NULL, 10); + uint32_t _ssrc = (uint32_t)(strtoul(ssrc.c_str(), NULL, 10)); + + + + int code = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc); + return srs_api_response_code(w, r, code); + } + else if(action == "sip_bye"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + int code = _srs_gb28181->notify_sip_bye(id); + return srs_api_response_code(w, r, code); + } + else if(action == "sip_raw_data"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + std::string body; + r->body_read_all(body); + int code = _srs_gb28181->notify_sip_raw_data(id, body); + return srs_api_response_code(w, r, code); + } + else if(action == "sip_unregister"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + int code = _srs_gb28181->notify_sip_unregister(id); + return srs_api_response_code(w, r, code); + } + else + { + return srs_api_response_code(w, r, ERROR_GB28181_ACTION_INVALID); + } + + }else { + return srs_api_response_code(w, r, ERROR_GB28181_SERVER_NOT_RUN); + } +} + SrsHttpApi::SrsHttpApi(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip) : SrsConnection(cm, fd, cip) { diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 5976c5de3..cf085f499 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -232,6 +232,16 @@ public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; + +class SrsGoApiGb28181 : public ISrsHttpHandler +{ +public: + SrsGoApiGb28181(); + virtual ~SrsGoApiGb28181(); +public: + virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +}; + class SrsHttpApi : virtual public SrsConnection, virtual public ISrsReloadHandler { private: diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index 2a6186e21..fa02a3c4e 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -73,6 +73,10 @@ srs_error_t ISrsUdpMuxHandler::on_stfd_change(srs_netfd_t /*fd*/) return srs_success; } +void ISrsUdpHandler::set_stfd(srs_netfd_t /*fd*/) +{ +} + ISrsTcpHandler::ISrsTcpHandler() { } @@ -118,6 +122,8 @@ srs_error_t SrsUdpListener::listen() if ((err = srs_udp_listen(ip, port, &lfd)) != srs_success) { return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); } + + handler->set_stfd(lfd); srs_freep(trd); trd = new SrsSTCoroutine("udp", this); @@ -431,3 +437,4 @@ srs_error_t SrsUdpMuxListener::cycle() return err; } + diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index 75aea4c52..dce082efd 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -47,6 +47,8 @@ public: // When fd changed, for instance, reload the listen port, // notify the handler and user can do something. virtual srs_error_t on_stfd_change(srs_netfd_t fd); + + virtual void set_stfd(srs_netfd_t fd); public: // When udp listener got a udp packet, notice server to process it. // @param type, the client type, used to create concrete connection, diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 85edcc885..efd4ac8e1 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -54,6 +54,8 @@ using namespace std; #include #include #include +#include +#include // system interval in srs_utime_t, // all resolution times should be times togother, @@ -111,6 +113,10 @@ std::string srs_listener_type2string(SrsListenerType type) return "RTSP"; case SrsListenerFlv: return "HTTP-FLV"; + case SrsListenerGb28181Sip: + return "GB28181-SIP over UDP"; + case SrsListenerGb28181RtpMux: + return "GB28181-Stream over RTP"; default: return "UNKONWN"; } @@ -303,7 +309,9 @@ srs_error_t SrsUdpStreamListener::listen(string i, int p) // the caller already ensure the type is ok, // we just assert here for unknown stream caster. - srs_assert(type == SrsListenerMpegTsOverUdp); + srs_assert(type == SrsListenerMpegTsOverUdp + || type == SrsListenerGb28181Sip + || type == SrsListenerGb28181RtpMux); ip = i; port = p; @@ -341,6 +349,26 @@ SrsUdpCasterListener::~SrsUdpCasterListener() srs_freep(caster); } + +SrsGb28181Listener::SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsUdpStreamListener(svr, t, NULL) +{ + // the caller already ensure the type is ok, + // we just assert here for unknown stream caster. + srs_assert(type == SrsListenerGb28181Sip + ||type == SrsListenerGb28181RtpMux); + + if (type == SrsListenerGb28181Sip) { + caster = new SrsGb28181SipService(c); + }else if(type == SrsListenerGb28181RtpMux){ + caster = new SrsGb28181RtpMuxService(c); + } +} + +SrsGb28181Listener::~SrsGb28181Listener() +{ + srs_freep(caster); +} + SrsSignalManager* SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer* s) @@ -663,6 +691,9 @@ void SrsServer::destroy() srs_freep(signal_manager); srs_freep(conn_manager); + + //free global gb28281 manager + srs_freep(_srs_gb28181); } void SrsServer::dispose() @@ -953,6 +984,10 @@ srs_error_t SrsServer::http_handle() if ((err = http_api_mux->handle("/api/v1/clusters", new SrsGoApiClusters())) != srs_success) { return srs_error_wrap(err, "handle raw"); } + + if ((err = http_api_mux->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) { + return srs_error_wrap(err, "handle raw"); + } // test the request info. if ((err = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { @@ -1307,6 +1342,30 @@ srs_error_t SrsServer::listen_http_stream() return err; } +srs_error_t SrsServer::listen_gb28281_sip(SrsConfDirective* stream_caster) +{ + srs_error_t err = srs_success; + + SrsListener* sip_listener = NULL; + sip_listener = new SrsGb28181Listener(this, SrsListenerGb28181Sip, stream_caster); + + int port = _srs_config->get_stream_caster_gb28181_sip_listen(stream_caster); + if (port <= 0) { + return srs_error_new(ERROR_STREAM_CASTER_PORT, "invalid sip port=%d", port); + } + + srs_assert(sip_listener != NULL); + + listeners.push_back(sip_listener); + + // TODO: support listen at <[ip:]port> + if ((err = sip_listener->listen(srs_any_address_for_listener(), port)) != srs_success) { + return srs_error_wrap(err, "listen at %d", port); + } + + return err; +} + srs_error_t SrsServer::listen_stream_caster() { srs_error_t err = srs_success; @@ -1331,18 +1390,34 @@ srs_error_t SrsServer::listen_stream_caster() listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster); } else if (srs_stream_caster_is_flv(caster)) { listener = new SrsHttpFlvListener(this, SrsListenerFlv, stream_caster); + } else if (srs_stream_caster_is_gb28181(caster)) { + //init global gb28281 manger + if (_srs_gb28181 == NULL){ + _srs_gb28181 = new SrsGb28181Manger(stream_caster); + if ((err = _srs_gb28181->initialize()) != srs_success){ + return err; + } + } + + //sip listener + if (_srs_config->get_stream_caster_gb28181_sip_enable(stream_caster)){ + if ((err = listen_gb28281_sip(stream_caster)) != srs_success){ + return err; + } + } + + //gb28281 stream listener + listener = new SrsGb28181Listener(this, SrsListenerGb28181RtpMux, stream_caster); } else { return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "invalid caster %s", caster.c_str()); } srs_assert(listener != NULL); listeners.push_back(listener); - int port = _srs_config->get_stream_caster_listen(stream_caster); if (port <= 0) { return srs_error_new(ERROR_STREAM_CASTER_PORT, "invalid port=%d", port); } - // TODO: support listen at <[ip:]port> if ((err = listener->listen(srs_any_address_for_listener(), port)) != srs_success) { return srs_error_wrap(err, "listen at %d", port); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index b702eb49e..dd014d210 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include class SrsServer; class SrsConnection; @@ -52,6 +54,8 @@ class SrsTcpListener; class SrsAppCasterFlv; class SrsRtspCaster; class SrsCoroutineManager; +class SrsGb28181Caster; + // The listener type for server to identify the connection, // that is, use different type to process the connection. @@ -69,6 +73,10 @@ enum SrsListenerType SrsListenerRtsp = 4, // TCP stream, FLV stream over HTTP. SrsListenerFlv = 5, + // UDP stream, gb28181 ps stream over rtp, + SrsListenerGb28181RtpMux = 6, + // UDP gb28181 sip server + SrsListenerGb28181Sip = 7, }; // A common tcp listener, for RTMP/HTTP server. @@ -156,6 +164,14 @@ public: virtual ~SrsUdpCasterListener(); }; +// A UDP gb28181 listener, for sip and rtp stream mux server. +class SrsGb28181Listener : public SrsUdpStreamListener +{ +public: + SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c); + virtual ~SrsGb28181Listener(); +}; + // Convert signal to io, // @see: st-1.9/docs/notes.html class SrsSignalManager : public ISrsCoroutineHandler @@ -303,6 +319,7 @@ private: virtual srs_error_t listen_http_api(); virtual srs_error_t listen_http_stream(); virtual srs_error_t listen_stream_caster(); + virtual srs_error_t listen_gb28281_sip(SrsConfDirective* c); // Close the listeners for specified type, // Remove the listen object from manager. virtual void close_listeners(SrsListenerType type); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 103c5e2ff..65b857021 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -348,6 +348,22 @@ #define ERROR_RTC_SOURCE_CHECK 5017 #define ERROR_RTC_SDP_EXCHANGE 5018 +/////////////////////////////////////////////////////// +// GB28181 API error. +/////////////////////////////////////////////////////// +#define ERROR_GB28181_SERVER_NOT_RUN 6000 +#define ERROR_GB28181_SESSION_IS_EXIST 6001 +#define ERROR_GB28181_SESSION_IS_NOTEXIST 6002 +#define ERROR_GB28181_RTP_PORT_FULL 6003 +#define ERROR_GB28181_PORT_MODE_INVALID 6004 +#define ERROR_GB28181_VALUE_EMPTY 6005 +#define ERROR_GB28181_ACTION_INVALID 6006 +#define ERROR_GB28181_SIP_NOT_RUN 6007 +#define ERROR_GB28281_SIP_INVITE_FAILED 6008 +#define ERROR_GB28281_SIP_BYE_FAILED 6009 +#define ERROR_GB28281_SIP_IS_INVITING 6010 +#define ERROR_GB28281_CREATER_RTMPMUXER_FAILED 6011 + /////////////////////////////////////////////////////// // HTTP API error. /////////////////////////////////////////////////////// diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp new file mode 100644 index 000000000..e24e743c2 --- /dev/null +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -0,0 +1,776 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#if !defined(SRS_EXPORT_LIBRTMP) + +#include +#include +#include +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned int srs_sip_random(int min,int max) +{ + srand(int(time(0))); + return rand() % (max - min + 1) + min; +} + +std::string srs_sip_get_form_to_uri(std::string msg) +{ + //;tag=536961166 + //sip:34020000002000000001@3402000000 + + size_t pos = msg.find("<"); + if (pos == string::npos) { + return msg; + } + + msg = msg.substr(pos+1); + + size_t pos2 = msg.find(">"); + if (pos2 == string::npos) { + return msg; + } + + msg = msg.substr(0, pos2); + return msg; +} + +std::string srs_sip_get_utc_date() +{ + // clock time + timeval tv; + if (gettimeofday(&tv, NULL) == -1) { + return ""; + } + + // to calendar time + struct tm* tm; + if ((tm = gmtime(&tv.tv_sec)) == NULL) { + return ""; + } + + //Date: 2020-03-21T14:20:57.638 + std::string utc_date = ""; + char buffer[25] = {0}; + snprintf(buffer, 25, + "%d-%02d-%02dT%02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000)); + utc_date = buffer; + return utc_date; +} + + +std::string srs_sip_get_param(std::string msg, std::string param) +{ + std::vector vec_params = srs_string_split(msg, ";"); + + size_t min_pos = string::npos; + for (vector::iterator it = vec_params.begin(); it != vec_params.end(); ++it) { + string value = *it; + + size_t pos = value.find(param); + if (pos == string::npos) { + continue; + } + + std::vector v_pram = srs_string_split(value, "="); + + if (v_pram.size() > 0) { + return v_pram.at(1); + } + } + return ""; +} + +SrsSipRequest::SrsSipRequest() +{ + seq = 0; + content_length = 0; + sdp = NULL; + transport = NULL; + + method = ""; + uri = "";; + version = "";; + seq = 0; + content_type = ""; + content_length = 0; + call_id = ""; + from = ""; + to = ""; + via = ""; + from_tag = ""; + to_tag = ""; + contact = ""; + user_agent = ""; + branch = ""; + status = ""; + expires = 3600; + max_forwards = 70; + www_authenticate = ""; + authorization = ""; + cmdtype = SrsSipCmdRequest; + + host = "127.0.0.1";; + host_port = 5060; + + serial = "";; + realm = "";; + + sip_auth_id = ""; + sip_auth_pwd = ""; + sip_username = ""; + peer_ip = ""; + peer_port = 0; +} + +SrsSipRequest::~SrsSipRequest() +{ + srs_freep(sdp); + srs_freep(transport); +} + +bool SrsSipRequest::is_register() +{ + return method == SRS_SIP_METHOD_REGISTER; +} + +bool SrsSipRequest::is_invite() +{ + return method == SRS_SIP_METHOD_INVITE; +} + +bool SrsSipRequest::is_ack() +{ + return method == SRS_SIP_METHOD_ACK; +} + +bool SrsSipRequest::is_message() +{ + return method == SRS_SIP_METHOD_MESSAGE; +} + +bool SrsSipRequest::is_bye() +{ + return method == SRS_SIP_METHOD_BYE; +} + +std::string SrsSipRequest::get_cmdtype_str() +{ + switch(cmdtype) { + case SrsSipCmdRequest: + return "request"; + case SrsSipCmdRespone: + return "respone"; + } + + return ""; +} + +void SrsSipRequest::copy(SrsSipRequest* src) +{ + if (!src){ + return; + } + + method = src->method; + uri = src->uri; + version = src->version; + seq = src->seq; + content_type = src->content_type; + content_length = src->content_length; + call_id = src->call_id; + from = src->from; + to = src->to; + via = src->via; + from_tag = src->from_tag; + to_tag = src->to_tag; + contact = src->contact; + user_agent = src->user_agent; + branch = src->branch; + status = src->status; + expires = src->expires; + max_forwards = src->max_forwards; + www_authenticate = src->www_authenticate; + authorization = src->authorization; + cmdtype = src->cmdtype; + + host = src->host; + host_port = src->host_port; + + serial = src->serial; + realm = src->realm; + + sip_auth_id = src->sip_auth_id; + sip_auth_pwd = src->sip_auth_pwd; + sip_username = src->sip_username; + peer_ip = src->peer_ip; + peer_port = src->peer_port; +} + +SrsSipStack::SrsSipStack() +{ + buf = new SrsSimpleStream(); +} + +SrsSipStack::~SrsSipStack() +{ + srs_freep(buf); +} + +srs_error_t SrsSipStack::parse_request(SrsSipRequest** preq, const char* recv_msg, int nb_len) +{ + srs_error_t err = srs_success; + + SrsSipRequest* req = new SrsSipRequest(); + if ((err = do_parse_request(req, recv_msg)) != srs_success) { + srs_freep(req); + return srs_error_wrap(err, "recv message"); + } + + *preq = req; + + return err; +} + +srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_msg) +{ + srs_error_t err = srs_success; + + std::vector header_body = srs_string_split(recv_msg, SRS_RTSP_CRLFCRLF); + std::string header = header_body.at(0); + std::string body = ""; + + if (header_body.size() > 1){ + body = header_body.at(1); + } + + srs_info("sip: header=%s\n", header.c_str()); + srs_info("sip: body=%s\n", body.c_str()); + + // parse one by one. + char* start = (char*)header.c_str(); + char* end = start + header.size(); + char* p = start; + char* newline_start = start; + std::string firstline = ""; + while (p < end) { + if (p[0] == '\r' && p[1] == '\n'){ + p +=2; + int linelen = (int)(p - newline_start); + std::string oneline(newline_start, linelen); + newline_start = p; + + if (firstline == ""){ + firstline = oneline; + srs_info("sip: first line=%s", firstline.c_str()); + }else{ + size_t pos = oneline.find(":"); + if (pos != string::npos){ + if (pos != 0) { + //ex: CSeq: 100 MESSAGE header is 'CSeq:',content is '100 MESSAGE' + std::string head = oneline.substr(0, pos+1); + std::string content = oneline.substr(pos+1, oneline.length()-pos-1); + content = srs_string_replace(content, "\r\n", ""); + content = srs_string_trim_start(content, " "); + char *phead = (char*)head.c_str(); + + if (!strcasecmp(phead, "call-id:")) { + std::vector vec_callid = srs_string_split(content, " "); + req->call_id = vec_callid.at(0); + } + else if (!strcasecmp(phead, "contact:")) { + req->contact = content; + } + else if (!strcasecmp(phead, "content-encoding:")) { + srs_trace("sip: message head %s content=%s", phead, content.c_str()); + } + else if (!strcasecmp(phead, "content-length:")) { + req->content_length = strtoul(content.c_str(), NULL, 10); + } + else if (!strcasecmp(phead, "content-type:")) { + req->content_type = content; + } + else if (!strcasecmp(phead, "cseq:")) { + std::vector vec_seq = srs_string_split(content, " "); + req->seq = strtoul(vec_seq.at(0).c_str(), NULL, 10); + req->method = vec_seq.at(1); + } + else if (!strcasecmp(phead, "from:")) { + content = srs_string_replace(content, "sip:", ""); + req->from = srs_sip_get_form_to_uri(content.c_str()); + if (srs_string_contains(content, "tag")) { + req->from_tag = srs_sip_get_param(content.c_str(), "tag"); + } + } + else if (!strcasecmp(phead, "to:")) { + content = srs_string_replace(content, "sip:", ""); + req->to = srs_sip_get_form_to_uri(content.c_str()); + if (srs_string_contains(content, "tag")) { + req->to_tag = srs_sip_get_param(content.c_str(), "tag"); + } + } + else if (!strcasecmp(phead, "via:")) { + std::vector vec_seq = srs_string_split(content, ";"); + req->via = content; + req->branch = srs_sip_get_param(content.c_str(), "branch"); + } + else if (!strcasecmp(phead, "expires:")){ + req->expires = strtoul(content.c_str(), NULL, 10); + } + else if (!strcasecmp(phead, "user-agent:")){ + req->user_agent = content; + } + else if (!strcasecmp(phead, "max-forwards:")){ + req->max_forwards = strtoul(content.c_str(), NULL, 10); + } + else if (!strcasecmp(phead, "www-authenticate:")){ + req->www_authenticate = content; + } + else if (!strcasecmp(phead, "authorization:")){ + req->authorization = content; + } + else { + //TODO: fixme + srs_trace("sip: unkonw message head %s content=%s", phead, content.c_str()); + } + } + } + } + }else{ + p++; + } + } + + std::vector method_uri_ver = srs_string_split(firstline, " "); + //respone first line text:SIP/2.0 200 OK + if (!strcasecmp(method_uri_ver.at(0).c_str(), "sip/2.0")) { + req->cmdtype = SrsSipCmdRespone; + //req->method= vec_seq.at(1); + req->status = method_uri_ver.at(1); + req->version = method_uri_ver.at(0); + req->uri = req->from; + + vector str = srs_string_split(req->to, "@"); + req->sip_auth_id = srs_string_replace(str.at(0), "sip:", ""); + + }else {//request first line text :MESSAGE sip:34020000002000000001@3402000000 SIP/2.0 + req->cmdtype = SrsSipCmdRequest; + req->method= method_uri_ver.at(0); + req->uri = method_uri_ver.at(1); + req->version = method_uri_ver.at(2); + + vector str = srs_string_split(req->from, "@"); + req->sip_auth_id = srs_string_replace(str.at(0), "sip:", ""); + } + + req->sip_username = req->sip_auth_id; + + srs_info("sip: method=%s uri=%s version=%s cmdtype=%s", + req->method.c_str(), req->uri.c_str(), req->version.c_str(), req->get_cmdtype_str().c_str()); + srs_info("via=%s", req->via.c_str()); + srs_info("via_branch=%s", req->branch.c_str()); + srs_info("cseq=%d", req->seq); + srs_info("contact=%s", req->contact.c_str()); + srs_info("from=%s", req->from.c_str()); + srs_info("to=%s", req->to.c_str()); + srs_info("callid=%s", req->call_id.c_str()); + srs_info("status=%s", req->status.c_str()); + srs_info("from_tag=%s", req->from_tag.c_str()); + srs_info("to_tag=%s", req->to_tag.c_str()); + srs_info("sip_auth_id=%s", req->sip_auth_id.c_str()); + + return err; +} + +void SrsSipStack::resp_keepalive(std::stringstream& ss, SrsSipRequest *req){ + ss << SRS_SIP_VERSION <<" 200 OK" << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP " << req->host << ":" << req->host_port << ";branch=" << req->branch << SRS_RTSP_CRLF + << "From: from.c_str() << ">;tag=" << req->from_tag << SRS_RTSP_CRLF + << "To: to.c_str() << ">\r\n" + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "CSeq: " << req->seq << " " << req->method << SRS_RTSP_CRLF + << "Contact: "<< req->contact << SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: "<< SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; +} + +void SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) +{ + if (req->method == "REGISTER"){ + /* + //request: sip-agent-----REGISTER------->sip-server + REGISTER sip:34020000002000000001@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 192.168.137.11:5060;rport;branch=z9hG4bK1371463273 + From: ;tag=2043466181 + To: + Call-ID: 1011047669 + CSeq: 1 REGISTER + Contact: + Max-Forwards: 70 + User-Agent: IP Camera + Expires: 3600 + Content-Length: 0 + + //response: sip-agent<-----200 OK--------sip-server + SIP/2.0 200 OK + Via: SIP/2.0/UDP 192.168.137.11:5060;rport;branch=z9hG4bK1371463273 + From: + To: + CSeq: 1 REGISTER + Call-ID: 1011047669 + Contact: + User-Agent: SRS/4.0.4(Leo) + Expires: 3600 + Content-Length: 0 + + */ + if (req->authorization.empty()){ + //TODO: fixme supoort 401 + //return req_401_unauthorized(ss, req); + } + + ss << SRS_SIP_VERSION <<" 200 OK" << SRS_RTSP_CRLF + << "Via: " << req->via << SRS_RTSP_CRLF + << "From: from << ">" << SRS_RTSP_CRLF + << "To: to << ">" << SRS_RTSP_CRLF + << "CSeq: "<< req->seq << " " << req->method << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "Contact: " << req->contact << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Expires: " << req->expires << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; + }else{ + /* + //request: sip-agnet-------MESSAGE------->sip-server + MESSAGE sip:34020000002000000001@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 192.168.137.11:5060;rport;branch=z9hG4bK1066375804 + From: ;tag=1925919231 + To: + Call-ID: 1185236415 + CSeq: 20 MESSAGE + Content-Type: Application/MANSCDP+xml + Max-Forwards: 70 + User-Agent: IP Camera + Content-Length: 175 + + + + Keepalive + 1 + 34020000001320000003 + OK + + + + //response: sip-agent------200 OK --------> sip-server + SIP/2.0 200 OK + Via: SIP/2.0/UDP 192.168.137.11:5060;rport;branch=z9hG4bK1066375804 + From: + To: + CSeq: 20 MESSAGE + Call-ID: 1185236415 + User-Agent: SRS/4.0.4(Leo) + Content-Length: 0 + + */ + + ss << SRS_SIP_VERSION <<" 200 OK" << SRS_RTSP_CRLF + << "Via: " << req->via << SRS_RTSP_CRLF + << "From: from << ">" << SRS_RTSP_CRLF + << "To: to << ">" << SRS_RTSP_CRLF + << "CSeq: "<< req->seq << " " << req->method << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; + } + +} + +void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, int port, uint32_t ssrc) +{ + /* + //request: sip-agent <-------INVITE------ sip-server + INVITE sip:34020000001320000003@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 39.100.155.146:15063;rport;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: + Call-ID: 200008805 + CSeq: 20 INVITE + Content-Type: Application/SDP + Contact: + Max-Forwards: 70 + User-Agent: SRS/4.0.4(Leo) + Subject: 34020000001320000003:630886,34020000002000000001:0 + Content-Length: 164 + + v=0 + o=34020000001320000003 0 0 IN IP4 39.100.155.146 + s=Play + c=IN IP4 39.100.155.146 + t=0 0 + m=video 9000 RTP/AVP 96 + a=recvonly + a=rtpmap:96 PS/90000 + y=630886 + //response: sip-agent --------100 Trying--------> sip-server + SIP/2.0 100 Trying + Via: SIP/2.0/UDP 39.100.155.146:15063;rport=15063;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: + Call-ID: 200008805 + CSeq: 20 INVITE + User-Agent: IP Camera + Content-Length: 0 + + //response: sip-agent -------200 OK--------> sip-server + SIP/2.0 200 OK + Via: SIP/2.0/UDP 39.100.155.146:15063;rport=15063;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: ;tag=1083111311 + Call-ID: 200008805 + CSeq: 20 INVITE + Contact: + Content-Type: application/sdp + User-Agent: IP Camera + Content-Length: 263 + + v=0 + o=34020000001320000003 1073 1073 IN IP4 192.168.137.11 + s=Play + c=IN IP4 192.168.137.11 + t=0 0 + m=video 15060 RTP/AVP 96 + a=setup:active + a=sendonly + a=rtpmap:96 PS/90000 + a=username:34020000001320000003 + a=password:12345678 + a=filesize:0 + y=0000630886 + f= + //request: sip-agent <------ ACK ------- sip-server + ACK sip:34020000001320000003@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 39.100.155.146:15063;rport;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: + Call-ID: 200008805 + CSeq: 20 ACK + Max-Forwards: 70 + User-Agent: SRS/4.0.4(Leo) + Content-Length: 0 + */ + + std::stringstream sdp; + sdp << "v=0" << SRS_RTSP_CRLF + << "o=" << req->sip_auth_id << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF + << "s=Play" << SRS_RTSP_CRLF + << "c=IN IP4 " << ip << SRS_RTSP_CRLF + << "t=0 0" << SRS_RTSP_CRLF + //TODO 97 98 99 current no support + //<< "m=video " << port <<" RTP/AVP 96 97 98 99" << SRS_RTSP_CRLF + << "m=video " << port <<" RTP/AVP 96" << SRS_RTSP_CRLF + << "a=recvonly" << SRS_RTSP_CRLF + << "a=rtpmap:96 PS/90000" << SRS_RTSP_CRLF + //TODO: current no support + //<< "a=rtpmap:97 MPEG4/90000" << SRS_RTSP_CRLF + //<< "a=rtpmap:98 H264/90000" << SRS_RTSP_CRLF + //<< "a=rtpmap:99 H265/90000" << SRS_RTSP_CRLF + //<< "a=streamMode:MAIN\r\n" + //<< "a=filesize:0\r\n" + << "y=" << ssrc << SRS_RTSP_CRLF; + + + int rand = srs_sip_random(1000, 9999); + std::stringstream from, to, uri, branch, from_tag; + //"INVITE sip:34020000001320000001@3402000000 SIP/2.0\r\n + uri << "sip:" << req->sip_auth_id << "@" << req->realm; + //From: ;tag=500485%d\r\n + from << req->serial << "@" << req->host << ":" << req->host_port; + to << req->sip_auth_id << "@" << req->realm; + + req->from = from.str(); + req->to = to.str(); + req->uri = uri.str(); + + branch << "z9hG4bK3420" << rand; + from_tag << "51235" << rand; + req->branch = branch.str(); + req->from_tag = from_tag.str(); + + ss << "INVITE " << req->uri << " " << SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF + << "From: from << ">;tag=" << req->from_tag << SRS_RTSP_CRLF + << "To: to << ">" << SRS_RTSP_CRLF + << "Call-ID: 20000" << rand <to << ">" << SRS_RTSP_CRLF + << "Max-Forwards: 70" << " \r\n" + << "User-Agent: " << SRS_SIP_USER_AGENT <sip_auth_id << ":" << ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF + << "Content-Length: " << sdp.str().length() << SRS_RTSP_CRLFCRLF + << sdp.str(); +} + + +void SrsSipStack::req_401_unauthorized(std::stringstream& ss, SrsSipRequest *req) +{ + /* sip-agent <-----401 Unauthorized ------ sip-server + SIP/2.0 401 Unauthorized + Via: SIP/2.0/UDP 192.168.137.92:5061;rport=61378;received=192.168.1.13;branch=z9hG4bK802519080 + From: ;tag=611442989 + To: ;tag=102092689 + CSeq: 1 REGISTER + Call-ID: 1650345118 + User-Agent: LiveGBS v200228 + Contact: + Content-Length: 0 + WWW-Authenticate: Digest realm="3402000000",qop="auth",nonce="f1da98bd160f3e2efe954c6eedf5f75a" + */ + + ss << SRS_SIP_VERSION <<" 401 Unauthorized" << SRS_RTSP_CRLF + //<< "Via: " << req->via << SRS_RTSP_CRLF + << "Via: " << req->via << ";rport=" << req->peer_port << ";received=" << req->peer_ip << ";branch=" << req->branch << SRS_RTSP_CRLF + << "From: from << ">" << SRS_RTSP_CRLF + << "To: to << ">" << SRS_RTSP_CRLF + << "CSeq: "<< req->seq << " " << req->method << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "Contact: " << req->contact << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLF + << "WWW-Authenticate: Digest realm=\"3402000000\",qop=\"auth\",nonce=\"f1da98bd160f3e2efe954c6eedf5f75a\"" << SRS_RTSP_CRLFCRLF; + return; +} + +void SrsSipStack::resp_ack(std::stringstream& ss, SrsSipRequest *req){ + /* + //request: sip-agent <------ ACK ------- sip-server + ACK sip:34020000001320000003@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 39.100.155.146:15063;rport;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: + Call-ID: 200008805 + CSeq: 20 ACK + Max-Forwards: 70 + User-Agent: SRS/4.0.4(Leo) + Content-Length: 0 + */ + + ss << "ACK " << "sip:" << req->sip_auth_id << "@" << req->realm << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP " << req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF + << "From: serial << "@" << req->host + ":" << req->host_port << ">;tag=" << req->from_tag << SRS_RTSP_CRLF + << "To: sip_auth_id << "@" << req->realm << ">\r\n" + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "CSeq: " << req->seq << " ACK"<< SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: "<< SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; +} + +void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) +{ + /* + //request: sip-agent <------BYE------ sip-server + BYE sip:34020000001320000003@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 39.100.155.146:15063;rport;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: ;tag=1083111311 + Call-ID: 200008805 + CSeq: 79 BYE + Max-Forwards: 70 + User-Agent: SRS/4.0.4(Leo) + Content-Length: 0 + + //response: sip-agent ------200 OK ------> sip-server + SIP/2.0 200 OK + Via: SIP/2.0/UDP 39.100.155.146:15063;rport=15063;branch=z9hG4bK34208805 + From: ;tag=512358805 + To: ;tag=1083111311 + Call-ID: 200008805 + CSeq: 79 BYE + User-Agent: IP Camera + Content-Length: 0 + + */ + + std::stringstream from, to, uri; + uri << "sip:" << req->sip_auth_id << "@" << req->realm; + from << req->serial << "@" << req->realm; + to << req->sip_auth_id << "@" << req->realm; + + req->from = from.str(); + req->to = to.str(); + req->uri = uri.str(); + + string to_tag, from_tag, branch; + + if (req->branch.empty()){ + branch = ""; + }else { + branch = ";branch=" + req->branch; + } + + if (req->from_tag.empty()){ + from_tag = ""; + }else { + from_tag = ";tag=" + req->from_tag; + } + + if (req->to_tag.empty()){ + to_tag = ""; + }else { + to_tag = ";tag=" + req->to_tag; + } + + int seq = srs_sip_random(22, 99); + ss << "BYE " << req->uri << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: "<< SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport" << branch << SRS_RTSP_CRLF + << "From: from << ">" << from_tag << SRS_RTSP_CRLF + << "To: to << ">" << to_tag << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "CSeq: "<< seq <<" BYE" << SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; + +} + +#endif + diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp new file mode 100644 index 000000000..006ecbba3 --- /dev/null +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -0,0 +1,147 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Lixin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SRS_PROTOCOL_SIP_HPP +#define SRS_PROTOCOL_SIP_HPP + +#include + +#if !defined(SRS_EXPORT_LIBRTMP) + +#include +#include + +#include +#include + +class SrsBuffer; +class SrsSimpleStream; +class SrsAudioFrame; + +// SIP methods +#define SRS_SIP_METHOD_REGISTER "REGISTER" +#define SRS_SIP_METHOD_MESSAGE "MESSAGE" +#define SRS_SIP_METHOD_INVITE "INVITE" +#define SRS_SIP_METHOD_ACK "ACK" +#define SRS_SIP_METHOD_BYE "BYE" + +// SIP-Version +#define SRS_SIP_VERSION "SIP/2.0" +#define SRS_SIP_USER_AGENT RTMP_SIG_SRS_SERVER + + +enum SrsSipCmdType{ + SrsSipCmdRequest=0, + SrsSipCmdRespone=1 +}; + +class SrsSipRequest +{ +public: + //sip header member + std::string method; + std::string uri; + std::string version; + std::string status; + + std::string via; + std::string from; + std::string to; + std::string from_tag; + std::string to_tag; + std::string branch; + + std::string call_id; + long seq; + + std::string contact; + std::string user_agent; + + std::string content_type; + long content_length; + + long expires; + int max_forwards; + + std::string www_authenticate; + std::string authorization; + +public: + std::string serial; + std::string realm; + std::string sip_auth_id; + std::string sip_auth_pwd; + std::string sip_username; + std::string peer_ip; + int peer_port; + std::string host; + int host_port; + SrsSipCmdType cmdtype; + +public: + SrsRtspSdp* sdp; + SrsRtspTransport* transport; +public: + SrsSipRequest(); + virtual ~SrsSipRequest(); +public: + virtual bool is_register(); + virtual bool is_invite(); + virtual bool is_message(); + virtual bool is_ack(); + virtual bool is_bye(); + + virtual void copy(SrsSipRequest* src); +public: + virtual std::string get_cmdtype_str(); +}; + +// The gb28181 sip protocol stack. +class SrsSipStack +{ +private: + // The cached bytes buffer. + SrsSimpleStream* buf; +public: + SrsSipStack(); + virtual ~SrsSipStack(); +public: + virtual srs_error_t parse_request(SrsSipRequest** preq, const char *recv_msg, int nb_buf); +protected: + virtual srs_error_t do_parse_request(SrsSipRequest* req, const char *recv_msg); + +public: + virtual void resp_status(std::stringstream& ss, SrsSipRequest *req); + virtual void resp_keepalive(std::stringstream& ss, SrsSipRequest *req); + virtual void resp_ack(std::stringstream& ss, SrsSipRequest *req); + + virtual void req_invite(std::stringstream& ss, SrsSipRequest *req, std::string ip, int port, uint32_t ssrc); + virtual void req_bye(std::stringstream& ss, SrsSipRequest *req); + virtual void req_401_unauthorized(std::stringstream& ss, SrsSipRequest *req); + +}; + +#endif + +#endif +